From 2d9c9a06482ff40b759501ba53c1caf73b80e097 Mon Sep 17 00:00:00 2001 From: shu-kitamura Date: Sun, 16 Mar 2025 00:12:52 +0900 Subject: [PATCH 01/12] Add support ARP(#680) --- src/networking/manage_packets.rs | 45 ++++++++++++++----- src/networking/types/packet_filters_fields.rs | 2 +- src/networking/types/protocol.rs | 6 ++- src/secondary_threads/parse_packets.rs | 1 + 4 files changed, 41 insertions(+), 13 deletions(-) diff --git a/src/networking/manage_packets.rs b/src/networking/manage_packets.rs index 7f2a35f45..aee510f0e 100644 --- a/src/networking/manage_packets.rs +++ b/src/networking/manage_packets.rs @@ -5,7 +5,7 @@ use std::sync::Mutex; use chrono::Local; use dns_lookup::lookup_addr; -use etherparse::{LaxPacketHeaders, LinkHeader, NetHeaders, TransportHeader}; +use etherparse::{EtherType, LaxPacketHeaders, LinkHeader, NetHeaders, TransportHeader}; use pcap::{Address, Device}; use crate::mmdb::asn::get_asn; @@ -46,6 +46,11 @@ pub fn analyze_headers( exchanged_bytes, ); + let is_arp: bool = match &headers.net { + Some(NetHeaders::Arp(_)) => true, + _ => false, + }; + if !analyze_network_header( headers.net, exchanged_bytes, @@ -56,14 +61,16 @@ pub fn analyze_headers( return None; } - if !analyze_transport_header( - headers.transport, - &mut packet_filters_fields.sport, - &mut packet_filters_fields.dport, - &mut packet_filters_fields.protocol, - icmp_type, - ) { - return None; + if !is_arp { + if !analyze_transport_header( + headers.transport, + &mut packet_filters_fields.sport, + &mut packet_filters_fields.dport, + &mut packet_filters_fields.protocol, + icmp_type, + ) { + return None; + } } Some(AddressPortPair::new( @@ -119,6 +126,20 @@ fn analyze_network_header( *exchanged_bytes += u128::from(40 + ipv6header.payload_length); true } + Some(NetHeaders::Arp(arppacket)) => { + println!("Arp"); + *network_protocol = match arppacket.proto_addr_type { + EtherType::IPV4 => IpVersion::IPv4, + EtherType::IPV6 => IpVersion::IPv6, + _ => return false, + }; + let src: [u8; 4] = arppacket.sender_protocol_addr().try_into().unwrap(); + let dst: [u8; 4] = arppacket.target_protocol_addr().try_into().unwrap(); + *address1 = IpAddr::from(src); + *address2 = IpAddr::from(dst); + *exchanged_bytes += arppacket.packet_len() as u128; + true + } _ => false, } } @@ -133,6 +154,7 @@ fn analyze_transport_header( protocol: &mut Protocol, icmp_type: &mut IcmpType, ) -> bool { + println!("{:?}", transport_header); match transport_header { Some(TransportHeader::Udp(udp_header)) => { *port1 = Some(udp_header.source_port); @@ -1240,6 +1262,7 @@ mod tests { Protocol::TCP => "mdns", Protocol::UDP => "zeroconf", Protocol::ICMP => panic!(), + Protocol::ARP => panic!(), }) ); @@ -1250,7 +1273,7 @@ mod tests { Protocol::TCP => Service::Name("netstat"), Protocol::UDP => Service::Unknown, Protocol::ICMP => panic!(), - } + Protocol::ARP => panic!(), } ); let key = @@ -1261,7 +1284,7 @@ mod tests { Protocol::TCP => Service::Unknown, Protocol::UDP => Service::Name("murmur"), Protocol::ICMP => panic!(), - } + Protocol::ARP => panic!(), } ); for (p1, p2) in [(Some(5353), Some(53)), (Some(53), Some(5353))] { diff --git a/src/networking/types/packet_filters_fields.rs b/src/networking/types/packet_filters_fields.rs index 268989f9e..2767160c8 100644 --- a/src/networking/types/packet_filters_fields.rs +++ b/src/networking/types/packet_filters_fields.rs @@ -24,7 +24,7 @@ impl Default for PacketFiltersFields { fn default() -> Self { Self { ip_version: IpVersion::IPv4, - protocol: Protocol::TCP, + protocol: Protocol::ARP, source: IpAddr::from_str("::").unwrap(), dest: IpAddr::from_str("::").unwrap(), sport: None, diff --git a/src/networking/types/protocol.rs b/src/networking/types/protocol.rs index 40ac658e8..6b83f7fc3 100644 --- a/src/networking/types/protocol.rs +++ b/src/networking/types/protocol.rs @@ -10,6 +10,8 @@ pub enum Protocol { UDP, /// Internet Control Message Protocol ICMP, + /// Address Resolution Protocol + ARP, } impl std::fmt::Display for Protocol { @@ -19,7 +21,7 @@ impl std::fmt::Display for Protocol { } impl Protocol { - pub const ALL: [Protocol; 3] = [Protocol::TCP, Protocol::UDP, Protocol::ICMP]; + pub const ALL: [Protocol; 4] = [Protocol::TCP, Protocol::UDP, Protocol::ICMP, Protocol::ARP]; } #[cfg(test)] @@ -33,6 +35,7 @@ mod tests { Protocol::TCP => assert_eq!(protocol.to_string(), "TCP"), Protocol::UDP => assert_eq!(protocol.to_string(), "UDP"), Protocol::ICMP => assert_eq!(protocol.to_string(), "ICMP"), + Protocol::ARP => assert_eq!(protocol.to_string(), "ARP"), } } } @@ -43,5 +46,6 @@ mod tests { assert_eq!(Protocol::ALL.get(0).unwrap(), &Protocol::TCP); assert_eq!(Protocol::ALL.get(1).unwrap(), &Protocol::UDP); assert_eq!(Protocol::ALL.get(2).unwrap(), &Protocol::ICMP); + assert_eq!(Protocol::ALL.get(3).unwrap(), &Protocol::ARP); } } diff --git a/src/secondary_threads/parse_packets.rs b/src/secondary_threads/parse_packets.rs index 41a215ca7..2c7ab65b8 100644 --- a/src/secondary_threads/parse_packets.rs +++ b/src/secondary_threads/parse_packets.rs @@ -65,6 +65,7 @@ pub fn parse_packets( &mut icmp_type, &mut packet_filters_fields, ); + println!("{:?}", key_option); if key_option.is_none() { continue; } From 82ae78abb958197b30ca7d4401ebc5685ee00c8d Mon Sep 17 00:00:00 2001 From: shu-kitamura Date: Sun, 16 Mar 2025 00:21:35 +0900 Subject: [PATCH 02/12] remove 'println()' for debugging --- src/networking/manage_packets.rs | 1 - src/secondary_threads/parse_packets.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/networking/manage_packets.rs b/src/networking/manage_packets.rs index aee510f0e..0468abe6b 100644 --- a/src/networking/manage_packets.rs +++ b/src/networking/manage_packets.rs @@ -127,7 +127,6 @@ fn analyze_network_header( true } Some(NetHeaders::Arp(arppacket)) => { - println!("Arp"); *network_protocol = match arppacket.proto_addr_type { EtherType::IPV4 => IpVersion::IPv4, EtherType::IPV6 => IpVersion::IPv6, diff --git a/src/secondary_threads/parse_packets.rs b/src/secondary_threads/parse_packets.rs index 2c7ab65b8..1dbc240fc 100644 --- a/src/secondary_threads/parse_packets.rs +++ b/src/secondary_threads/parse_packets.rs @@ -65,7 +65,7 @@ pub fn parse_packets( &mut icmp_type, &mut packet_filters_fields, ); - println!("{:?}", key_option); + if key_option.is_none() { continue; } From 8130e4f298c5c86f6f7be814670db6f92b0af8e7 Mon Sep 17 00:00:00 2001 From: shu-kitamura Date: Sun, 16 Mar 2025 00:27:55 +0900 Subject: [PATCH 03/12] remove 'println()' for debugging --- src/networking/manage_packets.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/networking/manage_packets.rs b/src/networking/manage_packets.rs index 0468abe6b..8bc284022 100644 --- a/src/networking/manage_packets.rs +++ b/src/networking/manage_packets.rs @@ -153,7 +153,6 @@ fn analyze_transport_header( protocol: &mut Protocol, icmp_type: &mut IcmpType, ) -> bool { - println!("{:?}", transport_header); match transport_header { Some(TransportHeader::Udp(udp_header)) => { *port1 = Some(udp_header.source_port); From 2c99a3c85f4e84ef34f99d0f6ee3cc961839ab50 Mon Sep 17 00:00:00 2001 From: shu-kitamura Date: Sun, 16 Mar 2025 09:54:26 +0900 Subject: [PATCH 04/12] Add arp_type.rs --- src/networking/types/arp_type.rs | 34 ++++++++++++++++++++++++++++++++ src/networking/types/mod.rs | 1 + 2 files changed, 35 insertions(+) create mode 100644 src/networking/types/arp_type.rs diff --git a/src/networking/types/arp_type.rs b/src/networking/types/arp_type.rs new file mode 100644 index 000000000..69d1a4aa3 --- /dev/null +++ b/src/networking/types/arp_type.rs @@ -0,0 +1,34 @@ +use std::fmt::{Display, Formatter}; + +use etherparse::ArpOperation; + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Default)] +#[allow(clippy::module_name_repetitions)] +pub enum ArpType { + Request, + Reply, + #[default] + Unknown, +} + +impl ArpType { + pub fn from_etherparse(arp_operation: &ArpOperation) -> ArpType { + match arp_operation { + ArpOperation(1) => Self::Request, + ArpOperation(2) => Self::Reply, + ArpOperation(_) => Self::Unknown, + } + } +} + +impl Display for ArpType { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, "{}", match self { + ArpType::Request => "Request", + ArpType::Reply => "Reply", + ArpType::Unknown => "?", + } + ) + } +} \ No newline at end of file diff --git a/src/networking/types/mod.rs b/src/networking/types/mod.rs index 93c87318a..8553faafb 100644 --- a/src/networking/types/mod.rs +++ b/src/networking/types/mod.rs @@ -1,4 +1,5 @@ pub mod address_port_pair; +pub mod arp_type; pub mod asn; pub mod bogon; pub mod byte_multiple; From 8c9858d1cdc100b6afc4ec857ef9c81b7643c463 Mon Sep 17 00:00:00 2001 From: shu-kitamura Date: Sun, 16 Mar 2025 11:16:26 +0900 Subject: [PATCH 05/12] Show Arp operation types in GUI --- src/gui/pages/connection_details_page.rs | 13 ++++++--- src/networking/manage_packets.rs | 27 +++++++++++++++---- src/networking/types/arp_type.rs | 15 ++++++++++- .../types/info_address_port_pair.rs | 3 +++ src/secondary_threads/parse_packets.rs | 4 +++ 5 files changed, 53 insertions(+), 9 deletions(-) diff --git a/src/gui/pages/connection_details_page.rs b/src/gui/pages/connection_details_page.rs index 840011dda..2e2a4d9ae 100644 --- a/src/gui/pages/connection_details_page.rs +++ b/src/gui/pages/connection_details_page.rs @@ -19,6 +19,7 @@ use crate::networking::manage_packets::{ get_address_to_lookup, get_traffic_type, is_local_connection, is_my_address, }; use crate::networking::types::address_port_pair::AddressPortPair; +use crate::networking::types::arp_type::ArpType; use crate::networking::types::bogon::is_bogon; use crate::networking::types::host::Host; use crate::networking::types::icmp_type::IcmpType; @@ -185,6 +186,7 @@ fn col_info<'a>( language: Language, ) -> Column<'a, Message, StyleType> { let is_icmp = key.protocol.eq(&Protocol::ICMP); + let is_arp = key.protocol.eq(&Protocol::ARP); let mut ret_val = Column::new() .spacing(10) @@ -207,7 +209,7 @@ fn col_info<'a>( font, )); - if !is_icmp { + if !is_icmp || !is_arp { ret_val = ret_val.push(TextType::highlighted_subtitle_with_desc( service_translation(language), &val.service.to_string(), @@ -234,7 +236,7 @@ fn col_info<'a>( font, )); - if is_icmp { + if is_icmp || is_arp { ret_val = ret_val.push( Column::new() .push( @@ -245,7 +247,12 @@ fn col_info<'a>( .push(Scrollable::with_direction( Column::new() .padding(Padding::ZERO.right(10).bottom(10)) - .push(Text::new(IcmpType::pretty_print_types(&val.icmp_types)).font(font)), + .push(Text::new(if is_icmp { + IcmpType::pretty_print_types(&val.icmp_types) + } else { + ArpType::pretty_print_types(&val.arp_types) + } + ).font(font)), Direction::Both { vertical: ScrollbarType::properties(), horizontal: ScrollbarType::properties(), diff --git a/src/networking/manage_packets.rs b/src/networking/manage_packets.rs index 8bc284022..199b9a0de 100644 --- a/src/networking/manage_packets.rs +++ b/src/networking/manage_packets.rs @@ -12,6 +12,7 @@ use crate::mmdb::asn::get_asn; use crate::mmdb::country::get_country; use crate::mmdb::types::mmdb_reader::MmdbReaders; use crate::networking::types::address_port_pair::AddressPortPair; +use crate::networking::types::arp_type::ArpType; use crate::networking::types::bogon::is_bogon; use crate::networking::types::data_info_host::DataInfoHost; use crate::networking::types::host::Host; @@ -37,6 +38,7 @@ pub fn analyze_headers( mac_addresses: &mut (Option, Option), exchanged_bytes: &mut u128, icmp_type: &mut IcmpType, + arp_type: &mut ArpType, packet_filters_fields: &mut PacketFiltersFields, ) -> Option { analyze_link_header( @@ -57,6 +59,7 @@ pub fn analyze_headers( &mut packet_filters_fields.ip_version, &mut packet_filters_fields.source, &mut packet_filters_fields.dest, + arp_type, ) { return None; } @@ -110,6 +113,7 @@ fn analyze_network_header( network_protocol: &mut IpVersion, address1: &mut IpAddr, address2: &mut IpAddr, + arp_type: &mut ArpType ) -> bool { match network_header { Some(NetHeaders::Ipv4(ipv4header, _)) => { @@ -126,17 +130,18 @@ fn analyze_network_header( *exchanged_bytes += u128::from(40 + ipv6header.payload_length); true } - Some(NetHeaders::Arp(arppacket)) => { - *network_protocol = match arppacket.proto_addr_type { + Some(NetHeaders::Arp(arp_packet)) => { + *network_protocol = match arp_packet.proto_addr_type { EtherType::IPV4 => IpVersion::IPv4, EtherType::IPV6 => IpVersion::IPv6, _ => return false, }; - let src: [u8; 4] = arppacket.sender_protocol_addr().try_into().unwrap(); - let dst: [u8; 4] = arppacket.target_protocol_addr().try_into().unwrap(); + let src: [u8; 4] = arp_packet.sender_protocol_addr().try_into().unwrap(); + let dst: [u8; 4] = arp_packet.target_protocol_addr().try_into().unwrap(); *address1 = IpAddr::from(src); *address2 = IpAddr::from(dst); - *exchanged_bytes += arppacket.packet_len() as u128; + *exchanged_bytes += arp_packet.packet_len() as u128; + *arp_type = ArpType::from_etherparse(&arp_packet.operation); true } _ => false, @@ -237,6 +242,7 @@ pub fn modify_or_insert_in_map( my_device: &MyDevice, mac_addresses: (Option, Option), icmp_type: IcmpType, + arp_type: ArpType, exchanged_bytes: u128, ) -> InfoAddressPortPair { let now = Local::now(); @@ -288,6 +294,12 @@ pub fn modify_or_insert_in_map( .and_modify(|n| *n += 1) .or_insert(1); } + if key.protocol.eq(&Protocol::ARP) { + info.arp_types + .entry(arp_type) + .and_modify(|n| *n += 1) + .or_insert(1); + } }) .or_insert_with(|| InfoAddressPortPair { mac_address1: mac_addresses.0, @@ -303,6 +315,11 @@ pub fn modify_or_insert_in_map( } else { HashMap::new() }, + arp_types: if key.protocol.eq(&Protocol::ARP) { + HashMap::from([(arp_type, 1)]) + } else { + HashMap::new() + }, }) .clone(); diff --git a/src/networking/types/arp_type.rs b/src/networking/types/arp_type.rs index 69d1a4aa3..f5814c47b 100644 --- a/src/networking/types/arp_type.rs +++ b/src/networking/types/arp_type.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use std::fmt::{Display, Formatter}; use etherparse::ArpOperation; @@ -19,6 +20,18 @@ impl ArpType { ArpOperation(_) => Self::Unknown, } } + + pub fn pretty_print_types(map: &HashMap) -> String { + let mut ret_val = String::new(); + + let mut vec: Vec<(&ArpType, &usize)> = map.iter().collect(); + vec.sort_by(|(_, a), (_, b)| b.cmp(a)); + + for (arp_type, n) in vec { + ret_val.push_str(&format!(" {arp_type} ({n})\n")); + } + ret_val + } } impl Display for ArpType { @@ -31,4 +44,4 @@ impl Display for ArpType { } ) } -} \ No newline at end of file +} diff --git a/src/networking/types/info_address_port_pair.rs b/src/networking/types/info_address_port_pair.rs index 44e8febfd..680987b60 100644 --- a/src/networking/types/info_address_port_pair.rs +++ b/src/networking/types/info_address_port_pair.rs @@ -6,6 +6,7 @@ use std::collections::HashMap; use chrono::{DateTime, Local}; use crate::networking::types::icmp_type::IcmpType; +use crate::networking::types::arp_type::ArpType; use crate::networking::types::traffic_direction::TrafficDirection; use crate::Service; @@ -32,4 +33,6 @@ pub struct InfoAddressPortPair { pub traffic_direction: TrafficDirection, /// Types of the ICMP messages exchanged, with the relative count (this is empty if not ICMP) pub icmp_types: HashMap, + /// Types of the ARP operations, with the relative count (this is empty if not ARP) + pub arp_types: HashMap } diff --git a/src/secondary_threads/parse_packets.rs b/src/secondary_threads/parse_packets.rs index 1dbc240fc..e695e39df 100644 --- a/src/secondary_threads/parse_packets.rs +++ b/src/secondary_threads/parse_packets.rs @@ -13,6 +13,7 @@ use crate::mmdb::types::mmdb_reader::MmdbReaders; use crate::networking::manage_packets::{ analyze_headers, get_address_to_lookup, modify_or_insert_in_map, reverse_dns_lookup, }; +use crate::networking::types::arp_type::ArpType; use crate::networking::types::capture_context::CaptureContext; use crate::networking::types::data_info::DataInfo; use crate::networking::types::filters::Filters; @@ -56,6 +57,7 @@ pub fn parse_packets( let mut exchanged_bytes = 0; let mut mac_addresses = (None, None); let mut icmp_type = IcmpType::default(); + let mut arp_type = ArpType::default(); let mut packet_filters_fields = PacketFiltersFields::default(); let key_option = analyze_headers( @@ -63,6 +65,7 @@ pub fn parse_packets( &mut mac_addresses, &mut exchanged_bytes, &mut icmp_type, + &mut arp_type, &mut packet_filters_fields, ); @@ -86,6 +89,7 @@ pub fn parse_packets( device, mac_addresses, icmp_type, + arp_type, exchanged_bytes, ); } From 5ae2d08ab0b6476127d6aa02d4ec91e0b049a2f7 Mon Sep 17 00:00:00 2001 From: shu-kitamura Date: Sun, 16 Mar 2025 11:27:05 +0900 Subject: [PATCH 06/12] Fixed a typo in the line break --- src/networking/manage_packets.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/networking/manage_packets.rs b/src/networking/manage_packets.rs index 199b9a0de..7cd18cc3f 100644 --- a/src/networking/manage_packets.rs +++ b/src/networking/manage_packets.rs @@ -1288,7 +1288,8 @@ mod tests { Protocol::TCP => Service::Name("netstat"), Protocol::UDP => Service::Unknown, Protocol::ICMP => panic!(), - Protocol::ARP => panic!(), } + Protocol::ARP => panic!(), + } ); let key = @@ -1299,7 +1300,8 @@ mod tests { Protocol::TCP => Service::Unknown, Protocol::UDP => Service::Name("murmur"), Protocol::ICMP => panic!(), - Protocol::ARP => panic!(), } + Protocol::ARP => panic!(), + } ); for (p1, p2) in [(Some(5353), Some(53)), (Some(53), Some(5353))] { From 6b3687eb9092060d751e19c89b17c843c454d151 Mon Sep 17 00:00:00 2001 From: shu-kitamura Date: Sun, 16 Mar 2025 11:33:43 +0900 Subject: [PATCH 07/12] Fixed test code for ARP --- src/gui/sniffer.rs | 6 ++++-- src/networking/types/protocol.rs | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/gui/sniffer.rs b/src/gui/sniffer.rs index 62d41b273..078d4c0cc 100644 --- a/src/gui/sniffer.rs +++ b/src/gui/sniffer.rs @@ -1056,11 +1056,13 @@ mod tests { sniffer.update(Message::ProtocolSelection(Protocol::UDP, false)); assert_eq!( sniffer.filters.protocols, - HashSet::from([Protocol::TCP, Protocol::ICMP]) + HashSet::from([Protocol::TCP, Protocol::ICMP, Protocol::ARP]) ); sniffer.update(Message::ProtocolSelection(Protocol::TCP, false)); - assert_eq!(sniffer.filters.protocols, HashSet::from([Protocol::ICMP])); + assert_eq!(sniffer.filters.protocols, HashSet::from([Protocol::ICMP, Protocol::ARP])); sniffer.update(Message::ProtocolSelection(Protocol::ICMP, false)); + assert_eq!(sniffer.filters.protocols, HashSet::from([Protocol::ARP])); + sniffer.update(Message::ProtocolSelection(Protocol::ARP, false)); assert_eq!(sniffer.filters.protocols, HashSet::new()); sniffer.update(Message::ProtocolSelection(Protocol::UDP, true)); assert_eq!(sniffer.filters.protocols, HashSet::from([Protocol::UDP])); diff --git a/src/networking/types/protocol.rs b/src/networking/types/protocol.rs index 6b83f7fc3..31f20e625 100644 --- a/src/networking/types/protocol.rs +++ b/src/networking/types/protocol.rs @@ -42,7 +42,7 @@ mod tests { #[test] fn test_all_protocols_collection() { - assert_eq!(Protocol::ALL.len(), 3); + assert_eq!(Protocol::ALL.len(), 4); assert_eq!(Protocol::ALL.get(0).unwrap(), &Protocol::TCP); assert_eq!(Protocol::ALL.get(1).unwrap(), &Protocol::UDP); assert_eq!(Protocol::ALL.get(2).unwrap(), &Protocol::ICMP); From 35d42d0d531af3f33e735b91c617795d8d6d666d Mon Sep 17 00:00:00 2001 From: shu-kitamura Date: Sun, 16 Mar 2025 22:39:11 +0900 Subject: [PATCH 08/12] Modified following review about IPv6 --- src/networking/manage_packets.rs | 34 +++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/src/networking/manage_packets.rs b/src/networking/manage_packets.rs index 7cd18cc3f..805d16dd2 100644 --- a/src/networking/manage_packets.rs +++ b/src/networking/manage_packets.rs @@ -131,15 +131,31 @@ fn analyze_network_header( true } Some(NetHeaders::Arp(arp_packet)) => { - *network_protocol = match arp_packet.proto_addr_type { - EtherType::IPV4 => IpVersion::IPv4, - EtherType::IPV6 => IpVersion::IPv6, - _ => return false, - }; - let src: [u8; 4] = arp_packet.sender_protocol_addr().try_into().unwrap(); - let dst: [u8; 4] = arp_packet.target_protocol_addr().try_into().unwrap(); - *address1 = IpAddr::from(src); - *address2 = IpAddr::from(dst); + match arp_packet.proto_addr_type { + EtherType::IPV4 => { + *network_protocol = IpVersion::IPv4; + *address1 = match TryInto::<[u8; 4]>::try_into(arp_packet.sender_protocol_addr()) { + Ok(source) => IpAddr::from(source), + Err(_) => return false + }; + *address2 = match TryInto::<[u8; 4]>::try_into(arp_packet.target_protocol_addr()) { + Ok(destination) => IpAddr::from(destination), + Err(_) => return false + }; + } + EtherType::IPV6 => { + *network_protocol = IpVersion::IPv6; + *address1 = match TryInto::<[u8; 16]>::try_into(arp_packet.sender_protocol_addr()) { + Ok(source) => IpAddr::from(source), + Err(_) => return false + }; + *address2 = match TryInto::<[u8; 16]>::try_into(arp_packet.target_protocol_addr()) { + Ok(destination) => IpAddr::from(destination), + Err(_) => return false + }; + } + _ => return false + } *exchanged_bytes += arp_packet.packet_len() as u128; *arp_type = ArpType::from_etherparse(&arp_packet.operation); true From 7e9a48c48805a5124d844f786a5a2928396a1fb7 Mon Sep 17 00:00:00 2001 From: GyulyVGC Date: Sat, 22 Mar 2025 23:01:18 +0100 Subject: [PATCH 09/12] fixes and improvements to ARP implementation --- src/gui/pages/connection_details_page.rs | 2 +- src/networking/manage_packets.rs | 11 ++++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/gui/pages/connection_details_page.rs b/src/gui/pages/connection_details_page.rs index 2e2a4d9ae..47f69d500 100644 --- a/src/gui/pages/connection_details_page.rs +++ b/src/gui/pages/connection_details_page.rs @@ -209,7 +209,7 @@ fn col_info<'a>( font, )); - if !is_icmp || !is_arp { + if !is_icmp && !is_arp { ret_val = ret_val.push(TextType::highlighted_subtitle_with_desc( service_translation(language), &val.service.to_string(), diff --git a/src/networking/manage_packets.rs b/src/networking/manage_packets.rs index 805d16dd2..cfc34738f 100644 --- a/src/networking/manage_packets.rs +++ b/src/networking/manage_packets.rs @@ -206,7 +206,7 @@ fn analyze_transport_header( } pub fn get_service(key: &AddressPortPair, traffic_direction: TrafficDirection) -> Service { - if key.port1.is_none() || key.port2.is_none() || key.protocol == Protocol::ICMP { + if key.port1.is_none() || key.port2.is_none() || key.protocol == Protocol::ICMP || key.protocol == Protocol::ARP { return Service::NotApplicable; } @@ -1292,8 +1292,7 @@ mod tests { Service::Name(match p { Protocol::TCP => "mdns", Protocol::UDP => "zeroconf", - Protocol::ICMP => panic!(), - Protocol::ARP => panic!(), + _ => panic!(), }) ); @@ -1303,8 +1302,7 @@ mod tests { match p { Protocol::TCP => Service::Name("netstat"), Protocol::UDP => Service::Unknown, - Protocol::ICMP => panic!(), - Protocol::ARP => panic!(), + _ => panic!(), } ); @@ -1315,8 +1313,7 @@ mod tests { match p { Protocol::TCP => Service::Unknown, Protocol::UDP => Service::Name("murmur"), - Protocol::ICMP => panic!(), - Protocol::ARP => panic!(), + _ => panic!(), } ); From 90df310a6f0fb714c75866190bf6460d3fe28292 Mon Sep 17 00:00:00 2001 From: GyulyVGC Date: Sat, 22 Mar 2025 23:01:35 +0100 Subject: [PATCH 10/12] fmt'd --- src/gui/pages/connection_details_page.rs | 14 +++--- src/gui/sniffer.rs | 5 +- src/networking/manage_packets.rs | 46 +++++++++++-------- src/networking/types/arp_type.rs | 22 +++++---- .../types/info_address_port_pair.rs | 4 +- 5 files changed, 53 insertions(+), 38 deletions(-) diff --git a/src/gui/pages/connection_details_page.rs b/src/gui/pages/connection_details_page.rs index 47f69d500..834e7ace8 100644 --- a/src/gui/pages/connection_details_page.rs +++ b/src/gui/pages/connection_details_page.rs @@ -247,12 +247,14 @@ fn col_info<'a>( .push(Scrollable::with_direction( Column::new() .padding(Padding::ZERO.right(10).bottom(10)) - .push(Text::new(if is_icmp { - IcmpType::pretty_print_types(&val.icmp_types) - } else { - ArpType::pretty_print_types(&val.arp_types) - } - ).font(font)), + .push( + Text::new(if is_icmp { + IcmpType::pretty_print_types(&val.icmp_types) + } else { + ArpType::pretty_print_types(&val.arp_types) + }) + .font(font), + ), Direction::Both { vertical: ScrollbarType::properties(), horizontal: ScrollbarType::properties(), diff --git a/src/gui/sniffer.rs b/src/gui/sniffer.rs index 078d4c0cc..3b0a9dafa 100644 --- a/src/gui/sniffer.rs +++ b/src/gui/sniffer.rs @@ -1059,7 +1059,10 @@ mod tests { HashSet::from([Protocol::TCP, Protocol::ICMP, Protocol::ARP]) ); sniffer.update(Message::ProtocolSelection(Protocol::TCP, false)); - assert_eq!(sniffer.filters.protocols, HashSet::from([Protocol::ICMP, Protocol::ARP])); + assert_eq!( + sniffer.filters.protocols, + HashSet::from([Protocol::ICMP, Protocol::ARP]) + ); sniffer.update(Message::ProtocolSelection(Protocol::ICMP, false)); assert_eq!(sniffer.filters.protocols, HashSet::from([Protocol::ARP])); sniffer.update(Message::ProtocolSelection(Protocol::ARP, false)); diff --git a/src/networking/manage_packets.rs b/src/networking/manage_packets.rs index cfc34738f..5e22a0b0b 100644 --- a/src/networking/manage_packets.rs +++ b/src/networking/manage_packets.rs @@ -113,7 +113,7 @@ fn analyze_network_header( network_protocol: &mut IpVersion, address1: &mut IpAddr, address2: &mut IpAddr, - arp_type: &mut ArpType + arp_type: &mut ArpType, ) -> bool { match network_header { Some(NetHeaders::Ipv4(ipv4header, _)) => { @@ -134,27 +134,31 @@ fn analyze_network_header( match arp_packet.proto_addr_type { EtherType::IPV4 => { *network_protocol = IpVersion::IPv4; - *address1 = match TryInto::<[u8; 4]>::try_into(arp_packet.sender_protocol_addr()) { - Ok(source) => IpAddr::from(source), - Err(_) => return false - }; - *address2 = match TryInto::<[u8; 4]>::try_into(arp_packet.target_protocol_addr()) { - Ok(destination) => IpAddr::from(destination), - Err(_) => return false - }; + *address1 = + match TryInto::<[u8; 4]>::try_into(arp_packet.sender_protocol_addr()) { + Ok(source) => IpAddr::from(source), + Err(_) => return false, + }; + *address2 = + match TryInto::<[u8; 4]>::try_into(arp_packet.target_protocol_addr()) { + Ok(destination) => IpAddr::from(destination), + Err(_) => return false, + }; } EtherType::IPV6 => { *network_protocol = IpVersion::IPv6; - *address1 = match TryInto::<[u8; 16]>::try_into(arp_packet.sender_protocol_addr()) { - Ok(source) => IpAddr::from(source), - Err(_) => return false - }; - *address2 = match TryInto::<[u8; 16]>::try_into(arp_packet.target_protocol_addr()) { - Ok(destination) => IpAddr::from(destination), - Err(_) => return false - }; + *address1 = + match TryInto::<[u8; 16]>::try_into(arp_packet.sender_protocol_addr()) { + Ok(source) => IpAddr::from(source), + Err(_) => return false, + }; + *address2 = + match TryInto::<[u8; 16]>::try_into(arp_packet.target_protocol_addr()) { + Ok(destination) => IpAddr::from(destination), + Err(_) => return false, + }; } - _ => return false + _ => return false, } *exchanged_bytes += arp_packet.packet_len() as u128; *arp_type = ArpType::from_etherparse(&arp_packet.operation); @@ -206,7 +210,11 @@ fn analyze_transport_header( } pub fn get_service(key: &AddressPortPair, traffic_direction: TrafficDirection) -> Service { - if key.port1.is_none() || key.port2.is_none() || key.protocol == Protocol::ICMP || key.protocol == Protocol::ARP { + if key.port1.is_none() + || key.port2.is_none() + || key.protocol == Protocol::ICMP + || key.protocol == Protocol::ARP + { return Service::NotApplicable; } diff --git a/src/networking/types/arp_type.rs b/src/networking/types/arp_type.rs index f5814c47b..c2a544b97 100644 --- a/src/networking/types/arp_type.rs +++ b/src/networking/types/arp_type.rs @@ -22,22 +22,24 @@ impl ArpType { } pub fn pretty_print_types(map: &HashMap) -> String { - let mut ret_val = String::new(); - - let mut vec: Vec<(&ArpType, &usize)> = map.iter().collect(); - vec.sort_by(|(_, a), (_, b)| b.cmp(a)); - - for (arp_type, n) in vec { - ret_val.push_str(&format!(" {arp_type} ({n})\n")); - } - ret_val + let mut ret_val = String::new(); + + let mut vec: Vec<(&ArpType, &usize)> = map.iter().collect(); + vec.sort_by(|(_, a), (_, b)| b.cmp(a)); + + for (arp_type, n) in vec { + ret_val.push_str(&format!(" {arp_type} ({n})\n")); + } + ret_val } } impl Display for ArpType { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!( - f, "{}", match self { + f, + "{}", + match self { ArpType::Request => "Request", ArpType::Reply => "Reply", ArpType::Unknown => "?", diff --git a/src/networking/types/info_address_port_pair.rs b/src/networking/types/info_address_port_pair.rs index 680987b60..52b39ce80 100644 --- a/src/networking/types/info_address_port_pair.rs +++ b/src/networking/types/info_address_port_pair.rs @@ -5,8 +5,8 @@ use std::collections::HashMap; use chrono::{DateTime, Local}; -use crate::networking::types::icmp_type::IcmpType; use crate::networking::types::arp_type::ArpType; +use crate::networking::types::icmp_type::IcmpType; use crate::networking::types::traffic_direction::TrafficDirection; use crate::Service; @@ -34,5 +34,5 @@ pub struct InfoAddressPortPair { /// Types of the ICMP messages exchanged, with the relative count (this is empty if not ICMP) pub icmp_types: HashMap, /// Types of the ARP operations, with the relative count (this is empty if not ARP) - pub arp_types: HashMap + pub arp_types: HashMap, } From 3a4e9cfc2312dcdb7a5d8a94a142380b393d9d33 Mon Sep 17 00:00:00 2001 From: GyulyVGC Date: Sat, 22 Mar 2025 23:50:54 +0100 Subject: [PATCH 11/12] fix clippy lints --- src/networking/manage_packets.rs | 17 +++++++---------- src/networking/types/arp_type.rs | 3 +-- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/networking/manage_packets.rs b/src/networking/manage_packets.rs index 5e22a0b0b..ab3399809 100644 --- a/src/networking/manage_packets.rs +++ b/src/networking/manage_packets.rs @@ -48,10 +48,7 @@ pub fn analyze_headers( exchanged_bytes, ); - let is_arp: bool = match &headers.net { - Some(NetHeaders::Arp(_)) => true, - _ => false, - }; + let is_arp = matches!(&headers.net, Some(NetHeaders::Arp(_))); if !analyze_network_header( headers.net, @@ -64,16 +61,16 @@ pub fn analyze_headers( return None; } - if !is_arp { - if !analyze_transport_header( + if !is_arp + && !analyze_transport_header( headers.transport, &mut packet_filters_fields.sport, &mut packet_filters_fields.dport, &mut packet_filters_fields.protocol, icmp_type, - ) { - return None; - } + ) + { + return None; } Some(AddressPortPair::new( @@ -161,7 +158,7 @@ fn analyze_network_header( _ => return false, } *exchanged_bytes += arp_packet.packet_len() as u128; - *arp_type = ArpType::from_etherparse(&arp_packet.operation); + *arp_type = ArpType::from_etherparse(arp_packet.operation); true } _ => false, diff --git a/src/networking/types/arp_type.rs b/src/networking/types/arp_type.rs index c2a544b97..cb8a158c1 100644 --- a/src/networking/types/arp_type.rs +++ b/src/networking/types/arp_type.rs @@ -4,7 +4,6 @@ use std::fmt::{Display, Formatter}; use etherparse::ArpOperation; #[derive(Copy, Clone, PartialEq, Eq, Hash, Default)] -#[allow(clippy::module_name_repetitions)] pub enum ArpType { Request, Reply, @@ -13,7 +12,7 @@ pub enum ArpType { } impl ArpType { - pub fn from_etherparse(arp_operation: &ArpOperation) -> ArpType { + pub fn from_etherparse(arp_operation: ArpOperation) -> ArpType { match arp_operation { ArpOperation(1) => Self::Request, ArpOperation(2) => Self::Reply, From a44b91ad9f70b280a974cceaf66f3ed3d9b46810 Mon Sep 17 00:00:00 2001 From: GyulyVGC Date: Sun, 23 Mar 2025 00:33:04 +0100 Subject: [PATCH 12/12] update CHANGELOG --- CHANGELOG.md | 1 + src/gui/pages/initial_page.rs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1475a6302..679edcd19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All Sniffnet releases with the relative changes are documented in this file. ## [UNRELEASED] +- Added support for ARP protocol ([#759](https://github.com/GyulyVGC/sniffnet/pull/759) — fixes [#680](https://github.com/GyulyVGC/sniffnet/issues/680)) - Identify and tag unassigned/reserved "bogon" IP addresses ([#678](https://github.com/GyulyVGC/sniffnet/pull/678) — fixes [#209](https://github.com/GyulyVGC/sniffnet/issues/209)) - Show data agglomerates in _Inspect_ page table ([#684](https://github.com/GyulyVGC/sniffnet/pull/684) — fixes [#601](https://github.com/GyulyVGC/sniffnet/issues/601)) - Updated some of the existing translations to v1.3: diff --git a/src/gui/pages/initial_page.rs b/src/gui/pages/initial_page.rs index 205156978..6ab574985 100644 --- a/src/gui/pages/initial_page.rs +++ b/src/gui/pages/initial_page.rs @@ -131,7 +131,7 @@ fn col_ip_buttons( .align_y(Alignment::Center) .font(font), ) - .width(90) + .width(80) .height(35) .class(if is_active { ButtonType::BorderedRoundSelected @@ -170,7 +170,7 @@ fn col_protocol_buttons( .align_y(Alignment::Center) .font(font), ) - .width(90) + .width(80) .height(35) .class(if is_active { ButtonType::BorderedRoundSelected