ant_quic/candidate_discovery/
linux.rs

1//! Linux-specific network interface discovery using netlink sockets
2//!
3//! This module provides production-ready network interface enumeration and monitoring
4//! for Linux platforms using netlink sockets for real-time network change detection.
5
6use std::{
7    collections::HashMap,
8    net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
9    time::Instant,
10};
11
12use nix::libc;
13use tracing::{debug, error, info, warn};
14
15use crate::candidate_discovery::{NetworkInterface, NetworkInterfaceDiscovery};
16
17/// Linux-specific network interface discovery using netlink
18pub struct LinuxInterfaceDiscovery {
19    /// Cached interface data to detect changes
20    cached_interfaces: HashMap<u32, LinuxInterface>,
21    /// Last scan timestamp for cache validation
22    last_scan_time: Option<Instant>,
23    /// Cache TTL for interface data
24    cache_ttl: std::time::Duration,
25    /// Current scan state
26    scan_state: ScanState,
27    /// Netlink socket for interface monitoring
28    netlink_socket: Option<NetlinkSocket>,
29    /// Interface enumeration configuration
30    interface_config: InterfaceConfig,
31}
32
33/// Internal representation of a Linux network interface
34#[derive(Debug, Clone)]
35struct LinuxInterface {
36    /// Interface index
37    index: u32,
38    /// Interface name
39    name: String,
40    /// Interface type
41    interface_type: InterfaceType,
42    /// Interface flags
43    flags: InterfaceFlags,
44    /// MTU size
45    mtu: u32,
46    /// IPv4 addresses with prefix length
47    ipv4_addresses: Vec<(Ipv4Addr, u8)>,
48    /// IPv6 addresses with prefix length
49    ipv6_addresses: Vec<(Ipv6Addr, u8)>,
50    /// Hardware address (MAC)
51    hardware_address: Option<[u8; 6]>,
52    /// Interface state
53    state: InterfaceState,
54    /// Last update timestamp
55    last_updated: Instant,
56}
57
58/// Linux interface types derived from netlink messages
59#[derive(Debug, Clone, Copy, PartialEq, Eq)]
60enum InterfaceType {
61    /// Ethernet interface
62    Ethernet,
63    /// Wireless interface
64    Wireless,
65    /// Loopback interface
66    Loopback,
67    /// Tunnel interface
68    Tunnel,
69    /// Point-to-point interface
70    PointToPoint,
71    /// Bridge interface
72    Bridge,
73    /// VLAN interface
74    Vlan,
75    /// Bond interface
76    Bond,
77    /// Virtual interface
78    Virtual,
79    /// Unknown interface type
80    Unknown(u16),
81}
82
83/// Interface flags from netlink
84#[derive(Debug, Clone, Copy, Default)]
85struct InterfaceFlags {
86    /// Interface is up
87    is_up: bool,
88    /// Interface is running
89    is_running: bool,
90    /// Interface is loopback
91    is_loopback: bool,
92    /// Interface is point-to-point
93    is_point_to_point: bool,
94    /// Interface supports multicast
95    supports_multicast: bool,
96    /// Interface supports broadcast
97    supports_broadcast: bool,
98    /// Interface is wireless
99    is_wireless: bool,
100}
101
102/// Interface operational state
103#[derive(Debug, Clone, Copy, PartialEq, Eq)]
104enum InterfaceState {
105    /// Unknown state
106    Unknown,
107    /// Interface is not present
108    NotPresent,
109    /// Interface is down
110    Down,
111    /// Interface is in lower layer down
112    LowerLayerDown,
113    /// Interface is testing
114    Testing,
115    /// Interface is dormant
116    Dormant,
117    /// Interface is up
118    Up,
119}
120
121/// Current state of the scanning process
122#[derive(Debug, Clone, PartialEq)]
123enum ScanState {
124    /// No scan in progress
125    Idle,
126    /// Scan initiated, waiting for completion
127    InProgress { started_at: Instant },
128    /// Scan completed, results available
129    Completed { scan_results: Vec<NetworkInterface> },
130    /// Scan failed with error
131    Failed { error: String },
132}
133
134/// Netlink socket for interface monitoring
135struct NetlinkSocket {
136    /// Socket file descriptor
137    socket_fd: i32,
138    /// Sequence number for netlink messages
139    sequence_number: u32,
140    /// Process ID for netlink messages
141    process_id: u32,
142    /// Buffer for receiving netlink messages
143    receive_buffer: Vec<u8>,
144    /// Last message timestamp
145    last_message_time: Option<Instant>,
146}
147
148/// Configuration for interface enumeration
149#[derive(Debug, Clone)]
150struct InterfaceConfig {
151    /// Include loopback interfaces
152    include_loopback: bool,
153    /// Include down interfaces
154    include_down: bool,
155    /// Include IPv6 addresses
156    include_ipv6: bool,
157    /// Minimum MTU size to consider
158    min_mtu: u32,
159    /// Maximum interfaces to enumerate
160    max_interfaces: u32,
161    /// Enable real-time monitoring
162    enable_monitoring: bool,
163    /// Filter by interface types
164    allowed_interface_types: Vec<InterfaceType>,
165}
166
167/// Linux netlink error types
168#[derive(Debug, Clone)]
169enum LinuxNetworkError {
170    /// Netlink socket creation failed
171    SocketCreationFailed { error: String },
172    /// Failed to bind netlink socket
173    SocketBindFailed { error: String },
174    /// Failed to send netlink message
175    MessageSendFailed { error: String },
176    /// Failed to receive netlink message
177    MessageReceiveFailed { error: String },
178    /// Invalid netlink message format
179    InvalidMessage { message: String },
180    /// Interface not found
181    InterfaceNotFound { interface_name: String },
182    /// Permission denied for netlink operations
183    PermissionDenied { operation: String },
184    /// System limit exceeded
185    SystemLimitExceeded { limit_type: String },
186    /// Network namespace error
187    NetworkNamespaceError { error: String },
188    /// Interface enumeration timeout
189    EnumerationTimeout { timeout: std::time::Duration },
190}
191
192/// Netlink message types
193#[derive(Debug, Clone, Copy, PartialEq, Eq)]
194enum NetlinkMessageType {
195    /// Get link information
196    GetLink,
197    /// Get address information
198    GetAddress,
199    /// Link state change
200    LinkStateChange,
201    /// Address change
202    AddressChange,
203    /// Route change
204    RouteChange,
205}
206
207/// Netlink message parsing result
208#[derive(Debug, Clone)]
209struct NetlinkMessage {
210    /// Message type
211    message_type: NetlinkMessageType,
212    /// Message flags
213    flags: u16,
214    /// Message sequence number
215    sequence: u32,
216    /// Message payload
217    payload: Vec<u8>,
218}
219
220impl LinuxInterfaceDiscovery {
221    /// Create a new Linux interface discovery instance
222    pub fn new() -> Self {
223        Self {
224            cached_interfaces: HashMap::new(),
225            last_scan_time: None,
226            cache_ttl: std::time::Duration::from_secs(30),
227            scan_state: ScanState::Idle,
228            netlink_socket: None,
229            interface_config: InterfaceConfig {
230                include_loopback: false,
231                include_down: false,
232                include_ipv6: true,
233                min_mtu: 1280, // IPv6 minimum MTU
234                max_interfaces: 64,
235                enable_monitoring: true,
236                allowed_interface_types: vec![
237                    InterfaceType::Ethernet,
238                    InterfaceType::Wireless,
239                    InterfaceType::Tunnel,
240                    InterfaceType::Bridge,
241                ],
242            },
243        }
244    }
245
246    /// Set interface configuration
247    pub fn set_interface_config(&mut self, config: InterfaceConfig) {
248        self.interface_config = config;
249    }
250
251    /// Initialize netlink socket for interface monitoring
252    pub fn initialize_netlink_socket(&mut self) -> Result<(), LinuxNetworkError> {
253        if self.netlink_socket.is_some() {
254            return Ok(());
255        }
256
257        // Create netlink socket
258        let socket_fd = unsafe {
259            libc::socket(
260                libc::AF_NETLINK,
261                libc::SOCK_RAW | libc::SOCK_CLOEXEC,
262                libc::NETLINK_ROUTE,
263            )
264        };
265
266        if socket_fd < 0 {
267            return Err(LinuxNetworkError::SocketCreationFailed {
268                error: format!(
269                    "Failed to create netlink socket: {}",
270                    std::io::Error::last_os_error()
271                ),
272            });
273        }
274
275        // Set up socket address
276        let mut addr: libc::sockaddr_nl = unsafe { std::mem::zeroed() };
277        addr.nl_family = libc::AF_NETLINK as u16;
278        addr.nl_pid = 0; // Kernel will assign PID
279        addr.nl_groups = (1 << (libc::RTNLGRP_LINK - 1))
280            | (1 << (libc::RTNLGRP_IPV4_IFADDR - 1))
281            | (1 << (libc::RTNLGRP_IPV6_IFADDR - 1));
282
283        // Bind socket
284        let bind_result = unsafe {
285            libc::bind(
286                socket_fd,
287                &addr as *const libc::sockaddr_nl as *const libc::sockaddr,
288                std::mem::size_of::<libc::sockaddr_nl>() as libc::socklen_t,
289            )
290        };
291
292        if bind_result < 0 {
293            unsafe {
294                libc::close(socket_fd);
295            }
296            return Err(LinuxNetworkError::SocketBindFailed {
297                error: format!(
298                    "Failed to bind netlink socket: {}",
299                    std::io::Error::last_os_error()
300                ),
301            });
302        }
303
304        // Get assigned PID
305        let mut addr_len = std::mem::size_of::<libc::sockaddr_nl>() as libc::socklen_t;
306        let getsockname_result = unsafe {
307            libc::getsockname(
308                socket_fd,
309                &mut addr as *mut libc::sockaddr_nl as *mut libc::sockaddr,
310                &mut addr_len,
311            )
312        };
313
314        if getsockname_result < 0 {
315            unsafe {
316                libc::close(socket_fd);
317            }
318            return Err(LinuxNetworkError::SocketBindFailed {
319                error: format!(
320                    "Failed to get socket name: {}",
321                    std::io::Error::last_os_error()
322                ),
323            });
324        }
325
326        // Set socket to non-blocking mode
327        let flags = unsafe { libc::fcntl(socket_fd, libc::F_GETFL) };
328        if flags >= 0 {
329            unsafe {
330                libc::fcntl(socket_fd, libc::F_SETFL, flags | libc::O_NONBLOCK);
331            }
332        }
333
334        self.netlink_socket = Some(NetlinkSocket {
335            socket_fd,
336            sequence_number: 1,
337            process_id: addr.nl_pid,
338            receive_buffer: vec![0; 8192],
339            last_message_time: None,
340        });
341
342        debug!("Netlink socket initialized with PID {}", addr.nl_pid);
343        Ok(())
344    }
345
346    /// Check for netlink messages indicating network changes
347    pub fn check_network_changes(&mut self) -> Result<bool, LinuxNetworkError> {
348        let socket = match self.netlink_socket.as_mut() {
349            Some(socket) => socket,
350            None => return Ok(false),
351        };
352
353        let mut changes_detected = false;
354
355        // Read available messages
356        loop {
357            let bytes_read = unsafe {
358                libc::recv(
359                    socket.socket_fd,
360                    socket.receive_buffer.as_mut_ptr() as *mut libc::c_void,
361                    socket.receive_buffer.len(),
362                    0,
363                )
364            };
365
366            if bytes_read < 0 {
367                let error = std::io::Error::last_os_error();
368                match error.kind() {
369                    std::io::ErrorKind::WouldBlock => break, // No more messages
370                    _ => {
371                        return Err(LinuxNetworkError::MessageReceiveFailed {
372                            error: format!("Failed to receive netlink message: {}", error),
373                        });
374                    }
375                }
376            }
377
378            if bytes_read == 0 {
379                break; // No more data
380            }
381
382            // Parse netlink messages
383            let messages =
384                Self::parse_netlink_messages(&socket.receive_buffer[..bytes_read as usize])?;
385
386            for message in messages {
387                match message.message_type {
388                    NetlinkMessageType::LinkStateChange | NetlinkMessageType::AddressChange => {
389                        changes_detected = true;
390                        debug!("Network change detected: {:?}", message.message_type);
391                    }
392                    _ => {}
393                }
394            }
395
396            socket.last_message_time = Some(Instant::now());
397        }
398
399        Ok(changes_detected)
400    }
401
402    /// Parse netlink messages from buffer
403    fn parse_netlink_messages(buffer: &[u8]) -> Result<Vec<NetlinkMessage>, LinuxNetworkError> {
404        let mut messages = Vec::new();
405        let mut offset = 0;
406
407        while offset + 16 <= buffer.len() {
408            // Parse netlink header
409            let length = u32::from_ne_bytes([
410                buffer[offset],
411                buffer[offset + 1],
412                buffer[offset + 2],
413                buffer[offset + 3],
414            ]) as usize;
415
416            if length < 16 || offset + length > buffer.len() {
417                break; // Invalid or incomplete message
418            }
419
420            let msg_type = u16::from_ne_bytes([buffer[offset + 4], buffer[offset + 5]]);
421
422            let flags = u16::from_ne_bytes([buffer[offset + 6], buffer[offset + 7]]);
423
424            let sequence = u32::from_ne_bytes([
425                buffer[offset + 8],
426                buffer[offset + 9],
427                buffer[offset + 10],
428                buffer[offset + 11],
429            ]);
430
431            let message_type = match msg_type {
432                libc::RTM_NEWLINK | libc::RTM_DELLINK => NetlinkMessageType::LinkStateChange,
433                libc::RTM_NEWADDR | libc::RTM_DELADDR => NetlinkMessageType::AddressChange,
434                libc::RTM_NEWROUTE | libc::RTM_DELROUTE => NetlinkMessageType::RouteChange,
435                _ => {
436                    offset += length;
437                    continue;
438                }
439            };
440
441            let payload = if length > 16 {
442                buffer[offset + 16..offset + length].to_vec()
443            } else {
444                Vec::new()
445            };
446
447            messages.push(NetlinkMessage {
448                message_type,
449                flags,
450                sequence,
451                payload,
452            });
453
454            offset += length;
455        }
456
457        Ok(messages)
458    }
459
460    /// Enumerate network interfaces using netlink
461    fn enumerate_interfaces(&mut self) -> Result<Vec<LinuxInterface>, LinuxNetworkError> {
462        let mut interfaces = Vec::new();
463
464        // Read /proc/net/dev for basic interface information
465        let proc_net_dev = match std::fs::read_to_string("/proc/net/dev") {
466            Ok(content) => content,
467            Err(e) => {
468                return Err(LinuxNetworkError::InterfaceNotFound {
469                    interface_name: format!("Failed to read /proc/net/dev: {}", e),
470                });
471            }
472        };
473
474        // Parse /proc/net/dev
475        for line in proc_net_dev.lines().skip(2) {
476            let parts: Vec<&str> = line.split_whitespace().collect();
477            if parts.len() < 2 {
478                continue;
479            }
480
481            let interface_name = parts[0].trim_end_matches(':');
482            if interface_name.is_empty() {
483                continue;
484            }
485
486            match self.get_interface_details(interface_name) {
487                Ok(interface) => {
488                    if self.should_include_interface(&interface) {
489                        interfaces.push(interface);
490                    }
491                }
492                Err(e) => {
493                    warn!(
494                        "Failed to get interface details for {}: {:?}",
495                        interface_name, e
496                    );
497                }
498            }
499
500            if interfaces.len() >= self.interface_config.max_interfaces as usize {
501                break;
502            }
503        }
504
505        debug!("Enumerated {} network interfaces", interfaces.len());
506        Ok(interfaces)
507    }
508
509    /// Get detailed information about a specific interface
510    fn get_interface_details(
511        &self,
512        interface_name: &str,
513    ) -> Result<LinuxInterface, LinuxNetworkError> {
514        // Get interface index
515        let interface_index = self.get_interface_index(interface_name)?;
516
517        // Get interface flags and state
518        let (flags, state, mtu) = self.get_interface_flags_and_state(interface_name)?;
519
520        // Determine interface type
521        let interface_type = self.determine_interface_type(interface_name, &flags)?;
522
523        // Get hardware address
524        let hardware_address = self.get_hardware_address(interface_name).ok();
525
526        // Get IP addresses
527        let ipv4_addresses = self.get_ipv4_addresses(interface_name)?;
528        let ipv6_addresses = if self.interface_config.include_ipv6 {
529            self.get_ipv6_addresses(interface_name)?
530        } else {
531            Vec::new()
532        };
533
534        Ok(LinuxInterface {
535            index: interface_index,
536            name: interface_name.to_string(),
537            interface_type,
538            flags,
539            mtu,
540            ipv4_addresses,
541            ipv6_addresses,
542            hardware_address,
543            state,
544            last_updated: Instant::now(),
545        })
546    }
547
548    /// Get interface index from name
549    fn get_interface_index(&self, interface_name: &str) -> Result<u32, LinuxNetworkError> {
550        let c_name = std::ffi::CString::new(interface_name).map_err(|_| {
551            LinuxNetworkError::InterfaceNotFound {
552                interface_name: format!("Invalid interface name: {}", interface_name),
553            }
554        })?;
555
556        let index = unsafe { libc::if_nametoindex(c_name.as_ptr()) };
557        if index == 0 {
558            return Err(LinuxNetworkError::InterfaceNotFound {
559                interface_name: interface_name.to_string(),
560            });
561        }
562
563        Ok(index)
564    }
565
566    /// Get interface flags and state
567    fn get_interface_flags_and_state(
568        &self,
569        interface_name: &str,
570    ) -> Result<(InterfaceFlags, InterfaceState, u32), LinuxNetworkError> {
571        let socket_fd = unsafe { libc::socket(libc::AF_INET, libc::SOCK_DGRAM, 0) };
572        if socket_fd < 0 {
573            return Err(LinuxNetworkError::SocketCreationFailed {
574                error: "Failed to create socket for interface query".to_string(),
575            });
576        }
577
578        let mut ifreq: libc::ifreq = unsafe { std::mem::zeroed() };
579        let name_bytes = interface_name.as_bytes();
580        let copy_len = std::cmp::min(name_bytes.len(), libc::IFNAMSIZ - 1);
581
582        unsafe {
583            std::ptr::copy_nonoverlapping(
584                name_bytes.as_ptr(),
585                ifreq.ifr_name.as_mut_ptr() as *mut u8,
586                copy_len,
587            );
588        }
589
590        // Get interface flags
591        let flags_result = unsafe {
592            libc::ioctl(
593                socket_fd,
594                libc::SIOCGIFFLAGS.try_into().unwrap(),
595                &mut ifreq,
596            )
597        };
598        if flags_result < 0 {
599            unsafe {
600                libc::close(socket_fd);
601            }
602            return Err(LinuxNetworkError::InterfaceNotFound {
603                interface_name: format!("Failed to get flags for interface {}", interface_name),
604            });
605        }
606
607        let raw_flags = unsafe { ifreq.ifr_ifru.ifru_flags };
608        let flags = InterfaceFlags {
609            is_up: (raw_flags & libc::IFF_UP as i16) != 0,
610            is_running: (raw_flags & libc::IFF_RUNNING as i16) != 0,
611            is_loopback: (raw_flags & libc::IFF_LOOPBACK as i16) != 0,
612            is_point_to_point: (raw_flags & libc::IFF_POINTOPOINT as i16) != 0,
613            supports_multicast: (raw_flags & libc::IFF_MULTICAST as i16) != 0,
614            supports_broadcast: (raw_flags & libc::IFF_BROADCAST as i16) != 0,
615            is_wireless: self.is_wireless_interface(interface_name),
616        };
617
618        // Get MTU
619        let mtu_result =
620            unsafe { libc::ioctl(socket_fd, libc::SIOCGIFMTU.try_into().unwrap(), &mut ifreq) };
621        let mtu = if mtu_result >= 0 {
622            unsafe { ifreq.ifr_ifru.ifru_mtu as u32 }
623        } else {
624            1500 // Default MTU
625        };
626
627        unsafe {
628            libc::close(socket_fd);
629        }
630
631        // Determine interface state
632        let state = if flags.is_up && flags.is_running {
633            InterfaceState::Up
634        } else if flags.is_up {
635            InterfaceState::Down
636        } else {
637            InterfaceState::Down
638        };
639
640        Ok((flags, state, mtu))
641    }
642
643    /// Determine interface type from name and characteristics
644    fn determine_interface_type(
645        &self,
646        interface_name: &str,
647        flags: &InterfaceFlags,
648    ) -> Result<InterfaceType, LinuxNetworkError> {
649        if flags.is_loopback {
650            return Ok(InterfaceType::Loopback);
651        }
652
653        if flags.is_point_to_point {
654            return Ok(InterfaceType::PointToPoint);
655        }
656
657        if flags.is_wireless {
658            return Ok(InterfaceType::Wireless);
659        }
660
661        // Check interface name patterns
662        if interface_name.starts_with("eth") || interface_name.starts_with("en") {
663            return Ok(InterfaceType::Ethernet);
664        }
665
666        if interface_name.starts_with("wlan") || interface_name.starts_with("wl") {
667            return Ok(InterfaceType::Wireless);
668        }
669
670        if interface_name.starts_with("tun") || interface_name.starts_with("tap") {
671            return Ok(InterfaceType::Tunnel);
672        }
673
674        if interface_name.starts_with("br") {
675            return Ok(InterfaceType::Bridge);
676        }
677
678        if interface_name.contains('.') {
679            return Ok(InterfaceType::Vlan);
680        }
681
682        if interface_name.starts_with("bond") {
683            return Ok(InterfaceType::Bond);
684        }
685
686        if interface_name.starts_with("veth") || interface_name.starts_with("docker") {
687            return Ok(InterfaceType::Virtual);
688        }
689
690        Ok(InterfaceType::Unknown(0))
691    }
692
693    /// Check if interface is wireless
694    fn is_wireless_interface(&self, interface_name: &str) -> bool {
695        // Check for wireless interface indicators
696        if interface_name.starts_with("wlan") || interface_name.starts_with("wl") {
697            return true;
698        }
699
700        // Check if wireless extensions are available
701        let wireless_path = format!("/sys/class/net/{}/wireless", interface_name);
702        std::path::Path::new(&wireless_path).exists()
703    }
704
705    /// Get hardware address for interface
706    fn get_hardware_address(&self, interface_name: &str) -> Result<[u8; 6], LinuxNetworkError> {
707        let socket_fd = unsafe { libc::socket(libc::AF_INET, libc::SOCK_DGRAM, 0) };
708        if socket_fd < 0 {
709            return Err(LinuxNetworkError::SocketCreationFailed {
710                error: "Failed to create socket for hardware address query".to_string(),
711            });
712        }
713
714        let mut ifreq: libc::ifreq = unsafe { std::mem::zeroed() };
715        let name_bytes = interface_name.as_bytes();
716        let copy_len = std::cmp::min(name_bytes.len(), libc::IFNAMSIZ - 1);
717
718        unsafe {
719            std::ptr::copy_nonoverlapping(
720                name_bytes.as_ptr(),
721                ifreq.ifr_name.as_mut_ptr() as *mut u8,
722                copy_len,
723            );
724        }
725
726        let result = unsafe {
727            libc::ioctl(
728                socket_fd,
729                libc::SIOCGIFHWADDR.try_into().unwrap(),
730                &mut ifreq,
731            )
732        };
733        unsafe {
734            libc::close(socket_fd);
735        }
736
737        if result < 0 {
738            return Err(LinuxNetworkError::InterfaceNotFound {
739                interface_name: format!("Failed to get hardware address for {}", interface_name),
740            });
741        }
742
743        let mut hw_addr = [0u8; 6];
744        unsafe {
745            std::ptr::copy_nonoverlapping(
746                ifreq.ifr_ifru.ifru_hwaddr.sa_data.as_ptr() as *const u8,
747                hw_addr.as_mut_ptr(),
748                6,
749            );
750        }
751
752        Ok(hw_addr)
753    }
754
755    /// Get IPv4 addresses for interface
756    fn get_ipv4_addresses(
757        &self,
758        interface_name: &str,
759    ) -> Result<Vec<(Ipv4Addr, u8)>, LinuxNetworkError> {
760        let mut addresses = Vec::new();
761
762        // Read /proc/net/fib_trie for IPv4 addresses
763        // This is a simplified implementation - production code would use netlink
764        let socket_fd = unsafe { libc::socket(libc::AF_INET, libc::SOCK_DGRAM, 0) };
765        if socket_fd < 0 {
766            return Ok(addresses);
767        }
768
769        let mut ifreq: libc::ifreq = unsafe { std::mem::zeroed() };
770        let name_bytes = interface_name.as_bytes();
771        let copy_len = std::cmp::min(name_bytes.len(), libc::IFNAMSIZ - 1);
772
773        unsafe {
774            std::ptr::copy_nonoverlapping(
775                name_bytes.as_ptr(),
776                ifreq.ifr_name.as_mut_ptr() as *mut u8,
777                copy_len,
778            );
779        }
780
781        let result =
782            unsafe { libc::ioctl(socket_fd, libc::SIOCGIFADDR.try_into().unwrap(), &mut ifreq) };
783        if result >= 0 {
784            let sockaddr_in = unsafe {
785                &*(&ifreq.ifr_ifru.ifru_addr as *const libc::sockaddr as *const libc::sockaddr_in)
786            };
787
788            if sockaddr_in.sin_family == libc::AF_INET as u16 {
789                let ip_bytes = sockaddr_in.sin_addr.s_addr.to_ne_bytes();
790                let ipv4_addr = Ipv4Addr::from(ip_bytes);
791
792                // Get netmask
793                let netmask_result = unsafe {
794                    libc::ioctl(
795                        socket_fd,
796                        libc::SIOCGIFNETMASK.try_into().unwrap(),
797                        &mut ifreq,
798                    )
799                };
800                let prefix_len = if netmask_result >= 0 {
801                    let netmask_sockaddr_in = unsafe {
802                        &*(&ifreq.ifr_ifru.ifru_netmask as *const libc::sockaddr
803                            as *const libc::sockaddr_in)
804                    };
805                    let netmask_bytes = netmask_sockaddr_in.sin_addr.s_addr.to_ne_bytes();
806                    let netmask = u32::from_ne_bytes(netmask_bytes);
807                    netmask.count_ones() as u8
808                } else {
809                    24 // Default /24
810                };
811
812                addresses.push((ipv4_addr, prefix_len));
813            }
814        }
815
816        unsafe {
817            libc::close(socket_fd);
818        }
819        Ok(addresses)
820    }
821
822    /// Get IPv6 addresses for interface
823    fn get_ipv6_addresses(
824        &self,
825        interface_name: &str,
826    ) -> Result<Vec<(Ipv6Addr, u8)>, LinuxNetworkError> {
827        let mut addresses = Vec::new();
828
829        // Read /proc/net/if_inet6 for IPv6 addresses
830        let if_inet6_content = match std::fs::read_to_string("/proc/net/if_inet6") {
831            Ok(content) => content,
832            Err(_) => return Ok(addresses), // IPv6 not available
833        };
834
835        for line in if_inet6_content.lines() {
836            let parts: Vec<&str> = line.split_whitespace().collect();
837            if parts.len() >= 6 {
838                let addr_str = parts[0];
839                let prefix_len_str = parts[1];
840                let if_name = parts[5];
841
842                if if_name == interface_name {
843                    if let Ok(prefix_len) = u8::from_str_radix(prefix_len_str, 16) {
844                        // Parse IPv6 address from hex string
845                        if addr_str.len() == 32 {
846                            // Convert hex string to bytes
847                            let mut ipv6_bytes = [0u8; 16];
848                            let mut valid = true;
849                            for i in 0..16 {
850                                if let Ok(byte) =
851                                    u8::from_str_radix(&addr_str[i * 2..i * 2 + 2], 16)
852                                {
853                                    ipv6_bytes[i] = byte;
854                                } else {
855                                    valid = false;
856                                    break;
857                                }
858                            }
859                            if valid {
860                                let ipv6_addr = Ipv6Addr::from(ipv6_bytes);
861                                addresses.push((ipv6_addr, prefix_len));
862                            }
863                        }
864                    }
865                }
866            }
867        }
868
869        Ok(addresses)
870    }
871
872    /// Check if an interface should be included based on configuration
873    fn should_include_interface(&self, interface: &LinuxInterface) -> bool {
874        // Check loopback filter
875        if interface.flags.is_loopback && !self.interface_config.include_loopback {
876            return false;
877        }
878
879        // Check operational state filter
880        if interface.state != InterfaceState::Up && !self.interface_config.include_down {
881            return false;
882        }
883
884        // Check MTU filter
885        if interface.mtu < self.interface_config.min_mtu {
886            return false;
887        }
888
889        // Check interface type filter
890        if !self.interface_config.allowed_interface_types.is_empty()
891            && !self
892                .interface_config
893                .allowed_interface_types
894                .contains(&interface.interface_type)
895        {
896            return false;
897        }
898
899        // Check if interface has any usable addresses
900        if interface.ipv4_addresses.is_empty() && interface.ipv6_addresses.is_empty() {
901            return false;
902        }
903
904        true
905    }
906
907    /// Convert Linux interface to generic NetworkInterface
908    fn convert_to_network_interface(&self, linux_interface: &LinuxInterface) -> NetworkInterface {
909        let mut addresses = Vec::new();
910
911        // Add IPv4 addresses
912        for (ipv4, _prefix) in &linux_interface.ipv4_addresses {
913            addresses.push(SocketAddr::new(IpAddr::V4(*ipv4), 0));
914        }
915
916        // Add IPv6 addresses
917        for (ipv6, _prefix) in &linux_interface.ipv6_addresses {
918            addresses.push(SocketAddr::new(IpAddr::V6(*ipv6), 0));
919        }
920
921        NetworkInterface {
922            name: linux_interface.name.clone(),
923            addresses,
924            is_up: linux_interface.state == InterfaceState::Up,
925            is_wireless: linux_interface.flags.is_wireless,
926            mtu: Some(linux_interface.mtu as u16),
927        }
928    }
929
930    /// Update cached interfaces with new scan results
931    fn update_cache(&mut self, interfaces: Vec<LinuxInterface>) {
932        self.cached_interfaces.clear();
933        for interface in interfaces {
934            self.cached_interfaces.insert(interface.index, interface);
935        }
936        self.last_scan_time = Some(Instant::now());
937    }
938
939    /// Check if cache is valid
940    fn is_cache_valid(&self) -> bool {
941        if let Some(last_scan) = self.last_scan_time {
942            last_scan.elapsed() < self.cache_ttl
943        } else {
944            false
945        }
946    }
947}
948
949impl NetworkInterfaceDiscovery for LinuxInterfaceDiscovery {
950    fn start_scan(&mut self) -> Result<(), String> {
951        debug!("Starting Linux network interface scan");
952
953        // Initialize netlink socket if monitoring is enabled
954        if self.interface_config.enable_monitoring {
955            if let Err(e) = self.initialize_netlink_socket() {
956                warn!("Failed to initialize netlink socket: {:?}", e);
957            }
958        }
959
960        // Check if we need to scan or can use cache
961        if self.is_cache_valid() {
962            if let Ok(changes) = self.check_network_changes() {
963                if !changes {
964                    debug!("Using cached interface data");
965                    let interfaces: Vec<NetworkInterface> = self
966                        .cached_interfaces
967                        .values()
968                        .map(|li| self.convert_to_network_interface(li))
969                        .collect();
970
971                    self.scan_state = ScanState::Completed {
972                        scan_results: interfaces,
973                    };
974                    return Ok(());
975                }
976            }
977        }
978
979        // Perform fresh scan
980        self.scan_state = ScanState::InProgress {
981            started_at: Instant::now(),
982        };
983
984        match self.enumerate_interfaces() {
985            Ok(interfaces) => {
986                debug!("Successfully enumerated {} interfaces", interfaces.len());
987
988                // Convert to generic NetworkInterface format
989                let network_interfaces: Vec<NetworkInterface> = interfaces
990                    .iter()
991                    .map(|li| self.convert_to_network_interface(li))
992                    .collect();
993
994                // Update cache
995                self.update_cache(interfaces);
996
997                self.scan_state = ScanState::Completed {
998                    scan_results: network_interfaces,
999                };
1000
1001                info!("Network interface scan completed successfully");
1002                Ok(())
1003            }
1004            Err(e) => {
1005                let error_msg = format!("Linux interface enumeration failed: {:?}", e);
1006                error!("{}", error_msg);
1007                self.scan_state = ScanState::Failed {
1008                    error: error_msg.clone(),
1009                };
1010                Err(error_msg)
1011            }
1012        }
1013    }
1014
1015    fn check_scan_complete(&mut self) -> Option<Vec<NetworkInterface>> {
1016        match &self.scan_state {
1017            ScanState::Completed { scan_results } => {
1018                let results = scan_results.clone();
1019                self.scan_state = ScanState::Idle;
1020                Some(results)
1021            }
1022            ScanState::Failed { error } => {
1023                warn!("Scan failed: {}", error);
1024                self.scan_state = ScanState::Idle;
1025                None
1026            }
1027            _ => None,
1028        }
1029    }
1030}
1031
1032impl Drop for LinuxInterfaceDiscovery {
1033    fn drop(&mut self) {
1034        // Clean up netlink socket
1035        if let Some(socket) = self.netlink_socket.take() {
1036            unsafe {
1037                libc::close(socket.socket_fd);
1038            }
1039        }
1040    }
1041}
1042
1043impl std::fmt::Display for LinuxNetworkError {
1044    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1045        match self {
1046            Self::SocketCreationFailed { error } => {
1047                write!(f, "Socket creation failed: {}", error)
1048            }
1049            Self::SocketBindFailed { error } => {
1050                write!(f, "Socket bind failed: {}", error)
1051            }
1052            Self::MessageSendFailed { error } => {
1053                write!(f, "Message send failed: {}", error)
1054            }
1055            Self::MessageReceiveFailed { error } => {
1056                write!(f, "Message receive failed: {}", error)
1057            }
1058            Self::InvalidMessage { message } => {
1059                write!(f, "Invalid message: {}", message)
1060            }
1061            Self::InterfaceNotFound { interface_name } => {
1062                write!(f, "Interface not found: {}", interface_name)
1063            }
1064            Self::PermissionDenied { operation } => {
1065                write!(f, "Permission denied for operation: {}", operation)
1066            }
1067            Self::SystemLimitExceeded { limit_type } => {
1068                write!(f, "System limit exceeded: {}", limit_type)
1069            }
1070            Self::NetworkNamespaceError { error } => {
1071                write!(f, "Network namespace error: {}", error)
1072            }
1073            Self::EnumerationTimeout { timeout } => {
1074                write!(f, "Enumeration timeout: {:?}", timeout)
1075            }
1076        }
1077    }
1078}
1079
1080impl std::error::Error for LinuxNetworkError {}
1081
1082#[cfg(test)]
1083mod tests {
1084    use super::*;
1085
1086    #[test]
1087    fn test_linux_interface_discovery_creation() {
1088        let discovery = LinuxInterfaceDiscovery::new();
1089        assert!(discovery.cached_interfaces.is_empty());
1090        assert!(discovery.last_scan_time.is_none());
1091    }
1092
1093    #[test]
1094    fn test_interface_config() {
1095        let mut discovery = LinuxInterfaceDiscovery::new();
1096        let config = InterfaceConfig {
1097            include_loopback: true,
1098            include_down: true,
1099            include_ipv6: false,
1100            min_mtu: 1000,
1101            max_interfaces: 32,
1102            enable_monitoring: false,
1103            allowed_interface_types: vec![InterfaceType::Ethernet],
1104        };
1105
1106        discovery.set_interface_config(config.clone());
1107        assert!(discovery.interface_config.include_loopback);
1108        assert_eq!(discovery.interface_config.min_mtu, 1000);
1109    }
1110
1111    #[test]
1112    fn test_wireless_interface_detection() {
1113        let discovery = LinuxInterfaceDiscovery::new();
1114
1115        assert!(discovery.is_wireless_interface("wlan0"));
1116        assert!(discovery.is_wireless_interface("wl0"));
1117        assert!(!discovery.is_wireless_interface("eth0"));
1118    }
1119
1120    #[test]
1121    fn test_interface_type_determination() {
1122        let discovery = LinuxInterfaceDiscovery::new();
1123        let flags = InterfaceFlags::default();
1124
1125        assert_eq!(
1126            discovery.determine_interface_type("eth0", &flags).unwrap(),
1127            InterfaceType::Ethernet
1128        );
1129        assert_eq!(
1130            discovery.determine_interface_type("wlan0", &flags).unwrap(),
1131            InterfaceType::Wireless
1132        );
1133        assert_eq!(
1134            discovery.determine_interface_type("tun0", &flags).unwrap(),
1135            InterfaceType::Tunnel
1136        );
1137    }
1138
1139    #[test]
1140    fn test_cache_validation() {
1141        let mut discovery = LinuxInterfaceDiscovery::new();
1142
1143        // No cache initially
1144        assert!(!discovery.is_cache_valid());
1145
1146        // Set cache time
1147        discovery.last_scan_time = Some(Instant::now());
1148        assert!(discovery.is_cache_valid());
1149
1150        // Expired cache
1151        discovery.last_scan_time = Some(Instant::now() - std::time::Duration::from_secs(60));
1152        assert!(!discovery.is_cache_valid());
1153    }
1154}