Skip to main content

ant_quic/candidate_discovery/
linux.rs

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