1use 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
17pub struct LinuxInterfaceDiscovery {
19 cached_interfaces: HashMap<u32, LinuxInterface>,
21 last_scan_time: Option<Instant>,
23 cache_ttl: std::time::Duration,
25 scan_state: ScanState,
27 netlink_socket: Option<NetlinkSocket>,
29 interface_config: InterfaceConfig,
31}
32
33#[derive(Debug, Clone)]
35struct LinuxInterface {
36 index: u32,
38 name: String,
40 interface_type: InterfaceType,
42 flags: InterfaceFlags,
44 mtu: u32,
46 ipv4_addresses: Vec<(Ipv4Addr, u8)>,
48 ipv6_addresses: Vec<(Ipv6Addr, u8)>,
50 hardware_address: Option<[u8; 6]>,
52 state: InterfaceState,
54 last_updated: Instant,
56}
57
58#[derive(Debug, Clone, Copy, PartialEq, Eq)]
60enum InterfaceType {
61 Ethernet,
63 Wireless,
65 Loopback,
67 Tunnel,
69 PointToPoint,
71 Bridge,
73 Vlan,
75 Bond,
77 Virtual,
79 Unknown(u16),
81}
82
83#[derive(Debug, Clone, Copy, Default)]
85struct InterfaceFlags {
86 is_up: bool,
88 is_running: bool,
90 is_loopback: bool,
92 is_point_to_point: bool,
94 supports_multicast: bool,
96 supports_broadcast: bool,
98 is_wireless: bool,
100}
101
102#[derive(Debug, Clone, Copy, PartialEq, Eq)]
104enum InterfaceState {
105 Unknown,
107 NotPresent,
109 Down,
111 LowerLayerDown,
113 Testing,
115 Dormant,
117 Up,
119}
120
121#[derive(Debug, Clone, PartialEq)]
123enum ScanState {
124 Idle,
126 InProgress { started_at: Instant },
128 Completed { scan_results: Vec<NetworkInterface> },
130 Failed { error: String },
132}
133
134struct NetlinkSocket {
136 socket_fd: i32,
138 sequence_number: u32,
140 process_id: u32,
142 receive_buffer: Vec<u8>,
144 last_message_time: Option<Instant>,
146}
147
148#[derive(Debug, Clone)]
150struct InterfaceConfig {
151 include_loopback: bool,
153 include_down: bool,
155 include_ipv6: bool,
157 min_mtu: u32,
159 max_interfaces: u32,
161 enable_monitoring: bool,
163 allowed_interface_types: Vec<InterfaceType>,
165}
166
167#[derive(Debug, Clone)]
169enum LinuxNetworkError {
170 SocketCreationFailed { error: String },
172 SocketBindFailed { error: String },
174 MessageSendFailed { error: String },
176 MessageReceiveFailed { error: String },
178 InvalidMessage { message: String },
180 InterfaceNotFound { interface_name: String },
182 PermissionDenied { operation: String },
184 SystemLimitExceeded { limit_type: String },
186 NetworkNamespaceError { error: String },
188 EnumerationTimeout { timeout: std::time::Duration },
190}
191
192#[derive(Debug, Clone, Copy, PartialEq, Eq)]
194enum NetlinkMessageType {
195 GetLink,
197 GetAddress,
199 LinkStateChange,
201 AddressChange,
203 RouteChange,
205}
206
207#[derive(Debug, Clone)]
209struct NetlinkMessage {
210 message_type: NetlinkMessageType,
212 flags: u16,
214 sequence: u32,
216 payload: Vec<u8>,
218}
219
220impl LinuxInterfaceDiscovery {
221 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, 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 pub fn set_interface_config(&mut self, config: InterfaceConfig) {
248 self.interface_config = config;
249 }
250
251 pub fn initialize_netlink_socket(&mut self) -> Result<(), LinuxNetworkError> {
253 if self.netlink_socket.is_some() {
254 return Ok(());
255 }
256
257 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 let mut addr: libc::sockaddr_nl = unsafe { std::mem::zeroed() };
277 addr.nl_family = libc::AF_NETLINK as u16;
278 addr.nl_pid = 0; addr.nl_groups = (1 << (libc::RTNLGRP_LINK - 1))
280 | (1 << (libc::RTNLGRP_IPV4_IFADDR - 1))
281 | (1 << (libc::RTNLGRP_IPV6_IFADDR - 1));
282
283 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 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 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 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 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, _ => {
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; }
381
382 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 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 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; }
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 fn enumerate_interfaces(&mut self) -> Result<Vec<LinuxInterface>, LinuxNetworkError> {
462 let mut interfaces = Vec::new();
463
464 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 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 fn get_interface_details(
511 &self,
512 interface_name: &str,
513 ) -> Result<LinuxInterface, LinuxNetworkError> {
514 let interface_index = self.get_interface_index(interface_name)?;
516
517 let (flags, state, mtu) = self.get_interface_flags_and_state(interface_name)?;
519
520 let interface_type = self.determine_interface_type(interface_name, &flags)?;
522
523 let hardware_address = self.get_hardware_address(interface_name).ok();
525
526 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 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 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 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 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 };
626
627 unsafe {
628 libc::close(socket_fd);
629 }
630
631 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 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 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 fn is_wireless_interface(&self, interface_name: &str) -> bool {
695 if interface_name.starts_with("wlan") || interface_name.starts_with("wl") {
697 return true;
698 }
699
700 let wireless_path = format!("/sys/class/net/{}/wireless", interface_name);
702 std::path::Path::new(&wireless_path).exists()
703 }
704
705 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 fn get_ipv4_addresses(
757 &self,
758 interface_name: &str,
759 ) -> Result<Vec<(Ipv4Addr, u8)>, LinuxNetworkError> {
760 let mut addresses = Vec::new();
761
762 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 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 };
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 fn get_ipv6_addresses(
824 &self,
825 interface_name: &str,
826 ) -> Result<Vec<(Ipv6Addr, u8)>, LinuxNetworkError> {
827 let mut addresses = Vec::new();
828
829 let if_inet6_content = match std::fs::read_to_string("/proc/net/if_inet6") {
831 Ok(content) => content,
832 Err(_) => return Ok(addresses), };
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 if addr_str.len() == 32 {
846 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 fn should_include_interface(&self, interface: &LinuxInterface) -> bool {
874 if interface.flags.is_loopback && !self.interface_config.include_loopback {
876 return false;
877 }
878
879 if interface.state != InterfaceState::Up && !self.interface_config.include_down {
881 return false;
882 }
883
884 if interface.mtu < self.interface_config.min_mtu {
886 return false;
887 }
888
889 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 if interface.ipv4_addresses.is_empty() && interface.ipv6_addresses.is_empty() {
901 return false;
902 }
903
904 true
905 }
906
907 fn convert_to_network_interface(&self, linux_interface: &LinuxInterface) -> NetworkInterface {
909 let mut addresses = Vec::new();
910
911 for (ipv4, _prefix) in &linux_interface.ipv4_addresses {
913 addresses.push(SocketAddr::new(IpAddr::V4(*ipv4), 0));
914 }
915
916 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 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 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 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 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 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 let network_interfaces: Vec<NetworkInterface> = interfaces
990 .iter()
991 .map(|li| self.convert_to_network_interface(li))
992 .collect();
993
994 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 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 assert!(!discovery.is_cache_valid());
1145
1146 discovery.last_scan_time = Some(Instant::now());
1148 assert!(discovery.is_cache_valid());
1149
1150 discovery.last_scan_time = Some(Instant::now() - std::time::Duration::from_secs(60));
1152 assert!(!discovery.is_cache_valid());
1153 }
1154}