diff --git a/src/main.rs b/src/main.rs index b735aaa..de3b5e2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -52,6 +52,7 @@ pub struct Cli { command: Option, /// Network interface(s) to monitor. Can be specified multiple times. If not specified, monitors all interfaces. + /// Use INTERFACE:native_vlan_id to specify native VLAN for untagged traffic. #[arg(short, long, conflicts_with = "pcap_file")] interface: Vec, diff --git a/src/ptp.rs b/src/ptp.rs index aad6876..1b6f84b 100644 --- a/src/ptp.rs +++ b/src/ptp.rs @@ -739,7 +739,7 @@ pub struct PtpTracker { // Track recent sync/follow-up senders per domain for transmitter-receiver correlation recent_sync_senders: HashMap>, // Track interfaces for determining inbound interface of packets - interfaces: Vec<(String, Option)>, + interfaces: Vec<(String, Option, Option)>, } impl PtpTracker { @@ -1013,7 +1013,7 @@ impl PtpTracker { pub fn get_local_ips(&self) -> Vec { self.interfaces .iter() - .filter_map(|(_, ip)| ip.map(std::net::IpAddr::V4)) + .filter_map(|(_, ip, _)| ip.map(std::net::IpAddr::V4)) .collect() } diff --git a/src/source.rs b/src/source.rs index cf70326..d76aa60 100644 --- a/src/source.rs +++ b/src/source.rs @@ -26,6 +26,8 @@ const GPTP_ETHERTYPE: u16 = 0x88f7; /// gPTP multicast MAC address (IEEE 802.1AS) const GPTP_MULTICAST_MAC: [u8; 6] = [0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e]; +type InterfaceSourceType = (String, Option, Option); + #[derive(Debug, Clone)] pub struct RawPacket { pub timestamp: std::time::SystemTime, @@ -43,7 +45,7 @@ pub struct RawPacket { pub enum PacketSource { Socket { receiver: mpsc::UnboundedReceiver, - interfaces: Vec<(String, Option)>, + interfaces: Vec, _multicast_sockets: Vec, }, Pcap { @@ -77,7 +79,7 @@ impl RawSocketReceiver { } } - pub fn get_interfaces(&self) -> &[(String, Option)] { + pub fn get_interfaces(&self) -> &[InterfaceSourceType] { match &self.source { PacketSource::Socket { interfaces, .. } => interfaces, PacketSource::Pcap { .. } => &[], @@ -106,7 +108,7 @@ fn iface_addrs_by_name(ifname: &str) -> io::Result> { Ok(v4) } -fn get_all_interface_addrs() -> io::Result)>> { +fn get_all_interface_addrs() -> io::Result> { let mut interfaces = Vec::new(); // Get available interfaces using pnet datalink @@ -119,12 +121,13 @@ fn get_all_interface_addrs() -> io::Result)>> { } let interface_name = iface.name.clone(); + let native_vlan_id: Option = None; // Get IPv4 addresses for this interface for ip in &iface.ips { if let IpAddr::V4(ipv4) = ip.ip() { if !ipv4.is_loopback() && is_suitable_interface_name(&interface_name) { - interfaces.push((interface_name.clone(), Some(ipv4))); + interfaces.push((interface_name.clone(), Some(ipv4), native_vlan_id)); break; // Only take first IPv4 address per interface } else { println!("Excluding interface: {} (filtered)", interface_name); @@ -185,7 +188,11 @@ fn join_multicast_group(interface_name: &str, interface_addr: Ipv4Addr) -> Resul Ok(socket) } -fn process_ethernet_packet(packet_data: &[u8], interface_name: &str) -> Option { +fn process_ethernet_packet( + packet_data: &[u8], + interface_name: &str, + native_vlan_id: Option, +) -> Option { let ethernet = EthernetPacket::new(packet_data)?; let mut vlan_id: Option = None; @@ -258,7 +265,7 @@ fn process_ethernet_packet(packet_data: &[u8], interface_name: &str) -> Option Option Option, sender: mpsc::UnboundedSender, _multicast_socket: Socket, ) -> Result<()> { @@ -354,7 +362,8 @@ async fn capture_on_interface( loop { match rx.next() { Ok(packet_data) => { - if let Some(raw_packet) = process_ethernet_packet(packet_data, &interface_name) + if let Some(raw_packet) = + process_ethernet_packet(packet_data, &interface_name, native_vlan_id) && sender.send(raw_packet).is_err() { // Receiver has been dropped, exit the loop @@ -383,8 +392,18 @@ pub async fn create_raw_socket_receiver(ifnames: &[String]) -> Result = ifname.split(":").collect(); + let mut native_vlan_id: Option = None; + let mut ifname_clone = ifname.clone(); + + if parts.len() > 1 { + ifname_clone = parts[0].to_string().clone(); + native_vlan_id = parts[1].parse::().ok(); + } + + let iface_v4 = iface_addrs_by_name(&ifname_clone)?; + + interfaces.push((ifname_clone, iface_v4, native_vlan_id)); } interfaces }; @@ -399,7 +418,11 @@ pub async fn create_raw_socket_receiver(ifnames: &[String]) -> Result>() .join(", ") ); @@ -408,9 +431,10 @@ pub async fn create_raw_socket_receiver(ifnames: &[String]) -> Result Result Result Result match block { Ok(pcap_file::pcapng::Block::EnhancedPacket(epb)) => { let packet_data = epb.data; - if let Some(raw_packet) = process_ethernet_packet(&packet_data, "pcap") { + if let Some(raw_packet) = process_ethernet_packet(&packet_data, "pcap", None) { if last_timestamp.is_none() || raw_packet.timestamp > last_timestamp.unwrap() { @@ -495,7 +524,7 @@ pub async fn create_pcap_receiver(pcap_path: &str) -> Result } Ok(pcap_file::pcapng::Block::SimplePacket(spb)) => { let packet_data = spb.data; - if let Some(raw_packet) = process_ethernet_packet(&packet_data, "pcap") { + if let Some(raw_packet) = process_ethernet_packet(&packet_data, "pcap", None) { if last_timestamp.is_none() || raw_packet.timestamp > last_timestamp.unwrap() { @@ -525,7 +554,7 @@ pub async fn create_pcap_receiver(pcap_path: &str) -> Result match pkt { Ok(packet) => { let packet_data = packet.data; - if let Some(raw_packet) = process_ethernet_packet(&packet_data, "pcap") { + if let Some(raw_packet) = process_ethernet_packet(&packet_data, "pcap", None) { if last_timestamp.is_none() || raw_packet.timestamp > last_timestamp.unwrap() {