1use std::{
7 collections::HashMap,
8 ffi::{CStr, CString},
9 io,
10 mem,
11 net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
12 os::unix::io::{AsRawFd, RawFd},
13 ptr,
14 slice,
15 time::Instant,
16};
17
18use nix::libc;
19use tracing::{debug, error, info, warn};
20
21use crate::candidate_discovery::{NetworkInterface, NetworkInterfaceDiscovery};
22
23pub struct LinuxInterfaceDiscovery {
25 cached_interfaces: HashMap<u32, LinuxInterface>,
27 last_scan_time: Option<Instant>,
29 cache_ttl: std::time::Duration,
31 scan_state: ScanState,
33 netlink_socket: Option<NetlinkSocket>,
35 interface_config: InterfaceConfig,
37}
38
39#[derive(Debug, Clone)]
41struct LinuxInterface {
42 index: u32,
44 name: String,
46 interface_type: InterfaceType,
48 flags: InterfaceFlags,
50 mtu: u32,
52 ipv4_addresses: Vec<(Ipv4Addr, u8)>,
54 ipv6_addresses: Vec<(Ipv6Addr, u8)>,
56 hardware_address: Option<[u8; 6]>,
58 state: InterfaceState,
60 last_updated: Instant,
62}
63
64#[derive(Debug, Clone, Copy, PartialEq, Eq)]
66enum InterfaceType {
67 Ethernet,
69 Wireless,
71 Loopback,
73 Tunnel,
75 PointToPoint,
77 Bridge,
79 Vlan,
81 Bond,
83 Virtual,
85 Unknown(u16),
87}
88
89#[derive(Debug, Clone, Copy, Default)]
91struct InterfaceFlags {
92 is_up: bool,
94 is_running: bool,
96 is_loopback: bool,
98 is_point_to_point: bool,
100 supports_multicast: bool,
102 supports_broadcast: bool,
104 is_wireless: bool,
106}
107
108#[derive(Debug, Clone, Copy, PartialEq, Eq)]
110enum InterfaceState {
111 Unknown,
113 NotPresent,
115 Down,
117 LowerLayerDown,
119 Testing,
121 Dormant,
123 Up,
125}
126
127#[derive(Debug, Clone, PartialEq)]
129enum ScanState {
130 Idle,
132 InProgress { started_at: Instant },
134 Completed { scan_results: Vec<NetworkInterface> },
136 Failed { error: String },
138}
139
140struct NetlinkSocket {
142 socket_fd: i32,
144 sequence_number: u32,
146 process_id: u32,
148 receive_buffer: Vec<u8>,
150 last_message_time: Option<Instant>,
152}
153
154#[derive(Debug, Clone)]
156struct InterfaceConfig {
157 include_loopback: bool,
159 include_down: bool,
161 include_ipv6: bool,
163 min_mtu: u32,
165 max_interfaces: u32,
167 enable_monitoring: bool,
169 allowed_interface_types: Vec<InterfaceType>,
171}
172
173#[derive(Debug, Clone)]
175enum LinuxNetworkError {
176 SocketCreationFailed { error: String },
178 SocketBindFailed { error: String },
180 MessageSendFailed { error: String },
182 MessageReceiveFailed { error: String },
184 InvalidMessage { message: String },
186 InterfaceNotFound { interface_name: String },
188 PermissionDenied { operation: String },
190 SystemLimitExceeded { limit_type: String },
192 NetworkNamespaceError { error: String },
194 EnumerationTimeout { timeout: std::time::Duration },
196}
197
198#[derive(Debug, Clone, Copy, PartialEq, Eq)]
200enum NetlinkMessageType {
201 GetLink,
203 GetAddress,
205 LinkStateChange,
207 AddressChange,
209 RouteChange,
211}
212
213#[derive(Debug, Clone)]
215struct NetlinkMessage {
216 message_type: NetlinkMessageType,
218 flags: u16,
220 sequence: u32,
222 payload: Vec<u8>,
224}
225
226impl LinuxInterfaceDiscovery {
227 pub fn new() -> Self {
229 Self {
230 cached_interfaces: HashMap::new(),
231 last_scan_time: None,
232 cache_ttl: std::time::Duration::from_secs(30),
233 scan_state: ScanState::Idle,
234 netlink_socket: None,
235 interface_config: InterfaceConfig {
236 include_loopback: false,
237 include_down: false,
238 include_ipv6: true,
239 min_mtu: 1280, max_interfaces: 64,
241 enable_monitoring: true,
242 allowed_interface_types: vec![
243 InterfaceType::Ethernet,
244 InterfaceType::Wireless,
245 InterfaceType::Tunnel,
246 InterfaceType::Bridge,
247 ],
248 },
249 }
250 }
251
252 pub fn set_interface_config(&mut self, config: InterfaceConfig) {
254 self.interface_config = config;
255 }
256
257 pub fn initialize_netlink_socket(&mut self) -> Result<(), LinuxNetworkError> {
259 if self.netlink_socket.is_some() {
260 return Ok(());
261 }
262
263 let socket_fd = unsafe {
265 libc::socket(
266 libc::AF_NETLINK,
267 libc::SOCK_RAW | libc::SOCK_CLOEXEC,
268 libc::NETLINK_ROUTE,
269 )
270 };
271
272 if socket_fd < 0 {
273 return Err(LinuxNetworkError::SocketCreationFailed {
274 error: format!("Failed to create netlink socket: {}",
275 std::io::Error::last_os_error()),
276 });
277 }
278
279 let mut addr: libc::sockaddr_nl = unsafe { std::mem::zeroed() };
281 addr.nl_family = libc::AF_NETLINK as u16;
282 addr.nl_pid = 0; addr.nl_groups = (1 << (libc::RTNLGRP_LINK - 1)) | (1 << (libc::RTNLGRP_IPV4_IFADDR - 1)) | (1 << (libc::RTNLGRP_IPV6_IFADDR - 1));
284
285 let bind_result = unsafe {
287 libc::bind(
288 socket_fd,
289 &addr as *const libc::sockaddr_nl as *const libc::sockaddr,
290 std::mem::size_of::<libc::sockaddr_nl>() as libc::socklen_t,
291 )
292 };
293
294 if bind_result < 0 {
295 unsafe { libc::close(socket_fd); }
296 return Err(LinuxNetworkError::SocketBindFailed {
297 error: format!("Failed to bind netlink socket: {}",
298 std::io::Error::last_os_error()),
299 });
300 }
301
302 let mut addr_len = std::mem::size_of::<libc::sockaddr_nl>() as libc::socklen_t;
304 let getsockname_result = unsafe {
305 libc::getsockname(
306 socket_fd,
307 &mut addr as *mut libc::sockaddr_nl as *mut libc::sockaddr,
308 &mut addr_len,
309 )
310 };
311
312 if getsockname_result < 0 {
313 unsafe { libc::close(socket_fd); }
314 return Err(LinuxNetworkError::SocketBindFailed {
315 error: format!("Failed to get socket name: {}",
316 std::io::Error::last_os_error()),
317 });
318 }
319
320 let flags = unsafe { libc::fcntl(socket_fd, libc::F_GETFL) };
322 if flags >= 0 {
323 unsafe {
324 libc::fcntl(socket_fd, libc::F_SETFL, flags | libc::O_NONBLOCK);
325 }
326 }
327
328 self.netlink_socket = Some(NetlinkSocket {
329 socket_fd,
330 sequence_number: 1,
331 process_id: addr.nl_pid,
332 receive_buffer: vec![0; 8192],
333 last_message_time: None,
334 });
335
336 debug!("Netlink socket initialized with PID {}", addr.nl_pid);
337 Ok(())
338 }
339
340 pub fn check_network_changes(&mut self) -> Result<bool, LinuxNetworkError> {
342 let socket = match self.netlink_socket.as_mut() {
343 Some(socket) => socket,
344 None => return Ok(false),
345 };
346
347 let mut changes_detected = false;
348
349 loop {
351 let bytes_read = unsafe {
352 libc::recv(
353 socket.socket_fd,
354 socket.receive_buffer.as_mut_ptr() as *mut libc::c_void,
355 socket.receive_buffer.len(),
356 0,
357 )
358 };
359
360 if bytes_read < 0 {
361 let error = std::io::Error::last_os_error();
362 match error.kind() {
363 std::io::ErrorKind::WouldBlock => break, _ => {
365 return Err(LinuxNetworkError::MessageReceiveFailed {
366 error: format!("Failed to receive netlink message: {}", error),
367 });
368 }
369 }
370 }
371
372 if bytes_read == 0 {
373 break; }
375
376 let messages = Self::parse_netlink_messages(&socket.receive_buffer[..bytes_read as usize])?;
378
379 for message in messages {
380 match message.message_type {
381 NetlinkMessageType::LinkStateChange |
382 NetlinkMessageType::AddressChange => {
383 changes_detected = true;
384 debug!("Network change detected: {:?}", message.message_type);
385 }
386 _ => {}
387 }
388 }
389
390 socket.last_message_time = Some(Instant::now());
391 }
392
393 Ok(changes_detected)
394 }
395
396 fn parse_netlink_messages(buffer: &[u8]) -> Result<Vec<NetlinkMessage>, LinuxNetworkError> {
398 let mut messages = Vec::new();
399 let mut offset = 0;
400
401 while offset + 16 <= buffer.len() {
402 let length = u32::from_ne_bytes([
404 buffer[offset],
405 buffer[offset + 1],
406 buffer[offset + 2],
407 buffer[offset + 3],
408 ]) as usize;
409
410 if length < 16 || offset + length > buffer.len() {
411 break; }
413
414 let msg_type = u16::from_ne_bytes([
415 buffer[offset + 4],
416 buffer[offset + 5],
417 ]);
418
419 let flags = u16::from_ne_bytes([
420 buffer[offset + 6],
421 buffer[offset + 7],
422 ]);
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!("Failed to get interface details for {}: {:?}", interface_name, e);
494 }
495 }
496
497 if interfaces.len() >= self.interface_config.max_interfaces as usize {
498 break;
499 }
500 }
501
502 debug!("Enumerated {} network interfaces", interfaces.len());
503 Ok(interfaces)
504 }
505
506 fn get_interface_details(&self, interface_name: &str) -> Result<LinuxInterface, LinuxNetworkError> {
508 let interface_index = self.get_interface_index(interface_name)?;
510
511 let (flags, state, mtu) = self.get_interface_flags_and_state(interface_name)?;
513
514 let interface_type = self.determine_interface_type(interface_name, &flags)?;
516
517 let hardware_address = self.get_hardware_address(interface_name).ok();
519
520 let ipv4_addresses = self.get_ipv4_addresses(interface_name)?;
522 let ipv6_addresses = if self.interface_config.include_ipv6 {
523 self.get_ipv6_addresses(interface_name)?
524 } else {
525 Vec::new()
526 };
527
528 Ok(LinuxInterface {
529 index: interface_index,
530 name: interface_name.to_string(),
531 interface_type,
532 flags,
533 mtu,
534 ipv4_addresses,
535 ipv6_addresses,
536 hardware_address,
537 state,
538 last_updated: Instant::now(),
539 })
540 }
541
542 fn get_interface_index(&self, interface_name: &str) -> Result<u32, LinuxNetworkError> {
544 let c_name = std::ffi::CString::new(interface_name).map_err(|_| {
545 LinuxNetworkError::InterfaceNotFound {
546 interface_name: format!("Invalid interface name: {}", interface_name),
547 }
548 })?;
549
550 let index = unsafe { libc::if_nametoindex(c_name.as_ptr()) };
551 if index == 0 {
552 return Err(LinuxNetworkError::InterfaceNotFound {
553 interface_name: interface_name.to_string(),
554 });
555 }
556
557 Ok(index)
558 }
559
560 fn get_interface_flags_and_state(&self, interface_name: &str) -> Result<(InterfaceFlags, InterfaceState, u32), LinuxNetworkError> {
562 let socket_fd = unsafe { libc::socket(libc::AF_INET, libc::SOCK_DGRAM, 0) };
563 if socket_fd < 0 {
564 return Err(LinuxNetworkError::SocketCreationFailed {
565 error: "Failed to create socket for interface query".to_string(),
566 });
567 }
568
569 let mut ifreq: libc::ifreq = unsafe { std::mem::zeroed() };
570 let name_bytes = interface_name.as_bytes();
571 let copy_len = std::cmp::min(name_bytes.len(), libc::IFNAMSIZ - 1);
572
573 unsafe {
574 std::ptr::copy_nonoverlapping(
575 name_bytes.as_ptr(),
576 ifreq.ifr_name.as_mut_ptr() as *mut u8,
577 copy_len,
578 );
579 }
580
581 let flags_result = unsafe { libc::ioctl(socket_fd, libc::SIOCGIFFLAGS, &mut ifreq) };
583 if flags_result < 0 {
584 unsafe { libc::close(socket_fd); }
585 return Err(LinuxNetworkError::InterfaceNotFound {
586 interface_name: format!("Failed to get flags for interface {}", interface_name),
587 });
588 }
589
590 let raw_flags = unsafe { ifreq.ifr_ifru.ifru_flags };
591 let flags = InterfaceFlags {
592 is_up: (raw_flags & libc::IFF_UP as i16) != 0,
593 is_running: (raw_flags & libc::IFF_RUNNING as i16) != 0,
594 is_loopback: (raw_flags & libc::IFF_LOOPBACK as i16) != 0,
595 is_point_to_point: (raw_flags & libc::IFF_POINTOPOINT as i16) != 0,
596 supports_multicast: (raw_flags & libc::IFF_MULTICAST as i16) != 0,
597 supports_broadcast: (raw_flags & libc::IFF_BROADCAST as i16) != 0,
598 is_wireless: self.is_wireless_interface(interface_name),
599 };
600
601 let mtu_result = unsafe { libc::ioctl(socket_fd, libc::SIOCGIFMTU, &mut ifreq) };
603 let mtu = if mtu_result >= 0 {
604 unsafe { ifreq.ifr_ifru.ifru_mtu as u32 }
605 } else {
606 1500 };
608
609 unsafe { libc::close(socket_fd); }
610
611 let state = if flags.is_up && flags.is_running {
613 InterfaceState::Up
614 } else if flags.is_up {
615 InterfaceState::Down
616 } else {
617 InterfaceState::Down
618 };
619
620 Ok((flags, state, mtu))
621 }
622
623 fn determine_interface_type(&self, interface_name: &str, flags: &InterfaceFlags) -> Result<InterfaceType, LinuxNetworkError> {
625 if flags.is_loopback {
626 return Ok(InterfaceType::Loopback);
627 }
628
629 if flags.is_point_to_point {
630 return Ok(InterfaceType::PointToPoint);
631 }
632
633 if flags.is_wireless {
634 return Ok(InterfaceType::Wireless);
635 }
636
637 if interface_name.starts_with("eth") || interface_name.starts_with("en") {
639 return Ok(InterfaceType::Ethernet);
640 }
641
642 if interface_name.starts_with("wlan") || interface_name.starts_with("wl") {
643 return Ok(InterfaceType::Wireless);
644 }
645
646 if interface_name.starts_with("tun") || interface_name.starts_with("tap") {
647 return Ok(InterfaceType::Tunnel);
648 }
649
650 if interface_name.starts_with("br") {
651 return Ok(InterfaceType::Bridge);
652 }
653
654 if interface_name.contains('.') {
655 return Ok(InterfaceType::Vlan);
656 }
657
658 if interface_name.starts_with("bond") {
659 return Ok(InterfaceType::Bond);
660 }
661
662 if interface_name.starts_with("veth") || interface_name.starts_with("docker") {
663 return Ok(InterfaceType::Virtual);
664 }
665
666 Ok(InterfaceType::Unknown(0))
667 }
668
669 fn is_wireless_interface(&self, interface_name: &str) -> bool {
671 if interface_name.starts_with("wlan") || interface_name.starts_with("wl") {
673 return true;
674 }
675
676 let wireless_path = format!("/sys/class/net/{}/wireless", interface_name);
678 std::path::Path::new(&wireless_path).exists()
679 }
680
681 fn get_hardware_address(&self, interface_name: &str) -> Result<[u8; 6], LinuxNetworkError> {
683 let socket_fd = unsafe { libc::socket(libc::AF_INET, libc::SOCK_DGRAM, 0) };
684 if socket_fd < 0 {
685 return Err(LinuxNetworkError::SocketCreationFailed {
686 error: "Failed to create socket for hardware address query".to_string(),
687 });
688 }
689
690 let mut ifreq: libc::ifreq = unsafe { std::mem::zeroed() };
691 let name_bytes = interface_name.as_bytes();
692 let copy_len = std::cmp::min(name_bytes.len(), libc::IFNAMSIZ - 1);
693
694 unsafe {
695 std::ptr::copy_nonoverlapping(
696 name_bytes.as_ptr(),
697 ifreq.ifr_name.as_mut_ptr() as *mut u8,
698 copy_len,
699 );
700 }
701
702 let result = unsafe { libc::ioctl(socket_fd, libc::SIOCGIFHWADDR, &mut ifreq) };
703 unsafe { libc::close(socket_fd); }
704
705 if result < 0 {
706 return Err(LinuxNetworkError::InterfaceNotFound {
707 interface_name: format!("Failed to get hardware address for {}", interface_name),
708 });
709 }
710
711 let mut hw_addr = [0u8; 6];
712 unsafe {
713 std::ptr::copy_nonoverlapping(
714 ifreq.ifr_ifru.ifru_hwaddr.sa_data.as_ptr() as *const u8,
715 hw_addr.as_mut_ptr(),
716 6,
717 );
718 }
719
720 Ok(hw_addr)
721 }
722
723 fn get_ipv4_addresses(&self, interface_name: &str) -> Result<Vec<(Ipv4Addr, u8)>, LinuxNetworkError> {
725 let mut addresses = Vec::new();
726
727 let socket_fd = unsafe { libc::socket(libc::AF_INET, libc::SOCK_DGRAM, 0) };
730 if socket_fd < 0 {
731 return Ok(addresses);
732 }
733
734 let mut ifreq: libc::ifreq = unsafe { std::mem::zeroed() };
735 let name_bytes = interface_name.as_bytes();
736 let copy_len = std::cmp::min(name_bytes.len(), libc::IFNAMSIZ - 1);
737
738 unsafe {
739 std::ptr::copy_nonoverlapping(
740 name_bytes.as_ptr(),
741 ifreq.ifr_name.as_mut_ptr() as *mut u8,
742 copy_len,
743 );
744 }
745
746 let result = unsafe { libc::ioctl(socket_fd, libc::SIOCGIFADDR, &mut ifreq) };
747 if result >= 0 {
748 let sockaddr_in = unsafe {
749 &*(&ifreq.ifr_ifru.ifru_addr as *const libc::sockaddr as *const libc::sockaddr_in)
750 };
751
752 if sockaddr_in.sin_family == libc::AF_INET as u16 {
753 let ip_bytes = sockaddr_in.sin_addr.s_addr.to_ne_bytes();
754 let ipv4_addr = Ipv4Addr::from(ip_bytes);
755
756 let netmask_result = unsafe { libc::ioctl(socket_fd, libc::SIOCGIFNETMASK, &mut ifreq) };
758 let prefix_len = if netmask_result >= 0 {
759 let netmask_sockaddr_in = unsafe {
760 &*(&ifreq.ifr_ifru.ifru_netmask as *const libc::sockaddr as *const libc::sockaddr_in)
761 };
762 let netmask_bytes = netmask_sockaddr_in.sin_addr.s_addr.to_ne_bytes();
763 let netmask = u32::from_ne_bytes(netmask_bytes);
764 netmask.count_ones() as u8
765 } else {
766 24 };
768
769 addresses.push((ipv4_addr, prefix_len));
770 }
771 }
772
773 unsafe { libc::close(socket_fd); }
774 Ok(addresses)
775 }
776
777 fn get_ipv6_addresses(&self, interface_name: &str) -> Result<Vec<(Ipv6Addr, u8)>, LinuxNetworkError> {
779 let mut addresses = Vec::new();
780
781 let if_inet6_content = match std::fs::read_to_string("/proc/net/if_inet6") {
783 Ok(content) => content,
784 Err(_) => return Ok(addresses), };
786
787 for line in if_inet6_content.lines() {
788 let parts: Vec<&str> = line.split_whitespace().collect();
789 if parts.len() >= 6 {
790 let addr_str = parts[0];
791 let prefix_len_str = parts[1];
792 let if_name = parts[5];
793
794 if if_name == interface_name {
795 if let Ok(prefix_len) = u8::from_str_radix(prefix_len_str, 16) {
796 if addr_str.len() == 32 {
798 let mut ipv6_bytes = [0u8; 16];
800 let mut valid = true;
801 for i in 0..16 {
802 if let Ok(byte) = u8::from_str_radix(&addr_str[i*2..i*2+2], 16) {
803 ipv6_bytes[i] = byte;
804 } else {
805 valid = false;
806 break;
807 }
808 }
809 if valid {
810 let ipv6_addr = Ipv6Addr::from(ipv6_bytes);
811 addresses.push((ipv6_addr, prefix_len));
812 }
813 }
814 }
815 }
816 }
817 }
818
819 Ok(addresses)
820 }
821
822 fn should_include_interface(&self, interface: &LinuxInterface) -> bool {
824 if interface.flags.is_loopback && !self.interface_config.include_loopback {
826 return false;
827 }
828
829 if interface.state != InterfaceState::Up && !self.interface_config.include_down {
831 return false;
832 }
833
834 if interface.mtu < self.interface_config.min_mtu {
836 return false;
837 }
838
839 if !self.interface_config.allowed_interface_types.is_empty() &&
841 !self.interface_config.allowed_interface_types.contains(&interface.interface_type) {
842 return false;
843 }
844
845 if interface.ipv4_addresses.is_empty() && interface.ipv6_addresses.is_empty() {
847 return false;
848 }
849
850 true
851 }
852
853 fn convert_to_network_interface(&self, linux_interface: &LinuxInterface) -> NetworkInterface {
855 let mut addresses = Vec::new();
856
857 for (ipv4, _prefix) in &linux_interface.ipv4_addresses {
859 addresses.push(SocketAddr::new(IpAddr::V4(*ipv4), 0));
860 }
861
862 for (ipv6, _prefix) in &linux_interface.ipv6_addresses {
864 addresses.push(SocketAddr::new(IpAddr::V6(*ipv6), 0));
865 }
866
867 NetworkInterface {
868 name: linux_interface.name.clone(),
869 addresses,
870 is_up: linux_interface.state == InterfaceState::Up,
871 is_wireless: linux_interface.flags.is_wireless,
872 mtu: Some(linux_interface.mtu as u16),
873 }
874 }
875
876 fn update_cache(&mut self, interfaces: Vec<LinuxInterface>) {
878 self.cached_interfaces.clear();
879 for interface in interfaces {
880 self.cached_interfaces.insert(interface.index, interface);
881 }
882 self.last_scan_time = Some(Instant::now());
883 }
884
885 fn is_cache_valid(&self) -> bool {
887 if let Some(last_scan) = self.last_scan_time {
888 last_scan.elapsed() < self.cache_ttl
889 } else {
890 false
891 }
892 }
893}
894
895impl NetworkInterfaceDiscovery for LinuxInterfaceDiscovery {
896 fn start_scan(&mut self) -> Result<(), String> {
897 debug!("Starting Linux network interface scan");
898
899 if self.interface_config.enable_monitoring {
901 if let Err(e) = self.initialize_netlink_socket() {
902 warn!("Failed to initialize netlink socket: {:?}", e);
903 }
904 }
905
906 if self.is_cache_valid() {
908 if let Ok(changes) = self.check_network_changes() {
909 if !changes {
910 debug!("Using cached interface data");
911 let interfaces: Vec<NetworkInterface> = self.cached_interfaces
912 .values()
913 .map(|li| self.convert_to_network_interface(li))
914 .collect();
915
916 self.scan_state = ScanState::Completed {
917 scan_results: interfaces,
918 };
919 return Ok(());
920 }
921 }
922 }
923
924 self.scan_state = ScanState::InProgress {
926 started_at: Instant::now(),
927 };
928
929 match self.enumerate_interfaces() {
930 Ok(interfaces) => {
931 debug!("Successfully enumerated {} interfaces", interfaces.len());
932
933 let network_interfaces: Vec<NetworkInterface> = interfaces
935 .iter()
936 .map(|li| self.convert_to_network_interface(li))
937 .collect();
938
939 self.update_cache(interfaces);
941
942 self.scan_state = ScanState::Completed {
943 scan_results: network_interfaces,
944 };
945
946 info!("Network interface scan completed successfully");
947 Ok(())
948 }
949 Err(e) => {
950 let error_msg = format!("Linux interface enumeration failed: {:?}", e);
951 error!("{}", error_msg);
952 self.scan_state = ScanState::Failed {
953 error: error_msg.clone(),
954 };
955 Err(error_msg)
956 }
957 }
958 }
959
960 fn check_scan_complete(&mut self) -> Option<Vec<NetworkInterface>> {
961 match &self.scan_state {
962 ScanState::Completed { scan_results } => {
963 let results = scan_results.clone();
964 self.scan_state = ScanState::Idle;
965 Some(results)
966 }
967 ScanState::Failed { error } => {
968 warn!("Scan failed: {}", error);
969 self.scan_state = ScanState::Idle;
970 None
971 }
972 _ => None,
973 }
974 }
975}
976
977impl Drop for LinuxInterfaceDiscovery {
978 fn drop(&mut self) {
979 if let Some(socket) = self.netlink_socket.take() {
981 unsafe {
982 libc::close(socket.socket_fd);
983 }
984 }
985 }
986}
987
988impl std::fmt::Display for LinuxNetworkError {
989 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
990 match self {
991 Self::SocketCreationFailed { error } => {
992 write!(f, "Socket creation failed: {}", error)
993 }
994 Self::SocketBindFailed { error } => {
995 write!(f, "Socket bind failed: {}", error)
996 }
997 Self::MessageSendFailed { error } => {
998 write!(f, "Message send failed: {}", error)
999 }
1000 Self::MessageReceiveFailed { error } => {
1001 write!(f, "Message receive failed: {}", error)
1002 }
1003 Self::InvalidMessage { message } => {
1004 write!(f, "Invalid message: {}", message)
1005 }
1006 Self::InterfaceNotFound { interface_name } => {
1007 write!(f, "Interface not found: {}", interface_name)
1008 }
1009 Self::PermissionDenied { operation } => {
1010 write!(f, "Permission denied for operation: {}", operation)
1011 }
1012 Self::SystemLimitExceeded { limit_type } => {
1013 write!(f, "System limit exceeded: {}", limit_type)
1014 }
1015 Self::NetworkNamespaceError { error } => {
1016 write!(f, "Network namespace error: {}", error)
1017 }
1018 Self::EnumerationTimeout { timeout } => {
1019 write!(f, "Enumeration timeout: {:?}", timeout)
1020 }
1021 }
1022 }
1023}
1024
1025impl std::error::Error for LinuxNetworkError {}
1026
1027#[cfg(test)]
1028mod tests {
1029 use super::*;
1030
1031 #[test]
1032 fn test_linux_interface_discovery_creation() {
1033 let discovery = LinuxInterfaceDiscovery::new();
1034 assert!(discovery.cached_interfaces.is_empty());
1035 assert!(discovery.last_scan_time.is_none());
1036 }
1037
1038 #[test]
1039 fn test_interface_config() {
1040 let mut discovery = LinuxInterfaceDiscovery::new();
1041 let config = InterfaceConfig {
1042 include_loopback: true,
1043 include_down: true,
1044 include_ipv6: false,
1045 min_mtu: 1000,
1046 max_interfaces: 32,
1047 enable_monitoring: false,
1048 allowed_interface_types: vec![InterfaceType::Ethernet],
1049 };
1050
1051 discovery.set_interface_config(config.clone());
1052 assert_eq!(discovery.interface_config.include_loopback, true);
1053 assert_eq!(discovery.interface_config.min_mtu, 1000);
1054 }
1055
1056 #[test]
1057 fn test_wireless_interface_detection() {
1058 let discovery = LinuxInterfaceDiscovery::new();
1059
1060 assert!(discovery.is_wireless_interface("wlan0"));
1061 assert!(discovery.is_wireless_interface("wl0"));
1062 assert!(!discovery.is_wireless_interface("eth0"));
1063 }
1064
1065 #[test]
1066 fn test_interface_type_determination() {
1067 let discovery = LinuxInterfaceDiscovery::new();
1068 let flags = InterfaceFlags::default();
1069
1070 assert_eq!(
1071 discovery.determine_interface_type("eth0", &flags).unwrap(),
1072 InterfaceType::Ethernet
1073 );
1074 assert_eq!(
1075 discovery.determine_interface_type("wlan0", &flags).unwrap(),
1076 InterfaceType::Wireless
1077 );
1078 assert_eq!(
1079 discovery.determine_interface_type("tun0", &flags).unwrap(),
1080 InterfaceType::Tunnel
1081 );
1082 }
1083
1084 #[test]
1085 fn test_cache_validation() {
1086 let mut discovery = LinuxInterfaceDiscovery::new();
1087
1088 assert!(!discovery.is_cache_valid());
1090
1091 discovery.last_scan_time = Some(Instant::now());
1093 assert!(discovery.is_cache_valid());
1094
1095 discovery.last_scan_time = Some(Instant::now() - std::time::Duration::from_secs(60));
1097 assert!(!discovery.is_cache_valid());
1098 }
1099}