diff options
author | Maximilian Bosch <maximilian@mbosch.me> | 2020-01-08 18:13:37 +0100 |
---|---|---|
committer | Maximilian Bosch <maximilian@mbosch.me> | 2020-01-08 20:10:45 +0100 |
commit | 2543ca7de4ba192ad6533259a59140deda8b1064 (patch) | |
tree | ee5c0aa38d54c26c3a6084b5b9eaa4cdbfdd83f6 /src | |
parent | 33fb8440b1f9c0ec45cfc0c3b6b0ea5c457ade6e (diff) |
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
`<libpnet/examples/packetdump.rs>` 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.
Diffstat (limited to 'src')
-rw-r--r-- | src/network/sniffer.rs | 96 | ||||
-rw-r--r-- | src/tests/cases/raw_mode.rs | 36 | ||||
-rw-r--r-- | src/tests/cases/snapshots/raw_mode__one_ip_packet_of_traffic.snap | 8 |
3 files changed, 97 insertions, 43 deletions
diff --git a/src/network/sniffer.rs b/src/network/sniffer.rs index e0a5a39..33c9b80 100644 --- a/src/network/sniffer.rs +++ b/src/network/sniffer.rs @@ -56,51 +56,61 @@ impl Sniffer { } pub fn next(&mut self) -> Option<Segment> { 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<Segment> { + 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 5353fd0..054f37b 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<u8> { + 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<u8>) -> String { let stdout_utf8 = String::from_utf8(output).unwrap(); use regex::Regex; @@ -45,6 +62,25 @@ fn format_raw_output(output: Vec<u8>) -> String { } #[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<dyn DataLinkReceiver>]; + 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( "10.0.0.2", 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 0000000..f5b78d9 --- /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: <TIMESTAMP_REMOVED> "1" up/down Bps: 42/0 connections: 1 +connection: <TIMESTAMP_REMOVED> <interface_name>:443 => 1.1.1.1:12345 (tcp) up/down Bps: 42/0 process: "1" +remote_address: <TIMESTAMP_REMOVED> 1.1.1.1 up/down Bps: 42/0 connections: 1 + |