From dff1c208960d36aa49b1a70560267571eba16ea7 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Thu, 28 Nov 2024 11:07:01 +0100 Subject: [PATCH] fix(tcpdump): partial checksum In some cases, the checksum calculation is offloaded to hardware and only the partial checksum is calculated (only the pseudo-header). The tcpdump example was not taking this into account and was flagging some packets with incorrect checksums. --- src/wire/ip.rs | 22 ++++++++++++++++++---- src/wire/ipv4.rs | 2 +- src/wire/tcp.rs | 19 +++++++++++++++++++ src/wire/udp.rs | 17 +++++++++++++++++ 4 files changed, 55 insertions(+), 5 deletions(-) diff --git a/src/wire/ip.rs b/src/wire/ip.rs index e61541cd0..31d4b9310 100644 --- a/src/wire/ip.rs +++ b/src/wire/ip.rs @@ -779,9 +779,17 @@ pub mod checksum { } // We use this in pretty printer implementations. - pub(crate) fn format_checksum(f: &mut fmt::Formatter, correct: bool) -> fmt::Result { + pub(crate) fn format_checksum( + f: &mut fmt::Formatter, + correct: bool, + partially_correct: bool, + ) -> fmt::Result { if !correct { - write!(f, " (checksum incorrect)") + if partially_correct { + write!(f, " (partial checksum correct)") + } else { + write!(f, " (checksum incorrect)") + } } else { Ok(()) } @@ -833,7 +841,10 @@ pub fn pretty_print_ip_payload>( )?; let valid = udp_packet.verify_checksum(&repr.src_addr(), &repr.dst_addr()); - format_checksum(f, valid) + let partially_valid = udp_packet + .verify_partial_checksum(&repr.src_addr(), &repr.dst_addr()); + + format_checksum(f, valid, partially_valid) } } } @@ -855,7 +866,10 @@ pub fn pretty_print_ip_payload>( write!(f, "{indent}{tcp_repr}")?; let valid = tcp_packet.verify_checksum(&repr.src_addr(), &repr.dst_addr()); - format_checksum(f, valid) + let partially_valid = tcp_packet + .verify_partial_checksum(&repr.src_addr(), &repr.dst_addr()); + + format_checksum(f, valid, partially_valid) } } } diff --git a/src/wire/ipv4.rs b/src/wire/ipv4.rs index 7629a46ed..a9cd07233 100644 --- a/src/wire/ipv4.rs +++ b/src/wire/ipv4.rs @@ -705,7 +705,7 @@ impl> PrettyPrint for Packet { return Ok(()); } else { write!(f, "{indent}{ip_repr}")?; - format_checksum(f, ip_packet.verify_checksum())?; + format_checksum(f, ip_packet.verify_checksum(), false)?; (ip_repr, ip_packet.payload()) } } diff --git a/src/wire/tcp.rs b/src/wire/tcp.rs index e064182d9..81cbaabe2 100644 --- a/src/wire/tcp.rs +++ b/src/wire/tcp.rs @@ -351,6 +351,25 @@ impl> Packet { Ok([None, None, None]) } + /// Validate the partial checksum. + /// + /// # Panics + /// This function panics unless `src_addr` and `dst_addr` belong to the same family, + /// and that family is IPv4 or IPv6. + /// + /// # Fuzzing + /// This function always returns `true` when fuzzing. + pub fn verify_partial_checksum(&self, src_addr: &IpAddress, dst_addr: &IpAddress) -> bool { + if cfg!(fuzzing) { + return true; + } + + let data = self.buffer.as_ref(); + + checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Tcp, data.len() as u32) + == self.checksum() + } + /// Validate the packet checksum. /// /// # Panics diff --git a/src/wire/udp.rs b/src/wire/udp.rs index 9d341e0ee..c35fbae76 100644 --- a/src/wire/udp.rs +++ b/src/wire/udp.rs @@ -101,6 +101,23 @@ impl> Packet { NetworkEndian::read_u16(&data[field::CHECKSUM]) } + /// Validate the partial packet checksum. + /// + /// # Panics + /// This function panics unless `src_addr` and `dst_addr` belong to the same family, + /// and that family is IPv4 or IPv6. + /// + /// # Fuzzing + /// This function always returns `true` when fuzzing. + pub fn verify_partial_checksum(&self, src_addr: &IpAddress, dst_addr: &IpAddress) -> bool { + if cfg!(fuzzing) { + return true; + } + + checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Udp, self.len() as u32) + == self.checksum() + } + /// Validate the packet checksum. /// /// # Panics