1use 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
24pub struct LinuxInterfaceDiscovery {
26 cached_interfaces: HashMap<u32, LinuxInterface>,
28 last_scan_time: Option<Instant>,
30 cache_ttl: std::time::Duration,
32 scan_state: ScanState,
34 netlink_socket: Option<NetlinkSocket>,
36 interface_config: InterfaceConfig,
38}
39
40#[derive(Debug, Clone)]
42struct LinuxInterface {
43 index: u32,
45 name: String,
47 interface_type: InterfaceType,
49 flags: InterfaceFlags,
51 mtu: u32,
53 ipv4_addresses: Vec<(Ipv4Addr, u8)>,
55 ipv6_addresses: Vec<(Ipv6Addr, u8)>,
57 #[allow(dead_code)]
59 hardware_address: Option<[u8; 6]>,
60 state: InterfaceState,
62 #[allow(dead_code)]
64 last_updated: Instant,
65}
66
67#[derive(Debug, Clone, Copy, PartialEq, Eq)]
69enum InterfaceType {
70 Ethernet,
72 Wireless,
74 Loopback,
76 Tunnel,
78 PointToPoint,
80 Bridge,
82 Vlan,
84 Bond,
86 Virtual,
88 Unknown(u16),
90}
91
92#[derive(Debug, Clone, Copy, Default)]
94struct InterfaceFlags {
95 is_up: bool,
97 is_running: bool,
99 is_loopback: bool,
101 is_point_to_point: bool,
103 #[allow(dead_code)]
105 supports_multicast: bool,
106 #[allow(dead_code)]
108 supports_broadcast: bool,
109 is_wireless: bool,
111}
112
113#[derive(Debug, Clone, Copy, PartialEq, Eq)]
115enum InterfaceState {
116 #[allow(dead_code)]
118 Unknown,
119 #[allow(dead_code)]
121 NotPresent,
122 Down,
124 #[allow(dead_code)]
126 LowerLayerDown,
127 #[allow(dead_code)]
129 Testing,
130 #[allow(dead_code)]
132 Dormant,
133 Up,
135}
136
137#[derive(Debug, Clone, PartialEq)]
139enum ScanState {
140 Idle,
142 InProgress { started_at: Instant },
144 Completed { scan_results: Vec<NetworkInterface> },
146 Failed { error: String },
148}
149
150struct NetlinkSocket {
152 socket_fd: i32,
154 #[allow(dead_code)]
156 sequence_number: u32,
157 #[allow(dead_code)]
159 process_id: u32,
160 receive_buffer: Vec<u8>,
162 last_message_time: Option<Instant>,
164}
165
166#[derive(Debug, Clone)]
168struct InterfaceConfig {
169 include_loopback: bool,
171 include_down: bool,
173 include_ipv6: bool,
175 min_mtu: u32,
177 max_interfaces: u32,
179 enable_monitoring: bool,
181 allowed_interface_types: Vec<InterfaceType>,
183}
184
185#[derive(Debug, Clone)]
187pub enum LinuxNetworkError {
188 SocketCreationFailed { error: String },
190 SocketBindFailed { error: String },
192 MessageSendFailed { error: String },
194 MessageReceiveFailed { error: String },
196 InvalidMessage { message: String },
198 InterfaceNotFound { interface_name: String },
200 PermissionDenied { operation: String },
202 SystemLimitExceeded { limit_type: String },
204 NetworkNamespaceError { error: String },
206 EnumerationTimeout { timeout: std::time::Duration },
208}
209
210#[derive(Debug, Clone, Copy, PartialEq, Eq)]
212enum NetlinkMessageType {
213 #[allow(dead_code)]
215 GetLink,
216 #[allow(dead_code)]
218 GetAddress,
219 LinkStateChange,
221 AddressChange,
223 RouteChange,
225}
226
227#[derive(Debug, Clone)]
229struct NetlinkMessage {
230 message_type: NetlinkMessageType,
232 #[allow(dead_code)]
234 flags: u16,
235 #[allow(dead_code)]
237 sequence: u32,
238 #[allow(dead_code)]
240 payload: Vec<u8>,
241}
242
243impl LinuxInterfaceDiscovery {
244 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, 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 pub fn set_interface_config(&mut self, config: InterfaceConfig) {
271 self.interface_config = config;
272 }
273
274 pub fn initialize_netlink_socket(&mut self) -> Result<(), LinuxNetworkError> {
276 if self.netlink_socket.is_some() {
277 return Ok(());
278 }
279
280 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 let mut addr: libc::sockaddr_nl = unsafe { std::mem::zeroed() };
306 addr.nl_family = libc::AF_NETLINK as u16;
307 addr.nl_pid = 0; addr.nl_groups = (1 << (libc::RTNLGRP_LINK - 1))
309 | (1 << (libc::RTNLGRP_IPV4_IFADDR - 1))
310 | (1 << (libc::RTNLGRP_IPV6_IFADDR - 1));
311
312 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 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 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 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 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, _ => {
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; }
410
411 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 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 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; }
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 fn enumerate_interfaces(&mut self) -> Result<Vec<LinuxInterface>, LinuxNetworkError> {
491 let mut interfaces = Vec::new();
492
493 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 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 fn get_interface_details(
540 &self,
541 interface_name: &str,
542 ) -> Result<LinuxInterface, LinuxNetworkError> {
543 let interface_index = self.get_interface_index(interface_name)?;
545
546 let (flags, state, mtu) = self.get_interface_flags_and_state(interface_name)?;
548
549 let interface_type = self.determine_interface_type(interface_name, &flags)?;
551
552 let hardware_address = self.get_hardware_address(interface_name).ok();
554
555 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 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 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 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 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 };
655
656 unsafe {
657 libc::close(socket_fd);
658 }
659
660 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 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 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 fn is_wireless_interface(&self, interface_name: &str) -> bool {
724 if interface_name.starts_with("wlan") || interface_name.starts_with("wl") {
726 return true;
727 }
728
729 let wireless_path = format!("/sys/class/net/{}/wireless", interface_name);
731 std::path::Path::new(&wireless_path).exists()
732 }
733
734 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 fn get_ipv4_addresses(
786 &self,
787 interface_name: &str,
788 ) -> Result<Vec<(Ipv4Addr, u8)>, LinuxNetworkError> {
789 let mut addresses = Vec::new();
790
791 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 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 };
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 fn get_ipv6_addresses(
853 &self,
854 interface_name: &str,
855 ) -> Result<Vec<(Ipv6Addr, u8)>, LinuxNetworkError> {
856 let mut addresses = Vec::new();
857
858 let if_inet6_content = match std::fs::read_to_string("/proc/net/if_inet6") {
860 Ok(content) => content,
861 Err(_) => return Ok(addresses), };
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 if addr_str.len() == 32 {
875 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 fn should_include_interface(&self, interface: &LinuxInterface) -> bool {
903 if interface.flags.is_loopback && !self.interface_config.include_loopback {
905 return false;
906 }
907
908 if interface.state != InterfaceState::Up && !self.interface_config.include_down {
910 return false;
911 }
912
913 if interface.mtu < self.interface_config.min_mtu {
915 return false;
916 }
917
918 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 if interface.ipv4_addresses.is_empty() && interface.ipv6_addresses.is_empty() {
930 return false;
931 }
932
933 true
934 }
935
936 fn convert_to_network_interface(&self, linux_interface: &LinuxInterface) -> NetworkInterface {
938 let mut addresses = Vec::new();
939
940 for (ipv4, _prefix) in &linux_interface.ipv4_addresses {
942 addresses.push(SocketAddr::new(IpAddr::V4(*ipv4), 0));
943 }
944
945 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 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 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 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 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 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 let network_interfaces: Vec<NetworkInterface> = interfaces
1019 .iter()
1020 .map(|li| self.convert_to_network_interface(li))
1021 .collect();
1022
1023 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 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 assert!(!discovery.is_cache_valid());
1174
1175 discovery.last_scan_time = Some(Instant::now());
1177 assert!(discovery.is_cache_valid());
1178
1179 discovery.last_scan_time = Some(Instant::now() - std::time::Duration::from_secs(60));
1181 assert!(!discovery.is_cache_valid());
1182 }
1183}