diff --git a/.travis.yml b/.travis.yml index 75bdeb6..6cdd711 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,10 @@ os: - linux -dist: bionic +dist: focal language: rust rust: - stable before_install: - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get -y install libpcap-dev tshark; fi + - sudo apt-get -y install libpcap-dev tshark diff --git a/README.md b/README.md index 86005ea..63543c9 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Small, fast, and correct L2/L3/L4 packet parser. The following protocol hierarchy can be parsed with this library: - * Ethernet (including vlan) + * Ethernet (including vlan/QinQ) * ARP * IPv4 (including options) * TCP (including options) diff --git a/fuzz.sh b/fuzz.sh index 62d1876..b241a1b 100755 --- a/fuzz.sh +++ b/fuzz.sh @@ -13,26 +13,26 @@ fi DOCKER="docker" ${DOCKER} build -t pdu-fuzz - <<'EOF' -FROM ubuntu:bionic -ENV LANG=C.UTF-8 \ - LC_ALL=C.UTF-8 +FROM ubuntu:focal +ENV LANG=C.UTF-8 LC_ALL=C.UTF-8 VOLUME /usr/local/src/pdu WORKDIR /usr/local/src/pdu SHELL ["/bin/bash", "-eu", "-o", "pipefail", "-c"] RUN \ export DEBIAN_FRONTEND=noninteractive; \ apt-get -q update; \ - apt-get -q install -y curl build-essential linux-headers-generic pkg-config binutils-dev libunwind-dev libpcap-dev tshark; \ + apt-get -q install -y curl build-essential linux-headers-generic pkg-config binutils-dev libunwind-dev libblocksruntime-dev liblzma-dev libpcap-dev tshark; \ apt-get -q clean autoclean; -RUN \ - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable; \ - source $HOME/.cargo/env; \ +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable; +RUN source $HOME/.cargo/env; \ cargo install honggfuzz; ENTRYPOINT \ mkdir -p /tmp/honggfuzz/$FUZZ_TARGET; \ source $HOME/.cargo/env; \ cd ./fuzz; \ - RUSTFLAGS="-C link-dead-code" HFUZZ_RUN_ARGS="-t 5 -T --output /tmp/honggfuzz/$FUZZ_TARGET" cargo hfuzz run $FUZZ_TARGET + export RUSTFLAGS="-C link-dead-code"; \ + export HFUZZ_RUN_ARGS="-t 5 -T --output /tmp/honggfuzz/$FUZZ_TARGET"; \ + cargo hfuzz run $FUZZ_TARGET EOF if [ -z "$1" ]; then diff --git a/fuzz/src/bin/arp.rs b/fuzz/src/bin/arp.rs index b11f83b..5f8e943 100644 --- a/fuzz/src/bin/arp.rs +++ b/fuzz/src/bin/arp.rs @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Alex Forster + Copyright (c) Alex Forster Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -19,26 +19,23 @@ use pdu::*; pub fn fuzz(data: &[u8]) { - match ArpPdu::new(&data) { - Ok(arp_pdu) => { - arp_pdu.hardware_type(); - arp_pdu.protocol_type(); - arp_pdu.hardware_length(); - arp_pdu.protocol_length(); - arp_pdu.opcode(); - arp_pdu.sender_hardware_address(); - arp_pdu.sender_protocol_address(); - arp_pdu.target_hardware_address(); - arp_pdu.target_protocol_address(); - } - Err(_) => {} + if let Ok(arp_pdu) = ArpPdu::new(data) { + arp_pdu.hardware_type(); + arp_pdu.protocol_type(); + arp_pdu.hardware_length(); + arp_pdu.protocol_length(); + arp_pdu.opcode(); + arp_pdu.sender_hardware_address(); + arp_pdu.sender_protocol_address(); + arp_pdu.target_hardware_address(); + arp_pdu.target_protocol_address(); } } fn main() { loop { honggfuzz::fuzz!(|data: &[u8]| { - fuzz(&data); + fuzz(data); }); } } diff --git a/fuzz/src/bin/ethernet.rs b/fuzz/src/bin/ethernet.rs index fe9b69c..82b9ebf 100644 --- a/fuzz/src/bin/ethernet.rs +++ b/fuzz/src/bin/ethernet.rs @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Alex Forster + Copyright (c) Alex Forster Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -19,24 +19,27 @@ use pdu::*; pub fn fuzz(data: &[u8]) { - match EthernetPdu::new(&data) { - Ok(ethernet_pdu) => { - ethernet_pdu.computed_ihl(); - ethernet_pdu.destination_address(); - ethernet_pdu.source_address(); - ethernet_pdu.ethertype(); - ethernet_pdu.vlan(); - ethernet_pdu.vlan_pcp(); - ethernet_pdu.vlan_dei(); + if let Ok(ethernet_pdu) = EthernetPdu::new(data) { + ethernet_pdu.computed_ihl(); + ethernet_pdu.destination_address(); + ethernet_pdu.source_address(); + ethernet_pdu.ethertype(); + ethernet_pdu.computed_ethertype(); + if let Some(vlan_tags) = ethernet_pdu.vlan_tags() { + for vlan_tag in vlan_tags { + vlan_tag.protocol_id; + vlan_tag.priority_codepoint; + vlan_tag.drop_eligible; + vlan_tag.id; + } } - Err(_) => {} } } fn main() { loop { honggfuzz::fuzz!(|data: &[u8]| { - fuzz(&data); + fuzz(data); }); } } diff --git a/fuzz/src/bin/gre.rs b/fuzz/src/bin/gre.rs index 14198d6..2e75ba0 100644 --- a/fuzz/src/bin/gre.rs +++ b/fuzz/src/bin/gre.rs @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Alex Forster + Copyright (c) Alex Forster Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -19,24 +19,21 @@ use pdu::*; pub fn fuzz(data: &[u8]) { - match GrePdu::new(&data) { - Ok(gre_pdu) => { - gre_pdu.computed_ihl(); - gre_pdu.version(); - gre_pdu.ethertype(); - gre_pdu.checksum(); - gre_pdu.computed_checksum(); - gre_pdu.key(); - gre_pdu.sequence_number(); - } - Err(_) => {} + if let Ok(gre_pdu) = GrePdu::new(data) { + gre_pdu.computed_ihl(); + gre_pdu.version(); + gre_pdu.ethertype(); + gre_pdu.checksum(); + gre_pdu.computed_checksum(); + gre_pdu.key(); + gre_pdu.sequence_number(); } } fn main() { loop { honggfuzz::fuzz!(|data: &[u8]| { - fuzz(&data); + fuzz(data); }); } } diff --git a/fuzz/src/bin/icmp.rs b/fuzz/src/bin/icmp.rs index bfa865e..eb489e2 100644 --- a/fuzz/src/bin/icmp.rs +++ b/fuzz/src/bin/icmp.rs @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Alex Forster + Copyright (c) Alex Forster Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -19,37 +19,34 @@ use pdu::*; pub fn fuzz(data: &[u8]) { - match IcmpPdu::new(&data) { - Ok(icmp_pdu) => { - icmp_pdu.message_type(); - icmp_pdu.message_code(); - icmp_pdu.checksum(); - let ip = Ip::Ipv4( - Ipv4Pdu::new(&[ - 0x45u8, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - ]) - .unwrap(), - ); - icmp_pdu.computed_checksum(&ip); - let ip = Ip::Ipv6( - Ipv6Pdu::new(&[ - 0x60u8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ]) - .unwrap(), - ); - icmp_pdu.computed_checksum(&ip); - } - Err(_) => {} + if let Ok(icmp_pdu) = IcmpPdu::new(data) { + icmp_pdu.message_type(); + icmp_pdu.message_code(); + icmp_pdu.checksum(); + let ip = Ip::Ipv4( + Ipv4Pdu::new(&[ + 0x45u8, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, + ]) + .unwrap(), + ); + icmp_pdu.computed_checksum(&ip); + let ip = Ip::Ipv6( + Ipv6Pdu::new(&[ + 0x60u8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]) + .unwrap(), + ); + icmp_pdu.computed_checksum(&ip); } } fn main() { loop { honggfuzz::fuzz!(|data: &[u8]| { - fuzz(&data); + fuzz(data); }); } } diff --git a/fuzz/src/bin/ipv4.rs b/fuzz/src/bin/ipv4.rs index c20c7f2..90cd1d0 100644 --- a/fuzz/src/bin/ipv4.rs +++ b/fuzz/src/bin/ipv4.rs @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Alex Forster + Copyright (c) Alex Forster Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -19,40 +19,37 @@ use pdu::*; pub fn fuzz(data: &[u8]) { - match Ipv4Pdu::new(&data) { - Ok(ipv4_pdu) => { - ipv4_pdu.version(); - ipv4_pdu.ihl(); - ipv4_pdu.computed_ihl(); - ipv4_pdu.dscp(); - ipv4_pdu.ecn(); - ipv4_pdu.total_length(); - ipv4_pdu.identification(); - ipv4_pdu.dont_fragment(); - ipv4_pdu.more_fragments(); - ipv4_pdu.fragment_offset(); - ipv4_pdu.ttl(); - ipv4_pdu.protocol(); - ipv4_pdu.checksum(); - ipv4_pdu.computed_checksum(); - ipv4_pdu.source_address(); - ipv4_pdu.destination_address(); - for option in ipv4_pdu.options() { - match option { - Ipv4Option::Raw { .. } => { - continue; - } + if let Ok(ipv4_pdu) = Ipv4Pdu::new(data) { + ipv4_pdu.version(); + ipv4_pdu.ihl(); + ipv4_pdu.computed_ihl(); + ipv4_pdu.dscp(); + ipv4_pdu.ecn(); + ipv4_pdu.total_length(); + ipv4_pdu.identification(); + ipv4_pdu.dont_fragment(); + ipv4_pdu.more_fragments(); + ipv4_pdu.fragment_offset(); + ipv4_pdu.ttl(); + ipv4_pdu.protocol(); + ipv4_pdu.checksum(); + ipv4_pdu.computed_checksum(); + ipv4_pdu.source_address(); + ipv4_pdu.destination_address(); + for option in ipv4_pdu.options() { + match option { + Ipv4Option::Raw { .. } => { + continue; } } } - Err(_) => {} } } fn main() { loop { honggfuzz::fuzz!(|data: &[u8]| { - fuzz(&data); + fuzz(data); }); } } diff --git a/fuzz/src/bin/ipv6.rs b/fuzz/src/bin/ipv6.rs index 5ff805c..218656a 100644 --- a/fuzz/src/bin/ipv6.rs +++ b/fuzz/src/bin/ipv6.rs @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Alex Forster + Copyright (c) Alex Forster Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -19,41 +19,38 @@ use pdu::*; pub fn fuzz(data: &[u8]) { - match Ipv6Pdu::new(&data) { - Ok(ipv6_pdu) => { - ipv6_pdu.version(); - ipv6_pdu.dscp(); - ipv6_pdu.ecn(); - ipv6_pdu.flow_label(); - ipv6_pdu.payload_length(); - ipv6_pdu.next_header(); - ipv6_pdu.computed_ihl(); - ipv6_pdu.computed_protocol(); - ipv6_pdu.computed_identification(); - ipv6_pdu.computed_more_fragments(); - ipv6_pdu.computed_fragment_offset(); - ipv6_pdu.hop_limit(); - ipv6_pdu.source_address(); - ipv6_pdu.destination_address(); - for extension_header in ipv6_pdu.extension_headers() { - match extension_header { - Ipv6ExtensionHeader::Raw { .. } => { - continue; - } - Ipv6ExtensionHeader::Fragment { .. } => { - continue; - } + if let Ok(ipv6_pdu) = Ipv6Pdu::new(data) { + ipv6_pdu.version(); + ipv6_pdu.dscp(); + ipv6_pdu.ecn(); + ipv6_pdu.flow_label(); + ipv6_pdu.payload_length(); + ipv6_pdu.next_header(); + ipv6_pdu.computed_ihl(); + ipv6_pdu.computed_protocol(); + ipv6_pdu.computed_identification(); + ipv6_pdu.computed_more_fragments(); + ipv6_pdu.computed_fragment_offset(); + ipv6_pdu.hop_limit(); + ipv6_pdu.source_address(); + ipv6_pdu.destination_address(); + for extension_header in ipv6_pdu.extension_headers() { + match extension_header { + Ipv6ExtensionHeader::Raw { .. } => { + continue; + } + Ipv6ExtensionHeader::Fragment { .. } => { + continue; } } } - Err(_) => {} } } fn main() { loop { honggfuzz::fuzz!(|data: &[u8]| { - fuzz(&data); + fuzz(data); }); } } diff --git a/fuzz/src/bin/tcp.rs b/fuzz/src/bin/tcp.rs index 71ed25d..ade4533 100644 --- a/fuzz/src/bin/tcp.rs +++ b/fuzz/src/bin/tcp.rs @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Alex Forster + Copyright (c) Alex Forster Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -19,78 +19,75 @@ use pdu::*; pub fn fuzz(data: &[u8]) { - match TcpPdu::new(&data) { - Ok(tcp_pdu) => { - tcp_pdu.source_port(); - tcp_pdu.destination_port(); - tcp_pdu.sequence_number(); - tcp_pdu.acknowledgement_number(); - tcp_pdu.data_offset(); - tcp_pdu.computed_data_offset(); - tcp_pdu.flags(); - tcp_pdu.fin(); - tcp_pdu.syn(); - tcp_pdu.rst(); - tcp_pdu.psh(); - tcp_pdu.ack(); - tcp_pdu.urg(); - tcp_pdu.ecn(); - tcp_pdu.cwr(); - tcp_pdu.window_size(); - tcp_pdu.computed_window_size(14); - tcp_pdu.checksum(); - let ip = Ip::Ipv4( - Ipv4Pdu::new(&[ - 0x45u8, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - ]) - .unwrap(), - ); - tcp_pdu.computed_checksum(&ip); - let ip = Ip::Ipv6( - Ipv6Pdu::new(&[ - 0x60u8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ]) - .unwrap(), - ); - tcp_pdu.computed_checksum(&ip); - tcp_pdu.urgent_pointer(); - for option in tcp_pdu.options() { - match option { - TcpOption::Raw { .. } => { - continue; - } - TcpOption::NoOp => { - continue; - } - TcpOption::Mss { .. } => { - continue; - } - TcpOption::WindowScale { .. } => { - continue; - } - TcpOption::SackPermitted => { - continue; - } - TcpOption::Sack { .. } => { - continue; - } - TcpOption::Timestamp { .. } => { - continue; - } + if let Ok(tcp_pdu) = TcpPdu::new(data) { + tcp_pdu.source_port(); + tcp_pdu.destination_port(); + tcp_pdu.sequence_number(); + tcp_pdu.acknowledgement_number(); + tcp_pdu.data_offset(); + tcp_pdu.computed_data_offset(); + tcp_pdu.flags(); + tcp_pdu.fin(); + tcp_pdu.syn(); + tcp_pdu.rst(); + tcp_pdu.psh(); + tcp_pdu.ack(); + tcp_pdu.urg(); + tcp_pdu.ecn(); + tcp_pdu.cwr(); + tcp_pdu.window_size(); + tcp_pdu.computed_window_size(14); + tcp_pdu.checksum(); + let ip = Ip::Ipv4( + Ipv4Pdu::new(&[ + 0x45u8, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, + ]) + .unwrap(), + ); + tcp_pdu.computed_checksum(&ip); + let ip = Ip::Ipv6( + Ipv6Pdu::new(&[ + 0x60u8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]) + .unwrap(), + ); + tcp_pdu.computed_checksum(&ip); + tcp_pdu.urgent_pointer(); + for option in tcp_pdu.options() { + match option { + TcpOption::Raw { .. } => { + continue; + } + TcpOption::NoOp => { + continue; + } + TcpOption::Mss { .. } => { + continue; + } + TcpOption::WindowScale { .. } => { + continue; + } + TcpOption::SackPermitted => { + continue; + } + TcpOption::Sack { .. } => { + continue; + } + TcpOption::Timestamp { .. } => { + continue; } } } - Err(_) => {} } } fn main() { loop { honggfuzz::fuzz!(|data: &[u8]| { - fuzz(&data); + fuzz(data); }); } } diff --git a/fuzz/src/bin/udp.rs b/fuzz/src/bin/udp.rs index bb72692..6d89343 100644 --- a/fuzz/src/bin/udp.rs +++ b/fuzz/src/bin/udp.rs @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Alex Forster + Copyright (c) Alex Forster Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -19,38 +19,35 @@ use pdu::*; pub fn fuzz(data: &[u8]) { - match UdpPdu::new(&data) { - Ok(udp_pdu) => { - udp_pdu.source_port(); - udp_pdu.destination_port(); - udp_pdu.length(); - udp_pdu.checksum(); - let ip = Ip::Ipv4( - Ipv4Pdu::new(&[ - 0x45u8, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - ]) - .unwrap(), - ); - udp_pdu.computed_checksum(&ip); - let ip = Ip::Ipv6( - Ipv6Pdu::new(&[ - 0x60u8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ]) - .unwrap(), - ); - udp_pdu.computed_checksum(&ip); - } - Err(_) => {} + if let Ok(udp_pdu) = UdpPdu::new(data) { + udp_pdu.source_port(); + udp_pdu.destination_port(); + udp_pdu.length(); + udp_pdu.checksum(); + let ip = Ip::Ipv4( + Ipv4Pdu::new(&[ + 0x45u8, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, + ]) + .unwrap(), + ); + udp_pdu.computed_checksum(&ip); + let ip = Ip::Ipv6( + Ipv6Pdu::new(&[ + 0x60u8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]) + .unwrap(), + ); + udp_pdu.computed_checksum(&ip); } } fn main() { loop { honggfuzz::fuzz!(|data: &[u8]| { - fuzz(&data); + fuzz(data); }); } } diff --git a/fuzz/src/lib.rs b/fuzz/src/lib.rs index 5e1677b..306e15f 100644 --- a/fuzz/src/lib.rs +++ b/fuzz/src/lib.rs @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Alex Forster + Copyright (c) Alex Forster Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/src/arp.rs b/src/arp.rs index 1bf93fc..cad3a34 100644 --- a/src/arp.rs +++ b/src/arp.rs @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Alex Forster + Copyright (c) Alex Forster Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -60,7 +60,7 @@ impl<'a> ArpPdu<'a> { /// Returns the slice of the underlying buffer that contains this PDU pub fn as_bytes(&'a self) -> &'a [u8] { - self.clone().into_bytes() + (*self).into_bytes() } /// Consumes this object and returns the slice of the underlying buffer that contains this PDU @@ -69,11 +69,11 @@ impl<'a> ArpPdu<'a> { } pub fn hardware_type(&'a self) -> u16 { - u16::from_be_bytes(self.buffer[0..=1].try_into().unwrap()) + u16::from_be_bytes(self.buffer[0..2].try_into().unwrap()) } pub fn protocol_type(&'a self) -> u16 { - u16::from_be_bytes(self.buffer[2..=3].try_into().unwrap()) + u16::from_be_bytes(self.buffer[2..4].try_into().unwrap()) } pub fn hardware_length(&'a self) -> u8 { @@ -85,7 +85,7 @@ impl<'a> ArpPdu<'a> { } pub fn opcode(&'a self) -> u16 { - u16::from_be_bytes(self.buffer[6..=7].try_into().unwrap()) + u16::from_be_bytes(self.buffer[6..8].try_into().unwrap()) } pub fn sender_hardware_address(&'a self) -> [u8; 6] { diff --git a/src/ethernet.rs b/src/ethernet.rs index b160312..a2a4469 100644 --- a/src/ethernet.rs +++ b/src/ethernet.rs @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Alex Forster + Copyright (c) Alex Forster Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -27,6 +27,7 @@ pub mod EtherType { pub const IPV4: u16 = 0x0800; pub const IPV6: u16 = 0x86DD; pub const DOT1Q: u16 = 0x8100; + pub const QINQ: u16 = 0x88A8; pub const TEB: u16 = 0x6558; } @@ -34,6 +35,7 @@ pub mod EtherType { #[derive(Debug, Copy, Clone)] pub struct EthernetPdu<'a> { buffer: &'a [u8], + ihl: usize, } /// Contains the inner payload of an [`EthernetPdu`] @@ -51,15 +53,37 @@ impl<'a> EthernetPdu<'a> { if buffer.len() < 14 { return Err(Error::Truncated); } - let pdu = EthernetPdu { buffer }; - if pdu.tpid() == EtherType::DOT1Q && buffer.len() < 18 { + let pos = 12; + let ethertype = u16::from_be_bytes(buffer[pos..pos + 2].try_into().unwrap()); + match ethertype { + EtherType::DOT1Q => Self::dot1q(buffer, pos + 2), + EtherType::QINQ => Self::qinq(buffer, pos + 2), + _ => Ok(EthernetPdu { buffer, ihl: 14 }), + } + } + + fn dot1q(buffer: &'a [u8], pos: usize) -> Result { + // buffer needs the tag + ether len/tag + if buffer.len() < pos + 4 { + return Err(Error::Truncated); + } + + Ok(EthernetPdu { buffer, ihl: pos + 4 }) + } + + fn qinq(buffer: &'a [u8], mut pos: usize) -> Result { + // buffer needs to contain tag + dot1q tag + if buffer.len() < pos + 4 { return Err(Error::Truncated); } - if pdu.ethertype() < 0x0600 { - // we don't support 802.3 (LLC) frames + + pos += 2; + let ethertype = u16::from_be_bytes(buffer[pos..pos + 2].try_into().unwrap()); + if ethertype != EtherType::DOT1Q { return Err(Error::Malformed); } - Ok(pdu) + + Self::dot1q(buffer, pos + 2) } /// Returns a reference to the entire underlying buffer that was provided during construction @@ -75,7 +99,7 @@ impl<'a> EthernetPdu<'a> { /// Returns the slice of the underlying buffer that contains the header part of this PDU pub fn as_bytes(&'a self) -> &'a [u8] { - self.clone().into_bytes() + (*self).into_bytes() } /// Consumes this object and returns the slice of the underlying buffer that contains the header part of this PDU @@ -85,13 +109,13 @@ impl<'a> EthernetPdu<'a> { /// Returns an object representing the inner payload of this PDU pub fn inner(&'a self) -> Result> { - self.clone().into_inner() + (*self).into_inner() } /// Consumes this object and returns an object representing the inner payload of this PDU pub fn into_inner(self) -> Result> { let rest = &self.buffer[self.computed_ihl()..]; - Ok(match self.ethertype() { + Ok(match self.computed_ethertype() { EtherType::ARP => Ethernet::Arp(super::ArpPdu::new(rest)?), EtherType::IPV4 => Ethernet::Ipv4(super::Ipv4Pdu::new(rest)?), EtherType::IPV6 => Ethernet::Ipv6(super::Ipv6Pdu::new(rest)?), @@ -100,10 +124,7 @@ impl<'a> EthernetPdu<'a> { } pub fn computed_ihl(&'a self) -> usize { - match self.tpid() { - EtherType::DOT1Q => 18, - _ => 14, - } + self.ihl } pub fn source_address(&'a self) -> [u8; 6] { @@ -118,35 +139,59 @@ impl<'a> EthernetPdu<'a> { destination_address } - pub fn tpid(&'a self) -> u16 { - u16::from_be_bytes(self.buffer[12..=13].try_into().unwrap()) - } - pub fn ethertype(&'a self) -> u16 { - match self.tpid() { - EtherType::DOT1Q => u16::from_be_bytes(self.buffer[16..=17].try_into().unwrap()), - ethertype => ethertype, - } + u16::from_be_bytes(self.buffer[12..14].try_into().unwrap()) } - pub fn vlan(&'a self) -> Option { - match self.tpid() { - EtherType::DOT1Q => Some(u16::from_be_bytes(self.buffer[14..=15].try_into().unwrap()) & 0x0FFF), - _ => None, - } + pub fn computed_ethertype(&'a self) -> u16 { + u16::from_be_bytes(self.buffer[(self.ihl - 2)..self.ihl].try_into().unwrap()) } - pub fn vlan_pcp(&'a self) -> Option { - match self.tpid() { - EtherType::DOT1Q => Some((self.buffer[14] & 0xE0) >> 5), + pub fn vlan_tags(&'a self) -> Option> { + match self.ethertype() { + EtherType::DOT1Q | EtherType::QINQ => Some(VlanTagIterator { buffer: self.buffer, pos: 12, eol: false }), _ => None, } } +} - pub fn vlan_dei(&'a self) -> Option { - match self.tpid() { - EtherType::DOT1Q => Some(((self.buffer[14] & 0x10) >> 4) > 0), - _ => None, +/// Represents a VLAN tag +#[derive(Debug, Copy, Clone)] +pub struct VlanTag { + pub protocol_id: u16, + pub priority_codepoint: u8, + pub drop_eligible: bool, + pub id: u16, +} + +#[derive(Debug, Copy, Clone)] +pub struct VlanTagIterator<'a> { + buffer: &'a [u8], + pos: usize, + eol: bool, +} + +impl<'a> Iterator for VlanTagIterator<'a> { + type Item = VlanTag; + + fn next(&mut self) -> Option { + if self.eol { + return None; + } + let tpid = u16::from_be_bytes(self.buffer[self.pos..self.pos + 2].try_into().unwrap()); + if tpid != EtherType::DOT1Q && tpid != EtherType::QINQ { + return None; + } + if tpid == EtherType::DOT1Q { + self.eol = true; } + let vlan_tag = VlanTag { + protocol_id: tpid, + priority_codepoint: (self.buffer[self.pos + 2] & 0xE0) >> 5, + drop_eligible: ((self.buffer[self.pos + 2] & 0x10) >> 4) > 0, + id: u16::from_be_bytes([self.buffer[self.pos + 2] & 0x0F, self.buffer[self.pos + 3]]), + }; + self.pos += 4; + Some(vlan_tag) } } diff --git a/src/gre.rs b/src/gre.rs index 2c37fd2..d6c97fd 100644 --- a/src/gre.rs +++ b/src/gre.rs @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Alex Forster + Copyright (c) Alex Forster Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -65,7 +65,7 @@ impl<'a> GrePdu<'a> { /// Returns the slice of the underlying buffer that contains the header part of this PDU pub fn as_bytes(&'a self) -> &'a [u8] { - self.clone().into_bytes() + (*self).into_bytes() } /// Consumes this object and returns the slice of the underlying buffer that contains the header part of this PDU @@ -75,7 +75,7 @@ impl<'a> GrePdu<'a> { /// Returns an object representing the inner payload of this PDU pub fn inner(&'a self) -> Result> { - self.clone().into_inner() + (*self).into_inner() } /// Consumes this object and returns an object representing the inner payload of this PDU @@ -108,7 +108,7 @@ impl<'a> GrePdu<'a> { } pub fn ethertype(&'a self) -> u16 { - u16::from_be_bytes(self.buffer[2..=3].try_into().unwrap()) + u16::from_be_bytes(self.buffer[2..4].try_into().unwrap()) } pub fn has_checksum(&'a self) -> bool { @@ -125,7 +125,7 @@ impl<'a> GrePdu<'a> { pub fn checksum(&'a self) -> Option { if self.has_checksum() { - Some(u16::from_be_bytes(self.buffer[4..=5].try_into().unwrap())) + Some(u16::from_be_bytes(self.buffer[4..6].try_into().unwrap())) } else { None } @@ -133,7 +133,7 @@ impl<'a> GrePdu<'a> { pub fn computed_checksum(&'a self) -> Option { if self.has_checksum() { - Some(util::checksum(&[&self.buffer[0..=3], &self.buffer[6..]])) + Some(util::checksum(&[&self.buffer[0..4], &self.buffer[6..]])) } else { None } @@ -141,9 +141,9 @@ impl<'a> GrePdu<'a> { pub fn key(&'a self) -> Option { if self.has_checksum() && self.has_key() { - Some(u32::from_be_bytes(self.buffer[8..=11].try_into().unwrap())) + Some(u32::from_be_bytes(self.buffer[8..12].try_into().unwrap())) } else if self.has_key() { - Some(u32::from_be_bytes(self.buffer[4..=7].try_into().unwrap())) + Some(u32::from_be_bytes(self.buffer[4..8].try_into().unwrap())) } else { None } @@ -151,11 +151,11 @@ impl<'a> GrePdu<'a> { pub fn sequence_number(&'a self) -> Option { if self.has_sequence_number() && self.has_checksum() && self.has_key() { - Some(u32::from_be_bytes(self.buffer[12..=15].try_into().unwrap())) + Some(u32::from_be_bytes(self.buffer[12..16].try_into().unwrap())) } else if self.has_sequence_number() && (self.has_checksum() || self.has_key()) { - Some(u32::from_be_bytes(self.buffer[8..=11].try_into().unwrap())) + Some(u32::from_be_bytes(self.buffer[8..12].try_into().unwrap())) } else if self.has_sequence_number() { - Some(u32::from_be_bytes(self.buffer[4..=7].try_into().unwrap())) + Some(u32::from_be_bytes(self.buffer[4..8].try_into().unwrap())) } else { None } diff --git a/src/icmp.rs b/src/icmp.rs index d9f4434..cc5621f 100644 --- a/src/icmp.rs +++ b/src/icmp.rs @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Alex Forster + Copyright (c) Alex Forster Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -54,7 +54,7 @@ impl<'a> IcmpPdu<'a> { /// Returns the slice of the underlying buffer that contains the header part of this PDU pub fn as_bytes(&'a self) -> &'a [u8] { - self.clone().into_bytes() + (*self).into_bytes() } /// Consumes this object and returns the slice of the underlying buffer that contains the header part of this PDU @@ -64,7 +64,7 @@ impl<'a> IcmpPdu<'a> { /// Returns an object representing the inner payload of this PDU pub fn inner(&'a self) -> Result> { - self.clone().into_inner() + (*self).into_inner() } /// Consumes this object and returns an object representing the inner payload of this PDU @@ -82,18 +82,18 @@ impl<'a> IcmpPdu<'a> { } pub fn checksum(&'a self) -> u16 { - u16::from_be_bytes(self.buffer[2..=3].try_into().unwrap()) + u16::from_be_bytes(self.buffer[2..4].try_into().unwrap()) } pub fn computed_checksum(&'a self, ip: &crate::Ip) -> u16 { match ip { - crate::Ip::Ipv4(_) => util::checksum(&[&self.buffer[0..=1], &self.buffer[4..]]), + crate::Ip::Ipv4(_) => util::checksum(&[&self.buffer[0..2], &self.buffer[4..]]), crate::Ip::Ipv6(ipv6) => util::checksum(&[ - &ipv6.source_address().as_ref(), - &ipv6.destination_address().as_ref(), - &(ipv6.payload_length() as u32).to_be_bytes().as_ref(), - &[0x0, 0x0, 0x0, ipv6.computed_protocol()].as_ref(), - &self.buffer[0..=1], + ipv6.source_address().as_ref(), + ipv6.destination_address().as_ref(), + (ipv6.payload_length() as u32).to_be_bytes().as_ref(), + [0x0, 0x0, 0x0, ipv6.computed_protocol()].as_ref(), + &self.buffer[0..2], &self.buffer[4..], ]), } @@ -107,4 +107,8 @@ impl<'a> IcmpPdu<'a> { pub fn computed_data_offset(&'a self) -> usize { 4 } + + pub fn rest(&'a self) -> &'a [u8] { + &self.buffer[4..8] + } } diff --git a/src/ip.rs b/src/ip.rs index d268dfd..b167c26 100644 --- a/src/ip.rs +++ b/src/ip.rs @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Alex Forster + Copyright (c) Alex Forster Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -28,6 +28,10 @@ pub mod IpProto { pub const ICMP: u8 = 1; pub const ICMP6: u8 = 58; pub const GRE: u8 = 47; + pub const HOPOPTS: u8 = 0; + pub const ROUTING: u8 = 43; + pub const FRAGMENT: u8 = 44; + pub const DESTOPTS: u8 = 60; } /// Contains either an [`Ipv4Pdu`] or [`Ipv6Pdu`] depending on address family @@ -96,7 +100,7 @@ impl<'a> Ipv4Pdu<'a> { /// Returns the slice of the underlying buffer that contains the header part of this PDU pub fn as_bytes(&'a self) -> &'a [u8] { - self.clone().into_bytes() + (*self).into_bytes() } /// Consumes this object and returns the slice of the underlying buffer that contains the header part of this PDU @@ -106,7 +110,7 @@ impl<'a> Ipv4Pdu<'a> { /// Returns an object representing the inner payload of this PDU pub fn inner(&'a self) -> Result> { - self.clone().into_inner() + (*self).into_inner() } /// Consumes this object and returns an object representing the inner payload of this PDU @@ -153,11 +157,11 @@ impl<'a> Ipv4Pdu<'a> { } pub fn total_length(&'a self) -> u16 { - u16::from_be_bytes(self.buffer[2..=3].try_into().unwrap()) + u16::from_be_bytes(self.buffer[2..4].try_into().unwrap()) } pub fn identification(&'a self) -> u16 { - u16::from_be_bytes(self.buffer[4..=5].try_into().unwrap()) + u16::from_be_bytes(self.buffer[4..6].try_into().unwrap()) } pub fn dont_fragment(&'a self) -> bool { @@ -185,11 +189,11 @@ impl<'a> Ipv4Pdu<'a> { } pub fn checksum(&'a self) -> u16 { - u16::from_be_bytes(self.buffer[10..=11].try_into().unwrap()) + u16::from_be_bytes(self.buffer[10..12].try_into().unwrap()) } pub fn computed_checksum(&'a self) -> u16 { - util::checksum(&[&self.buffer[0..=9], &self.buffer[12..self.computed_ihl()]]) + util::checksum(&[&self.buffer[0..10], &self.buffer[12..self.computed_ihl()]]) } pub fn source_address(&'a self) -> [u8; 4] { @@ -205,7 +209,7 @@ impl<'a> Ipv4Pdu<'a> { } pub fn options(&'a self) -> Ipv4OptionIterator<'a> { - Ipv4OptionIterator { buffer: &self.buffer, pos: 20, ihl: self.computed_ihl() } + Ipv4OptionIterator { buffer: self.buffer, pos: 20, ihl: self.computed_ihl() } } } @@ -281,7 +285,7 @@ impl<'a> Ipv6Pdu<'a> { } let mut position = 40; let mut next_header = buffer[6]; - while let 0 | 43 | 44 | 59 | 60 = next_header { + while let IpProto::HOPOPTS | IpProto::ROUTING | IpProto::FRAGMENT | IpProto::DESTOPTS = next_header { if buffer.len() <= (position + 1) { return Err(Error::Truncated); } @@ -310,7 +314,7 @@ impl<'a> Ipv6Pdu<'a> { /// Returns the slice of the underlying buffer that contains the header part of this PDU pub fn as_bytes(&'a self) -> &'a [u8] { - self.clone().into_bytes() + (*self).into_bytes() } /// Consumes this object and returns the slice of the underlying buffer that contains the header part of this PDU @@ -320,7 +324,7 @@ impl<'a> Ipv6Pdu<'a> { /// Returns an object representing the inner payload of this PDU pub fn inner(&'a self) -> Result> { - self.clone().into_inner() + (*self).into_inner() } /// Consumes this object and returns an object representing the inner payload of this PDU @@ -363,7 +367,7 @@ impl<'a> Ipv6Pdu<'a> { } pub fn payload_length(&'a self) -> u16 { - u16::from_be_bytes(self.buffer[4..=5].try_into().unwrap()) + u16::from_be_bytes(self.buffer[4..6].try_into().unwrap()) } pub fn next_header(&'a self) -> u8 { @@ -373,7 +377,7 @@ impl<'a> Ipv6Pdu<'a> { pub fn computed_ihl(&'a self) -> usize { let mut position = 40; let mut next_header = self.next_header(); - while let 0 | 43 | 44 | 59 | 60 = next_header { + while let IpProto::HOPOPTS | IpProto::ROUTING | IpProto::FRAGMENT | IpProto::DESTOPTS = next_header { next_header = self.buffer[position]; position += ((self.buffer[position + 1] as usize) + 1) * 8; } @@ -383,7 +387,7 @@ impl<'a> Ipv6Pdu<'a> { pub fn computed_protocol(&'a self) -> u8 { let mut position = 40; let mut next_header = self.next_header(); - while let 0 | 43 | 44 | 59 | 60 = next_header { + while let IpProto::HOPOPTS | IpProto::ROUTING | IpProto::FRAGMENT | IpProto::DESTOPTS = next_header { next_header = self.buffer[position]; position += ((self.buffer[position + 1] as usize) + 1) * 8; } @@ -456,7 +460,7 @@ impl<'a> Iterator for Ipv6ExtensionHeaderIterator<'a> { type Item = Ipv6ExtensionHeader<'a>; fn next(&mut self) -> Option { - if let 0 | 43 | 44 | 59 | 60 = self.next_header { + if let IpProto::HOPOPTS | IpProto::ROUTING | IpProto::FRAGMENT | IpProto::DESTOPTS = self.next_header { let header = self.next_header; self.next_header = self.buffer[self.pos]; let header_length = ((self.buffer[self.pos + 1] as usize) + 1) * 8; diff --git a/src/lib.rs b/src/lib.rs index c6312a1..df067d4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Alex Forster + Copyright (c) Alex Forster Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/src/tcp.rs b/src/tcp.rs index 5016c6c..4c74d55 100644 --- a/src/tcp.rs +++ b/src/tcp.rs @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Alex Forster + Copyright (c) Alex Forster Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -68,7 +68,7 @@ impl<'a> TcpPdu<'a> { /// Returns the slice of the underlying buffer that contains the header part of this PDU pub fn as_bytes(&'a self) -> &'a [u8] { - self.clone().into_bytes() + (*self).into_bytes() } /// Consumes this object and returns the slice of the underlying buffer that contains the header part of this PDU @@ -78,7 +78,7 @@ impl<'a> TcpPdu<'a> { /// Returns an object representing the inner payload of this PDU pub fn inner(&'a self) -> Result> { - self.clone().into_inner() + (*self).into_inner() } /// Consumes this object and returns an object representing the inner payload of this PDU @@ -88,19 +88,19 @@ impl<'a> TcpPdu<'a> { } pub fn source_port(&'a self) -> u16 { - u16::from_be_bytes(self.buffer[0..=1].try_into().unwrap()) + u16::from_be_bytes(self.buffer[0..2].try_into().unwrap()) } pub fn destination_port(&'a self) -> u16 { - u16::from_be_bytes(self.buffer[2..=3].try_into().unwrap()) + u16::from_be_bytes(self.buffer[2..4].try_into().unwrap()) } pub fn sequence_number(&'a self) -> u32 { - u32::from_be_bytes(self.buffer[4..=7].try_into().unwrap()) + u32::from_be_bytes(self.buffer[4..8].try_into().unwrap()) } pub fn acknowledgement_number(&'a self) -> u32 { - u32::from_be_bytes(self.buffer[8..=11].try_into().unwrap()) + u32::from_be_bytes(self.buffer[8..12].try_into().unwrap()) } pub fn data_offset(&'a self) -> u8 { @@ -148,7 +148,7 @@ impl<'a> TcpPdu<'a> { } pub fn window_size(&'a self) -> u16 { - u16::from_be_bytes(self.buffer[14..=15].try_into().unwrap()) + u16::from_be_bytes(self.buffer[14..16].try_into().unwrap()) } pub fn computed_window_size(&'a self, shift: u8) -> u32 { @@ -156,32 +156,32 @@ impl<'a> TcpPdu<'a> { } pub fn checksum(&'a self) -> u16 { - u16::from_be_bytes(self.buffer[16..=17].try_into().unwrap()) + u16::from_be_bytes(self.buffer[16..18].try_into().unwrap()) } pub fn computed_checksum(&'a self, ip: &crate::Ip) -> u16 { match ip { crate::Ip::Ipv4(ipv4) => util::checksum(&[ - &ipv4.source_address().as_ref(), - &ipv4.destination_address().as_ref(), - &[0x00, ipv4.protocol()].as_ref(), - &(ipv4.total_length() as usize - ipv4.computed_ihl()).to_be_bytes().as_ref(), - &self.buffer[0..=15], + ipv4.source_address().as_ref(), + ipv4.destination_address().as_ref(), + [0x00, ipv4.protocol()].as_ref(), + (ipv4.total_length() as usize - ipv4.computed_ihl()).to_be_bytes().as_ref(), + &self.buffer[0..16], &self.buffer[18..], ]), crate::Ip::Ipv6(ipv6) => util::checksum(&[ - &ipv6.source_address().as_ref(), - &ipv6.destination_address().as_ref(), - &(ipv6.payload_length() as u32).to_be_bytes().as_ref(), - &[0x0, 0x0, 0x0, ipv6.computed_protocol()].as_ref(), - &self.buffer[0..=15], + ipv6.source_address().as_ref(), + ipv6.destination_address().as_ref(), + (ipv6.payload_length() as u32).to_be_bytes().as_ref(), + [0x0, 0x0, 0x0, ipv6.computed_protocol()].as_ref(), + &self.buffer[0..16], &self.buffer[18..], ]), } } pub fn urgent_pointer(&'a self) -> u16 { - u16::from_be_bytes(self.buffer[18..=19].try_into().unwrap()) + u16::from_be_bytes(self.buffer[18..20].try_into().unwrap()) } pub fn options(&'a self) -> TcpOptionIterator<'a> { @@ -235,16 +235,16 @@ impl<'a> Iterator for TcpOptionIterator<'a> { match option { 0 => None, 1 => Some(TcpOption::NoOp), - 2 if len == 4 => Some(TcpOption::Mss { - size: u16::from_be_bytes(self.buffer[pos + 2..=pos + 3].try_into().unwrap()), - }), + 2 if len == 4 => { + Some(TcpOption::Mss { size: u16::from_be_bytes(self.buffer[pos + 2..pos + 4].try_into().unwrap()) }) + } 3 if len == 3 => Some(TcpOption::WindowScale { shift: self.buffer[pos + 2] }), 4 => Some(TcpOption::SackPermitted), 5 if len == 10 => Some(TcpOption::Sack { blocks: [ Some(( - u32::from_be_bytes(self.buffer[pos + 2..=pos + 5].try_into().unwrap()), - u32::from_be_bytes(self.buffer[pos + 6..=pos + 9].try_into().unwrap()), + u32::from_be_bytes(self.buffer[pos + 2..pos + 6].try_into().unwrap()), + u32::from_be_bytes(self.buffer[pos + 6..pos + 10].try_into().unwrap()), )), None, None, @@ -254,12 +254,12 @@ impl<'a> Iterator for TcpOptionIterator<'a> { 5 if len == 18 => Some(TcpOption::Sack { blocks: [ Some(( - u32::from_be_bytes(self.buffer[pos + 2..=pos + 5].try_into().unwrap()), - u32::from_be_bytes(self.buffer[pos + 6..=pos + 9].try_into().unwrap()), + u32::from_be_bytes(self.buffer[pos + 2..pos + 6].try_into().unwrap()), + u32::from_be_bytes(self.buffer[pos + 6..pos + 10].try_into().unwrap()), )), Some(( - u32::from_be_bytes(self.buffer[pos + 10..=pos + 13].try_into().unwrap()), - u32::from_be_bytes(self.buffer[pos + 14..=pos + 17].try_into().unwrap()), + u32::from_be_bytes(self.buffer[pos + 10..pos + 14].try_into().unwrap()), + u32::from_be_bytes(self.buffer[pos + 14..pos + 18].try_into().unwrap()), )), None, None, @@ -268,16 +268,16 @@ impl<'a> Iterator for TcpOptionIterator<'a> { 5 if len == 26 => Some(TcpOption::Sack { blocks: [ Some(( - u32::from_be_bytes(self.buffer[pos + 2..=pos + 5].try_into().unwrap()), - u32::from_be_bytes(self.buffer[pos + 6..=pos + 9].try_into().unwrap()), + u32::from_be_bytes(self.buffer[pos + 2..pos + 6].try_into().unwrap()), + u32::from_be_bytes(self.buffer[pos + 6..pos + 10].try_into().unwrap()), )), Some(( - u32::from_be_bytes(self.buffer[pos + 10..=pos + 13].try_into().unwrap()), - u32::from_be_bytes(self.buffer[pos + 14..=pos + 17].try_into().unwrap()), + u32::from_be_bytes(self.buffer[pos + 10..pos + 14].try_into().unwrap()), + u32::from_be_bytes(self.buffer[pos + 14..pos + 18].try_into().unwrap()), )), Some(( - u32::from_be_bytes(self.buffer[pos + 18..=pos + 21].try_into().unwrap()), - u32::from_be_bytes(self.buffer[pos + 22..=pos + 25].try_into().unwrap()), + u32::from_be_bytes(self.buffer[pos + 18..pos + 22].try_into().unwrap()), + u32::from_be_bytes(self.buffer[pos + 22..pos + 26].try_into().unwrap()), )), None, ], @@ -285,26 +285,26 @@ impl<'a> Iterator for TcpOptionIterator<'a> { 5 if len == 34 => Some(TcpOption::Sack { blocks: [ Some(( - u32::from_be_bytes(self.buffer[pos + 2..=pos + 5].try_into().unwrap()), - u32::from_be_bytes(self.buffer[pos + 6..=pos + 9].try_into().unwrap()), + u32::from_be_bytes(self.buffer[pos + 2..pos + 6].try_into().unwrap()), + u32::from_be_bytes(self.buffer[pos + 6..pos + 10].try_into().unwrap()), )), Some(( - u32::from_be_bytes(self.buffer[pos + 10..=pos + 13].try_into().unwrap()), - u32::from_be_bytes(self.buffer[pos + 14..=pos + 17].try_into().unwrap()), + u32::from_be_bytes(self.buffer[pos + 10..pos + 14].try_into().unwrap()), + u32::from_be_bytes(self.buffer[pos + 14..pos + 18].try_into().unwrap()), )), Some(( - u32::from_be_bytes(self.buffer[pos + 18..=pos + 21].try_into().unwrap()), - u32::from_be_bytes(self.buffer[pos + 22..=pos + 25].try_into().unwrap()), + u32::from_be_bytes(self.buffer[pos + 18..pos + 22].try_into().unwrap()), + u32::from_be_bytes(self.buffer[pos + 22..pos + 26].try_into().unwrap()), )), Some(( - u32::from_be_bytes(self.buffer[pos + 26..=pos + 29].try_into().unwrap()), - u32::from_be_bytes(self.buffer[pos + 30..=pos + 33].try_into().unwrap()), + u32::from_be_bytes(self.buffer[pos + 26..pos + 30].try_into().unwrap()), + u32::from_be_bytes(self.buffer[pos + 30..pos + 34].try_into().unwrap()), )), ], }), 8 if len == 10 => Some(TcpOption::Timestamp { - val: u32::from_be_bytes(self.buffer[pos + 2..=pos + 5].try_into().unwrap()), - ecr: u32::from_be_bytes(self.buffer[pos + 6..=pos + 9].try_into().unwrap()), + val: u32::from_be_bytes(self.buffer[pos + 2..pos + 6].try_into().unwrap()), + ecr: u32::from_be_bytes(self.buffer[pos + 6..pos + 10].try_into().unwrap()), }), _ => Some(TcpOption::Raw { option, data: &self.buffer[pos..(pos + len)] }), } diff --git a/src/udp.rs b/src/udp.rs index 986e012..580b835 100644 --- a/src/udp.rs +++ b/src/udp.rs @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Alex Forster + Copyright (c) Alex Forster Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -55,7 +55,7 @@ impl<'a> UdpPdu<'a> { /// Returns the slice of the underlying buffer that contains the header part of this PDU pub fn as_bytes(&'a self) -> &'a [u8] { - self.clone().into_bytes() + (*self).into_bytes() } /// Consumes this object and returns the slice of the underlying buffer that contains the header part of this PDU @@ -65,7 +65,7 @@ impl<'a> UdpPdu<'a> { /// Returns an object representing the inner payload of this PDU pub fn inner(&'a self) -> Result> { - self.clone().into_inner() + (*self).into_inner() } /// Consumes this object and returns an object representing the inner payload of this PDU @@ -75,37 +75,37 @@ impl<'a> UdpPdu<'a> { } pub fn source_port(&'a self) -> u16 { - u16::from_be_bytes(self.buffer[0..=1].try_into().unwrap()) + u16::from_be_bytes(self.buffer[0..2].try_into().unwrap()) } pub fn destination_port(&'a self) -> u16 { - u16::from_be_bytes(self.buffer[2..=3].try_into().unwrap()) + u16::from_be_bytes(self.buffer[2..4].try_into().unwrap()) } pub fn length(&'a self) -> u16 { - u16::from_be_bytes(self.buffer[4..=5].try_into().unwrap()) + u16::from_be_bytes(self.buffer[4..6].try_into().unwrap()) } pub fn checksum(&'a self) -> u16 { - u16::from_be_bytes(self.buffer[6..=7].try_into().unwrap()) + u16::from_be_bytes(self.buffer[6..8].try_into().unwrap()) } pub fn computed_checksum(&'a self, ip: &crate::Ip) -> u16 { let mut csum = match ip { crate::Ip::Ipv4(ipv4) => util::checksum(&[ - &ipv4.source_address().as_ref(), - &ipv4.destination_address().as_ref(), - &[0x00, ipv4.protocol()].as_ref(), - &self.length().to_be_bytes().as_ref(), - &self.buffer[0..=5], + ipv4.source_address().as_ref(), + ipv4.destination_address().as_ref(), + [0x00, ipv4.protocol()].as_ref(), + self.length().to_be_bytes().as_ref(), + &self.buffer[0..6], &self.buffer[8..], ]), crate::Ip::Ipv6(ipv6) => util::checksum(&[ - &ipv6.source_address().as_ref(), - &ipv6.destination_address().as_ref(), - &(self.length() as u32).to_be_bytes().as_ref(), - &[0x0, 0x0, 0x0, ipv6.computed_protocol()].as_ref(), - &self.buffer[0..=5], + ipv6.source_address().as_ref(), + ipv6.destination_address().as_ref(), + (self.length() as u32).to_be_bytes().as_ref(), + [0x0, 0x0, 0x0, ipv6.computed_protocol()].as_ref(), + &self.buffer[0..6], &self.buffer[8..], ]), }; diff --git a/src/util.rs b/src/util.rs index dc62e38..c404df3 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Alex Forster + Copyright (c) Alex Forster Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -39,14 +39,14 @@ fn sum(mut buffer: &[u8]) -> u16 { while buffer.len() >= 32 { let mut b = &buffer[..32]; while b.len() >= 2 { - accum += u16::from_be_bytes(b[0..=1].try_into().unwrap()) as u32; + accum += u16::from_be_bytes(b[0..2].try_into().unwrap()) as u32; b = &b[2..]; } buffer = &buffer[32..]; } while buffer.len() >= 2 { - accum += u16::from_be_bytes(buffer[0..=1].try_into().unwrap()) as u32; + accum += u16::from_be_bytes(buffer[0..2].try_into().unwrap()) as u32; buffer = &buffer[2..]; } diff --git a/test.sh b/test.sh index 6ff4eaf..d7de5ce 100755 --- a/test.sh +++ b/test.sh @@ -3,9 +3,8 @@ DOCKER="docker" ${DOCKER} build -t pdu-test - <<'EOF' -FROM ubuntu:bionic -ENV LANG=C.UTF-8 \ - LC_ALL=C.UTF-8 +FROM ubuntu:focal +ENV LANG=C.UTF-8 LC_ALL=C.UTF-8 VOLUME /usr/local/src/pdu WORKDIR /usr/local/src/pdu SHELL ["/bin/bash", "-eu", "-o", "pipefail", "-c"] diff --git a/tests/pcaps/connection termination.pcap b/tests/pcaps/connection-termination.pcap similarity index 100% rename from tests/pcaps/connection termination.pcap rename to tests/pcaps/connection-termination.pcap diff --git a/tests/pcaps/gre_and_4over6.pcap b/tests/pcaps/gre-and-4over6.pcap similarity index 100% rename from tests/pcaps/gre_and_4over6.pcap rename to tests/pcaps/gre-and-4over6.pcap diff --git a/tests/pcaps/ICMP_across_dot1q.pcap b/tests/pcaps/icmp-across-dot1q.pcap similarity index 100% rename from tests/pcaps/ICMP_across_dot1q.pcap rename to tests/pcaps/icmp-across-dot1q.pcap diff --git a/tests/pcaps/ICMPv6_echos.pcap b/tests/pcaps/icmpv6-echos.pcap similarity index 100% rename from tests/pcaps/ICMPv6_echos.pcap rename to tests/pcaps/icmpv6-echos.pcap diff --git a/tests/pcaps/PPTP_negotiation.pcap b/tests/pcaps/pptp-negotiation.pcap similarity index 100% rename from tests/pcaps/PPTP_negotiation.pcap rename to tests/pcaps/pptp-negotiation.pcap diff --git a/tests/pcaps/QinQ.pcap.pcap b/tests/pcaps/q-in-q.pcap similarity index 100% rename from tests/pcaps/QinQ.pcap.pcap rename to tests/pcaps/q-in-q.pcap diff --git a/tests/pcaps/TCP_SACK.pcap b/tests/pcaps/tcp-sack.pcap similarity index 100% rename from tests/pcaps/TCP_SACK.pcap rename to tests/pcaps/tcp-sack.pcap diff --git a/tests/tests.rs b/tests/tests.rs index 2d3bebf..5a5d871 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Alex Forster + Copyright (c) Alex Forster Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -26,10 +26,6 @@ use std::path; use std::process::{Command, Stdio}; use std::result::Result; -use base16; -use pcap; -use roxmltree as xml; - fn hex_decode>(length: usize, input: &T) -> Vec { let input = input.as_ref(); let mut padding = Vec::new(); @@ -54,7 +50,9 @@ fn hex_decode>(length: usize, input: &T) -> Vec { } } -fn descendant_value(node: &xml::Node, proto: &str, field: &str, length: usize) -> Result, Box> { +fn descendant_value( + node: &roxmltree::Node, proto: &str, field: &str, length: usize, +) -> Result, Box> { let descendant = node.descendants().find(|n| n.attribute("name") == Some(format!("{}.{}", proto, field).as_str())); eprintln!("{}.{} = {:?}", proto, field, descendant); let descendant = if let Some(descendant) = descendant { @@ -70,7 +68,7 @@ fn descendant_value(node: &xml::Node, proto: &str, field: &str, length: usize) - Ok(hex_decode(length, value)) } -fn descendant_show(node: &xml::Node, proto: &str, field: &str, length: usize) -> Result, Box> { +fn descendant_show(node: &roxmltree::Node, proto: &str, field: &str, length: usize) -> Result, Box> { let descendant = node.descendants().find(|n| n.attribute("name") == Some(format!("{}.{}", proto, field).as_str())); eprintln!("{}.{} = {:?}", proto, field, descendant); let descendant = if let Some(descendant) = descendant { @@ -86,7 +84,7 @@ fn descendant_show(node: &xml::Node, proto: &str, field: &str, length: usize) -> Ok(hex_decode(length, value)) } -fn visit_ethernet_pdu(pdu: &EthernetPdu, mut nodes: VecDeque) -> Result<(), Box> { +fn visit_ethernet_pdu(pdu: &EthernetPdu, mut nodes: VecDeque) -> Result<(), Box> { let node = nodes.pop_front().unwrap(); if node.attribute("name") == Some("_ws.malformed") { return Err("node: malformed".into()); @@ -95,26 +93,38 @@ fn visit_ethernet_pdu(pdu: &EthernetPdu, mut nodes: VecDeque) -> Resu assert_eq!(pdu.destination_address().as_ref(), descendant_value(&node, "eth", "dst", 6)?.as_slice()); assert_eq!(pdu.source_address().as_ref(), descendant_value(&node, "eth", "src", 6)?.as_slice()); - assert_eq!(&pdu.tpid().to_be_bytes(), descendant_value(&node, "eth", "type", 2)?.as_slice()); + assert_eq!(&pdu.ethertype().to_be_bytes(), descendant_value(&node, "eth", "type", 2)?.as_slice()); + + let mut vlan_count = 0; + for _ in node.next_siblings().take_while(|n| n.attribute("name") == Some("vlan")) { + vlan_count += 1; + } - if node.next_sibling_element().and_then(|sibling| sibling.attribute("name")) == Some("vlan") { + let vlan_tags = pdu.vlan_tags(); + let mut last_etype = None; + for _ in 0..vlan_count { let node = nodes.pop_front().unwrap(); assert_eq!(node.attribute("name"), Some("vlan")); - - assert_eq!(&pdu.vlan().unwrap().to_be_bytes(), descendant_value(&node, "vlan", "id", 2)?.as_slice()); - assert_eq!(&pdu.vlan_pcp().unwrap().to_be_bytes(), descendant_value(&node, "vlan", "priority", 1)?.as_slice()); + let vlan_tag = vlan_tags.unwrap().next().unwrap(); + assert_eq!(&vlan_tag.protocol_id.to_be_bytes(), descendant_value(&node, "vlan", "etype", 1)?.as_slice()); + last_etype = Some(vlan_tag.protocol_id.to_be_bytes()); assert_eq!( - (pdu.vlan_dei().unwrap() as u8).to_be_bytes(), - descendant_value(&node, "vlan", "dei", 1)?.as_slice() + &vlan_tag.priority_codepoint.to_be_bytes(), + descendant_value(&node, "vlan", "priority", 1)?.as_slice() ); - assert_eq!(&pdu.ethertype().to_be_bytes(), descendant_value(&node, "vlan", "etype", 2)?.as_slice()); - } else { - assert_eq!(&pdu.ethertype().to_be_bytes(), descendant_value(&node, "eth", "type", 2)?.as_slice()); + assert_eq!((vlan_tag.drop_eligible as u8).to_be_bytes(), descendant_value(&node, "vlan", "dei", 1)?.as_slice()); + } + + if let Some(last_etype) = last_etype { + assert_eq!(&last_etype, descendant_value(&node, "vlan", "id", 2)?.as_slice()); } match pdu.inner() { Ok(ethernet) => match ethernet { - Ethernet::Raw(raw) => Ok(assert_eq!(&pdu.buffer()[pdu.computed_ihl()..], raw)), + Ethernet::Raw(raw) => { + assert_eq!(&pdu.buffer()[pdu.computed_ihl()..], raw); + Ok(()) + } Ethernet::Arp(arp_pdu) => visit_arp_pdu(&arp_pdu, nodes), Ethernet::Ipv4(ipv4_pdu) => visit_ipv4_pdu(&ipv4_pdu, nodes), Ethernet::Ipv6(ipv6_pdu) => visit_ipv6_pdu(&ipv6_pdu, nodes), @@ -123,7 +133,7 @@ fn visit_ethernet_pdu(pdu: &EthernetPdu, mut nodes: VecDeque) -> Resu } } -fn visit_arp_pdu(pdu: &ArpPdu, mut nodes: VecDeque) -> Result<(), Box> { +fn visit_arp_pdu(pdu: &ArpPdu, mut nodes: VecDeque) -> Result<(), Box> { let node = nodes.pop_front().unwrap(); if node.attribute("name") == Some("_ws.malformed") { return Err("node: malformed".into()); @@ -143,7 +153,7 @@ fn visit_arp_pdu(pdu: &ArpPdu, mut nodes: VecDeque) -> Result<(), Box Ok(()) } -fn visit_ipv4_pdu(pdu: &Ipv4Pdu, mut nodes: VecDeque) -> Result<(), Box> { +fn visit_ipv4_pdu(pdu: &Ipv4Pdu, mut nodes: VecDeque) -> Result<(), Box> { let node = nodes.pop_front().unwrap(); if node.attribute("name") == Some("_ws.malformed") { return Err("node: malformed".into()); @@ -151,7 +161,7 @@ fn visit_ipv4_pdu(pdu: &Ipv4Pdu, mut nodes: VecDeque) -> Result<(), B assert_eq!(node.attribute("name"), Some("ip")); assert_eq!(pdu.version().to_be_bytes(), descendant_value(&node, "ip", "version", 1)?.as_slice()); - // wireshark 2.6 ip.hdr_len[value] is not correctly right-shifted by 4 + // wireshark 3.2.3 ip.hdr_len[value] is not correctly right-shifted by 4 //assert_eq!(pdu.ihl().to_be_bytes(), descendant_value(&node, "ip", "hdr_len", 1)?.as_slice()); assert_eq!(pdu.dscp().to_be_bytes(), descendant_value(&node, "ip", "dsfield.dscp", 1)?.as_slice()); assert_eq!(pdu.ecn().to_be_bytes(), descendant_value(&node, "ip", "dsfield.ecn", 1)?.as_slice()); @@ -159,7 +169,8 @@ fn visit_ipv4_pdu(pdu: &Ipv4Pdu, mut nodes: VecDeque) -> Result<(), B assert_eq!(pdu.identification().to_be_bytes(), descendant_value(&node, "ip", "id", 2)?.as_slice()); assert_eq!((pdu.dont_fragment() as u8).to_be_bytes(), descendant_value(&node, "ip", "flags.df", 1)?.as_slice()); assert_eq!((pdu.more_fragments() as u8).to_be_bytes(), descendant_value(&node, "ip", "flags.mf", 1)?.as_slice()); - assert_eq!(pdu.fragment_offset().to_be_bytes(), descendant_value(&node, "ip", "frag_offset", 2)?.as_slice()); + // wireshark 3.2.3 ip.frag_offset[value] is not correctly masked with 0x1FFF + //assert_eq!(pdu.fragment_offset().to_be_bytes(), descendant_value(&node, "ip", "frag_offset", 2)?.as_slice()); assert_eq!(pdu.ttl().to_be_bytes(), descendant_value(&node, "ip", "ttl", 1)?.as_slice()); assert_eq!(pdu.protocol().to_be_bytes(), descendant_value(&node, "ip", "proto", 1)?.as_slice()); assert_eq!(pdu.checksum().to_be_bytes(), descendant_value(&node, "ip", "checksum", 2)?.as_slice()); @@ -170,7 +181,7 @@ fn visit_ipv4_pdu(pdu: &Ipv4Pdu, mut nodes: VecDeque) -> Result<(), B assert_eq!(pdu.destination_address().as_ref(), descendant_value(&node, "ip", "dst", 4)?.as_slice()); if let Some(options) = node.children().find(|n| n.attribute("name") == Some("")) { - let mut options = options.children().filter(|n| n.is_element()).collect::>(); + let mut options = options.children().filter(|n| n.is_element()).collect::>(); for option in pdu.options() { let node = options.pop_front().unwrap(); match option { @@ -187,7 +198,10 @@ fn visit_ipv4_pdu(pdu: &Ipv4Pdu, mut nodes: VecDeque) -> Result<(), B match pdu.inner() { Ok(ipv4) => match ipv4 { - Ipv4::Raw(raw) => Ok(assert_eq!(&pdu.buffer()[pdu.computed_ihl()..], raw)), + Ipv4::Raw(raw) => { + assert_eq!(&pdu.buffer()[pdu.computed_ihl()..], raw); + Ok(()) + } Ipv4::Tcp(tcp_pdu) => visit_tcp_pdu(&tcp_pdu, &Ip::Ipv4(*pdu), nodes), Ipv4::Udp(udp_pdu) => visit_udp_pdu(&udp_pdu, &Ip::Ipv4(*pdu), nodes), Ipv4::Icmp(icmp_pdu) => visit_icmp_pdu(&icmp_pdu, &Ip::Ipv4(*pdu), nodes), @@ -197,7 +211,7 @@ fn visit_ipv4_pdu(pdu: &Ipv4Pdu, mut nodes: VecDeque) -> Result<(), B } } -fn visit_ipv6_pdu(pdu: &Ipv6Pdu, mut nodes: VecDeque) -> Result<(), Box> { +fn visit_ipv6_pdu(pdu: &Ipv6Pdu, mut nodes: VecDeque) -> Result<(), Box> { let node = nodes.pop_front().unwrap(); if node.attribute("name") == Some("_ws.malformed") { return Err("node: malformed".into()); @@ -219,7 +233,7 @@ fn visit_ipv6_pdu(pdu: &Ipv6Pdu, mut nodes: VecDeque) -> Result<(), B pdu.computed_identification().unwrap().to_be_bytes(), descendant_value(&fraghdr, "ipv6", "fraghdr.ident", 4)?.as_slice() ); - // wireshark 2.6 ipv6.fraghdr.offset[value] is not correctly multiplied by 8 + // wireshark 3.2.3 ipv6.fraghdr.offset[value] is not correctly multiplied by 8 //assert_eq!( // pdu.computed_fragment_offset().unwrap().to_be_bytes(), // descendant_value(&fraghdr, "ipv6", "fraghdr.offset", 2)?.as_slice() @@ -232,7 +246,10 @@ fn visit_ipv6_pdu(pdu: &Ipv6Pdu, mut nodes: VecDeque) -> Result<(), B match pdu.inner() { Ok(ipv6) => match ipv6 { - Ipv6::Raw(raw) => Ok(assert_eq!(&pdu.buffer()[pdu.computed_ihl()..], raw)), + Ipv6::Raw(raw) => { + assert_eq!(&pdu.buffer()[pdu.computed_ihl()..], raw); + Ok(()) + } Ipv6::Tcp(tcp_pdu) => visit_tcp_pdu(&tcp_pdu, &Ip::Ipv6(*pdu), nodes), Ipv6::Udp(udp_pdu) => visit_udp_pdu(&udp_pdu, &Ip::Ipv6(*pdu), nodes), Ipv6::Icmp(icmp_pdu) => visit_icmp_pdu(&icmp_pdu, &Ip::Ipv6(*pdu), nodes), @@ -242,7 +259,7 @@ fn visit_ipv6_pdu(pdu: &Ipv6Pdu, mut nodes: VecDeque) -> Result<(), B } } -fn visit_tcp_pdu(pdu: &TcpPdu, ip_pdu: &Ip, mut nodes: VecDeque) -> Result<(), Box> { +fn visit_tcp_pdu(pdu: &TcpPdu, ip_pdu: &Ip, mut nodes: VecDeque) -> Result<(), Box> { let node = nodes.pop_front().unwrap(); if node.attribute("name") == Some("_ws.malformed") { return Err("node: malformed".into()); @@ -253,7 +270,7 @@ fn visit_tcp_pdu(pdu: &TcpPdu, ip_pdu: &Ip, mut nodes: VecDeque) -> R assert_eq!(pdu.destination_port().to_be_bytes(), descendant_value(&node, "tcp", "dstport", 2)?.as_slice()); assert_eq!(pdu.sequence_number().to_be_bytes(), descendant_value(&node, "tcp", "seq", 4)?.as_slice()); assert_eq!(pdu.acknowledgement_number().to_be_bytes(), descendant_value(&node, "tcp", "ack", 4)?.as_slice()); - // wireshark 2.6 tcp.hdr_len[value] is not correctly right-shifted by 4 + // wireshark 3.2.3 tcp.hdr_len[value] is not correctly right-shifted by 4 //assert_eq!(pdu.data_offset().to_be_bytes(), descendant_value(&node, "tcp", "hdr_len", 1)?.as_slice()); assert_eq!(pdu.flags().to_be_bytes(), descendant_value(&node, "tcp", "flags", 1)?.as_slice()); assert_eq!((pdu.fin() as u8).to_be_bytes(), descendant_value(&node, "tcp", "flags.fin", 1)?.as_slice()); @@ -269,7 +286,7 @@ fn visit_tcp_pdu(pdu: &TcpPdu, ip_pdu: &Ip, mut nodes: VecDeque) -> R assert_eq!(pdu.checksum().to_be_bytes(), descendant_value(&node, "tcp", "checksum", 2)?.as_slice()); if descendant_show(&node, "tcp", "checksum.status", 1)?.eq(&[0x01]) { assert_eq!( - pdu.computed_checksum(&ip_pdu).to_be_bytes(), + pdu.computed_checksum(ip_pdu).to_be_bytes(), descendant_value(&node, "tcp", "checksum", 2)?.as_slice() ); } @@ -316,10 +333,10 @@ fn visit_tcp_pdu(pdu: &TcpPdu, ip_pdu: &Ip, mut nodes: VecDeque) -> R "tcp.options.sack" => { if let Some(TcpOption::Sack { blocks }) = options.pop_front() { match blocks { - [Some((l, r)), None, None, None] | - [Some((l, _)), Some((_, r)), None, None] | - [Some((l, _)), Some((_, _)), Some((_, r)), None] | - [Some((l, _)), Some((_, _)), Some((_, _)), Some((_, r))] => { + [Some((l, r)), None, None, None] + | [Some((l, _)), Some((_, r)), None, None] + | [Some((l, _)), Some((_, _)), Some((_, r)), None] + | [Some((l, _)), Some((_, _)), Some((_, _)), Some((_, r))] => { assert_eq!( &l.to_be_bytes(), descendant_value(&node, "tcp", "options.sack_le", 4)?.as_slice() @@ -355,10 +372,16 @@ fn visit_tcp_pdu(pdu: &TcpPdu, ip_pdu: &Ip, mut nodes: VecDeque) -> R assert!(options.is_empty()); - Ok(()) + match pdu.inner() { + Ok(Tcp::Raw(raw)) => { + assert_eq!(&pdu.buffer()[pdu.computed_data_offset()..], raw); + Ok(()) + } + Err(e) => Err(e.into()), + } } -fn visit_udp_pdu(pdu: &UdpPdu, ip_pdu: &Ip, mut nodes: VecDeque) -> Result<(), Box> { +fn visit_udp_pdu(pdu: &UdpPdu, ip_pdu: &Ip, mut nodes: VecDeque) -> Result<(), Box> { let node = nodes.pop_front().unwrap(); if node.attribute("name") == Some("_ws.malformed") { return Err("node: malformed".into()); @@ -371,15 +394,21 @@ fn visit_udp_pdu(pdu: &UdpPdu, ip_pdu: &Ip, mut nodes: VecDeque) -> R assert_eq!(pdu.checksum().to_be_bytes(), descendant_value(&node, "udp", "checksum", 2)?.as_slice()); if descendant_show(&node, "udp", "checksum.status", 1)?.eq(&[0x01]) { assert_eq!( - pdu.computed_checksum(&ip_pdu).to_be_bytes(), + pdu.computed_checksum(ip_pdu).to_be_bytes(), descendant_value(&node, "udp", "checksum", 2)?.as_slice() ); } - Ok(()) + match pdu.inner() { + Ok(Udp::Raw(raw)) => { + assert_eq!(&pdu.buffer()[pdu.computed_data_offset()..], raw); + Ok(()) + } + Err(e) => Err(e.into()), + } } -fn visit_icmp_pdu(pdu: &IcmpPdu, ip_pdu: &Ip, mut nodes: VecDeque) -> Result<(), Box> { +fn visit_icmp_pdu(pdu: &IcmpPdu, ip_pdu: &Ip, mut nodes: VecDeque) -> Result<(), Box> { let node = nodes.pop_front().unwrap(); if node.attribute("name") == Some("_ws.malformed") { return Err("node: malformed".into()); @@ -396,15 +425,21 @@ fn visit_icmp_pdu(pdu: &IcmpPdu, ip_pdu: &Ip, mut nodes: VecDeque) -> assert_eq!(pdu.checksum().to_be_bytes(), descendant_value(&node, proto, "checksum", 2)?.as_slice()); if descendant_show(&node, proto, "checksum.status", 1)?.eq(&[0x01]) { assert_eq!( - pdu.computed_checksum(&ip_pdu).to_be_bytes(), + pdu.computed_checksum(ip_pdu).to_be_bytes(), descendant_value(&node, proto, "checksum", 2)?.as_slice() ); } - Ok(()) + match pdu.inner() { + Ok(Icmp::Raw(raw)) => { + assert_eq!(&pdu.buffer()[pdu.computed_data_offset()..], raw); + Ok(()) + } + Err(e) => Err(e.into()), + } } -fn visit_gre_pdu(pdu: &GrePdu, mut nodes: VecDeque) -> Result<(), Box> { +fn visit_gre_pdu(pdu: &GrePdu, mut nodes: VecDeque) -> Result<(), Box> { let node = nodes.pop_front().unwrap(); if node.attribute("name") == Some("_ws.malformed") { return Err("node: malformed".into()); @@ -437,7 +472,10 @@ fn visit_gre_pdu(pdu: &GrePdu, mut nodes: VecDeque) -> Result<(), Box match pdu.inner() { Ok(gre) => match gre { - Gre::Raw(raw) => Ok(assert_eq!(&pdu.buffer()[pdu.computed_ihl()..], raw)), + Gre::Raw(raw) => { + assert_eq!(&pdu.buffer()[pdu.computed_ihl()..], raw); + Ok(()) + } Gre::Ethernet(ethernet_pdu) => visit_ethernet_pdu(ðernet_pdu, nodes), Gre::Ipv4(ipv4_pdu) => visit_ipv4_pdu(&ipv4_pdu, nodes), Gre::Ipv6(ipv6_pdu) => visit_ipv6_pdu(&ipv6_pdu, nodes), @@ -482,8 +520,8 @@ fn test_pcaps() -> Result<(), Box> { } let dissections = String::from_utf8(output.stdout)?; - let dissections = xml::Document::parse(dissections.as_str())?; - let dissections: Vec = + let dissections = roxmltree::Document::parse(dissections.as_str())?; + let dissections: Vec = dissections.root().first_element_child().unwrap().children().filter(|n| n.is_element()).collect(); let mut pcap = match pcap::Capture::from_file(&pcap_file) { @@ -498,7 +536,7 @@ fn test_pcaps() -> Result<(), Box> { for dissection in dissections.iter() { let data = pcap.next().unwrap().data; i += 1; - let dissections: VecDeque = dissection + let dissections: VecDeque = dissection .children() .filter(|n| n.is_element()) .skip(2) @@ -513,7 +551,7 @@ fn test_pcaps() -> Result<(), Box> { eprintln!("{} (#{})", &pcap_file, i + 1); let first_layer = dissections.front().unwrap().attribute("name"); if first_layer == Some("eth") { - match EthernetPdu::new(&data) { + match EthernetPdu::new(data) { Ok(ethernet_pdu) => match visit_ethernet_pdu(ðernet_pdu, dissections) { Ok(()) => {} Err(e) => { @@ -527,7 +565,7 @@ fn test_pcaps() -> Result<(), Box> { } } } else if first_layer == Some("ip") || first_layer == Some("ipv6") { - match Ip::new(&data) { + match Ip::new(data) { Ok(Ip::Ipv4(ipv4_pdu)) => match visit_ipv4_pdu(&ipv4_pdu, dissections) { Ok(()) => {} Err(e) => {