From 2543ca7de4ba192ad6533259a59140deda8b1064 Mon Sep 17 00:00:00 2001 From: Maximilian Bosch Date: Wed, 8 Jan 2020 18:13:37 +0100 Subject: [PATCH 1/3] Ensure that layer3 packets are displayed as well When using e.g. WireGuard (a VPN which completely acts on layer3), no packages will be matched as it's attempted to parse those as ethernet (=layer2) packets. This is a problem as all layer3-packets fail to get parsed properly (due to different offsets in the packet, wrong protocols will be determined for instance). This change inherits the basic idea from `` to check if it's possible to parse version info using the IpPacket-parsers and if that fails, the sniffer will fall-back to the ethernet-based approach. --- src/network/sniffer.rs | 96 ++++++++++--------- src/tests/cases/raw_mode.rs | 36 +++++++ .../raw_mode__one_ip_packet_of_traffic.snap | 8 ++ 3 files changed, 97 insertions(+), 43 deletions(-) create mode 100644 src/tests/cases/snapshots/raw_mode__one_ip_packet_of_traffic.snap diff --git a/src/network/sniffer.rs b/src/network/sniffer.rs index e0a5a3962..33c9b8042 100644 --- a/src/network/sniffer.rs +++ b/src/network/sniffer.rs @@ -56,51 +56,61 @@ impl Sniffer { } pub fn next(&mut self) -> Option { let bytes = self.network_frames.next().ok()?; - let packet = EthernetPacket::new(bytes)?; - match packet.get_ethertype() { - EtherType(2048) => { - let ip_packet = Ipv4Packet::new(packet.payload())?; - let (protocol, source_port, destination_port, data_length) = - match ip_packet.get_next_level_protocol() { - IpNextHeaderProtocol(6) => { - let message = TcpPacket::new(ip_packet.payload())?; - ( - Protocol::Tcp, - message.get_source(), - message.get_destination(), - ip_packet.payload().len() as u128, - ) - } - IpNextHeaderProtocol(17) => { - let datagram = UdpPacket::new(ip_packet.payload())?; - ( - Protocol::Udp, - datagram.get_source(), - datagram.get_destination(), - ip_packet.payload().len() as u128, - ) - } - _ => return None, - }; - let interface_name = self.network_interface.name.clone(); - let direction = Direction::new(&self.network_interface.ips, &ip_packet); - let from = SocketAddr::new(IpAddr::V4(ip_packet.get_source()), source_port); - let to = SocketAddr::new(IpAddr::V4(ip_packet.get_destination()), destination_port); + let ip_packet = Ipv4Packet::new(&bytes)?; + let version = ip_packet.get_version(); - let connection = match direction { - Direction::Download => { - Connection::new(from, to.ip(), destination_port, protocol)? - } - Direction::Upload => Connection::new(to, from.ip(), source_port, protocol)?, - }; - Some(Segment { - interface_name, - connection, - data_length, - direction, - }) + match version { + 4 => Self::handle_v4(ip_packet, &self.network_interface), + 6 => None, // FIXME v6 support! + _ => { + let pkg = EthernetPacket::new(bytes)?; + match pkg.get_ethertype() { + EtherType(2048) => Self::handle_v4(Ipv4Packet::new(pkg.payload())?, &self.network_interface), + _ => None, + } } - _ => None, } } + fn handle_v4(ip_packet: Ipv4Packet, network_interface: &NetworkInterface) -> Option { + let (protocol, source_port, destination_port, data_length) = + match ip_packet.get_next_level_protocol() { + IpNextHeaderProtocol(6) => { + let message = TcpPacket::new(ip_packet.payload())?; + ( + Protocol::Tcp, + message.get_source(), + message.get_destination(), + ip_packet.payload().len() as u128, + ) + } + IpNextHeaderProtocol(17) => { + let datagram = UdpPacket::new(ip_packet.payload())?; + ( + Protocol::Udp, + datagram.get_source(), + datagram.get_destination(), + ip_packet.payload().len() as u128, + ) + } + _ => return None, + }; + + let interface_name = network_interface.name.clone(); + let direction = Direction::new(&network_interface.ips, &ip_packet); + let from = SocketAddr::new(IpAddr::V4(ip_packet.get_source()), source_port); + let to = SocketAddr::new(IpAddr::V4(ip_packet.get_destination()), destination_port); + + let connection = match direction { + Direction::Download => { + Connection::new(from, to.ip(), destination_port, protocol)? + }, + Direction::Upload => Connection::new(to, from.ip(), source_port, protocol)?, + }; + Some(Segment { + interface_name, + connection, + data_length, + direction, + }) + } } diff --git a/src/tests/cases/raw_mode.rs b/src/tests/cases/raw_mode.rs index 5353fd033..054f37ba2 100644 --- a/src/tests/cases/raw_mode.rs +++ b/src/tests/cases/raw_mode.rs @@ -36,6 +36,23 @@ fn build_tcp_packet( pkt.packet().to_vec() } +fn build_ip_tcp_packet( + source_ip: &str, + destination_ip: &str, + source_port: u16, + destination_port: u16, + payload: &'static [u8], +) -> Vec { + let mut pkt_buf = [0u8; 1500]; + let pkt = packet_builder!( + pkt_buf, + ipv4({set_source => ipv4addr!(source_ip), set_destination => ipv4addr!(destination_ip) }) / + tcp({set_source => source_port, set_destination => destination_port }) / + payload(payload) + ); + pkt.packet().to_vec() +} + fn format_raw_output(output: Vec) -> String { let stdout_utf8 = String::from_utf8(output).unwrap(); use regex::Regex; @@ -44,6 +61,25 @@ fn format_raw_output(output: Vec) -> String { format!("{}", replaced) } +#[test] +fn one_ip_packet_of_traffic() { + let network_frames = vec![NetworkFrames::new(vec![Some(build_ip_tcp_packet( + "10.0.0.2", + "1.1.1.1", + 443, + 12345, + b"I am a fake tcp packet", + ))]) as Box]; + let (_, _, backend) = test_backend_factory(190, 50); + let stdout = Arc::new(Mutex::new(Vec::new())); + let os_input = os_input_output_stdout(network_frames, 2, Some(stdout.clone())); + let opts = opts_raw(); + start(backend, os_input, opts); + let stdout = Arc::try_unwrap(stdout).unwrap().into_inner().unwrap(); + let formatted = format_raw_output(stdout); + assert_snapshot!(formatted); +} + #[test] fn one_packet_of_traffic() { let network_frames = vec![NetworkFrames::new(vec![Some(build_tcp_packet( diff --git a/src/tests/cases/snapshots/raw_mode__one_ip_packet_of_traffic.snap b/src/tests/cases/snapshots/raw_mode__one_ip_packet_of_traffic.snap new file mode 100644 index 000000000..f5b78d93f --- /dev/null +++ b/src/tests/cases/snapshots/raw_mode__one_ip_packet_of_traffic.snap @@ -0,0 +1,8 @@ +--- +source: src/tests/cases/raw_mode.rs +expression: formatted +--- +process: "1" up/down Bps: 42/0 connections: 1 +connection: :443 => 1.1.1.1:12345 (tcp) up/down Bps: 42/0 process: "1" +remote_address: 1.1.1.1 up/down Bps: 42/0 connections: 1 + From 10e23776e95724cee77b367bdaaf111df8c1be51 Mon Sep 17 00:00:00 2001 From: Maximilian Bosch Date: Wed, 8 Jan 2020 19:55:00 +0100 Subject: [PATCH 2/3] Use more meaningful consts from libpnet to match packets --- src/network/sniffer.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/network/sniffer.rs b/src/network/sniffer.rs index 33c9b8042..fccf0b980 100644 --- a/src/network/sniffer.rs +++ b/src/network/sniffer.rs @@ -1,8 +1,8 @@ use ::std::boxed::Box; use ::pnet_bandwhich_fork::datalink::{DataLinkReceiver, NetworkInterface}; -use ::pnet_bandwhich_fork::packet::ethernet::{EtherType, EthernetPacket}; -use ::pnet_bandwhich_fork::packet::ip::IpNextHeaderProtocol; +use ::pnet_bandwhich_fork::packet::ethernet::{EtherTypes, EthernetPacket}; +use ::pnet_bandwhich_fork::packet::ip::IpNextHeaderProtocols; use ::pnet_bandwhich_fork::packet::ipv4::Ipv4Packet; use ::pnet_bandwhich_fork::packet::tcp::TcpPacket; use ::pnet_bandwhich_fork::packet::udp::UdpPacket; @@ -65,7 +65,7 @@ impl Sniffer { _ => { let pkg = EthernetPacket::new(bytes)?; match pkg.get_ethertype() { - EtherType(2048) => Self::handle_v4(Ipv4Packet::new(pkg.payload())?, &self.network_interface), + EtherTypes::Ipv4 => Self::handle_v4(Ipv4Packet::new(pkg.payload())?, &self.network_interface), _ => None, } } @@ -74,7 +74,7 @@ impl Sniffer { fn handle_v4(ip_packet: Ipv4Packet, network_interface: &NetworkInterface) -> Option { let (protocol, source_port, destination_port, data_length) = match ip_packet.get_next_level_protocol() { - IpNextHeaderProtocol(6) => { + IpNextHeaderProtocols::Tcp => { let message = TcpPacket::new(ip_packet.payload())?; ( Protocol::Tcp, @@ -83,7 +83,7 @@ impl Sniffer { ip_packet.payload().len() as u128, ) } - IpNextHeaderProtocol(17) => { + IpNextHeaderProtocols::Udp => { let datagram = UdpPacket::new(ip_packet.payload())?; ( Protocol::Udp, From 9cae8da50be0cf0e2fe049f37117c4250d502db4 Mon Sep 17 00:00:00 2001 From: Maximilian Bosch Date: Thu, 9 Jan 2020 01:10:21 +0100 Subject: [PATCH 3/3] Fix codestyle using rustfmt --- src/network/sniffer.rs | 8 ++++---- src/tests/cases/raw_mode.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/network/sniffer.rs b/src/network/sniffer.rs index fccf0b980..ae737145b 100644 --- a/src/network/sniffer.rs +++ b/src/network/sniffer.rs @@ -65,7 +65,9 @@ impl Sniffer { _ => { let pkg = EthernetPacket::new(bytes)?; match pkg.get_ethertype() { - EtherTypes::Ipv4 => Self::handle_v4(Ipv4Packet::new(pkg.payload())?, &self.network_interface), + EtherTypes::Ipv4 => { + Self::handle_v4(Ipv4Packet::new(pkg.payload())?, &self.network_interface) + } _ => None, } } @@ -101,9 +103,7 @@ impl Sniffer { let to = SocketAddr::new(IpAddr::V4(ip_packet.get_destination()), destination_port); let connection = match direction { - Direction::Download => { - Connection::new(from, to.ip(), destination_port, protocol)? - }, + Direction::Download => Connection::new(from, to.ip(), destination_port, protocol)?, Direction::Upload => Connection::new(to, from.ip(), source_port, protocol)?, }; Some(Segment { diff --git a/src/tests/cases/raw_mode.rs b/src/tests/cases/raw_mode.rs index 054f37ba2..03c3684ed 100644 --- a/src/tests/cases/raw_mode.rs +++ b/src/tests/cases/raw_mode.rs @@ -37,7 +37,7 @@ fn build_tcp_packet( } fn build_ip_tcp_packet( - source_ip: &str, + source_ip: &str, destination_ip: &str, source_port: u16, destination_port: u16,