From 85eae3c813d7ab3d4320ab67da49d9c6fbdb58bf Mon Sep 17 00:00:00 2001 From: Zeeshan Lakhani Date: Thu, 21 May 2020 18:47:13 -0400 Subject: [PATCH 1/2] icmpv4 time exceeded message Also includes: - doc fixes for links to private fns --- core/src/packets/icmp/v4/mod.rs | 8 + core/src/packets/icmp/v4/time_exceeded.rs | 231 ++++++++++++++++++++++ core/src/packets/icmp/v6/mod.rs | 8 +- core/src/packets/icmp/v6/ndp/redirect.rs | 2 +- core/src/packets/icmp/v6/time_exceeded.rs | 2 +- core/src/packets/icmp/v6/too_big.rs | 2 +- 6 files changed, 246 insertions(+), 7 deletions(-) create mode 100644 core/src/packets/icmp/v4/time_exceeded.rs diff --git a/core/src/packets/icmp/v4/mod.rs b/core/src/packets/icmp/v4/mod.rs index 4ffbe277..07a5a77c 100644 --- a/core/src/packets/icmp/v4/mod.rs +++ b/core/src/packets/icmp/v4/mod.rs @@ -20,9 +20,11 @@ mod echo_reply; mod echo_request; +mod time_exceeded; pub use self::echo_reply::*; pub use self::echo_request::*; +pub use self::time_exceeded::*; pub use capsule_macros::Icmpv4Packet; use crate::packets::ip::v4::Ipv4; @@ -276,6 +278,11 @@ pub mod Icmpv4Types { /// /// [Echo Reply]: crate::packets::icmp::v4::EchoReply pub const EchoReply: Icmpv4Type = Icmpv4Type(0); + + /// Message type for [Time Exceeded]. + /// + /// [Time Exceeded]: crate::packets::icmp::v4::TimeExceeded + pub const TimeExceeded: Icmpv4Type = Icmpv4Type(11); } impl fmt::Display for Icmpv4Type { @@ -286,6 +293,7 @@ impl fmt::Display for Icmpv4Type { match *self { Icmpv4Types::EchoRequest => "Echo Request".to_string(), Icmpv4Types::EchoReply => "Echo Reply".to_string(), + Icmpv4Types::TimeExceeded => "Time Exceeded".to_string(), _ => format!("{}", self.0), } ) diff --git a/core/src/packets/icmp/v4/time_exceeded.rs b/core/src/packets/icmp/v4/time_exceeded.rs new file mode 100644 index 00000000..e97ca8b9 --- /dev/null +++ b/core/src/packets/icmp/v4/time_exceeded.rs @@ -0,0 +1,231 @@ +/* +* Copyright 2019 Comcast Cable Communications Management, LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* SPDX-License-Identifier: Apache-2.0 +*/ + +use crate::packets::icmp::v4::{Icmpv4, Icmpv4Message, Icmpv4Packet, Icmpv4Type, Icmpv4Types}; +use crate::packets::ip::v4::IPV4_MIN_MTU; +use crate::packets::types::u32be; +use crate::packets::{Internal, Packet}; +use crate::SizeOf; +use failure::Fallible; +use std::fmt; +use std::ptr::NonNull; + +/// Time Exceeded Message defined in [IETF RFC 792]. +/// +/// ``` +/// 0 1 2 3 +/// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/// | Type | Code | Checksum | +/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/// | Unused | +/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/// | Internet Header + 64 bits of Original Data Datagram | +/// ``` +/// +/// [IETF RFC 792]: https://tools.ietf.org/html/rfc792 +#[derive(Icmpv4Packet)] +pub struct TimeExceeded { + icmp: Icmpv4, + body: NonNull, +} + +impl TimeExceeded { + /// Returns the offset where the data field in the message body starts. + #[inline] + fn data_offset(&self) -> usize { + self.payload_offset() + TimeExceededBody::size_of() + } + + /// Returns the length of the data field in the message body. + #[inline] + fn data_len(&self) -> usize { + self.payload_len() - TimeExceededBody::size_of() + } + + /// Returns the invoking packet as a `u8` slice. + #[inline] + pub fn data(&self) -> &[u8] { + if let Ok(data) = self + .icmp() + .mbuf() + .read_data_slice(self.data_offset(), self.data_len()) + { + unsafe { &*data.as_ptr() } + } else { + &[] + } + } +} + +impl fmt::Debug for TimeExceeded { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TimeExceeded") + .field("type", &format!("{}", self.msg_type())) + .field("code", &self.code()) + .field("checksum", &format!("0x{:04x}", self.checksum())) + .field("$offset", &self.offset()) + .field("$len", &self.len()) + .field("$header_len", &self.header_len()) + .finish() + } +} + +impl Icmpv4Message for TimeExceeded { + #[inline] + fn msg_type() -> Icmpv4Type { + Icmpv4Types::TimeExceeded + } + + #[inline] + fn icmp(&self) -> &Icmpv4 { + &self.icmp + } + + #[inline] + fn icmp_mut(&mut self) -> &mut Icmpv4 { + &mut self.icmp + } + + #[inline] + fn into_icmp(self) -> Icmpv4 { + self.icmp + } + + #[inline] + unsafe fn clone(&self, internal: Internal) -> Self { + TimeExceeded { + icmp: self.icmp.clone(internal), + body: self.body, + } + } + + #[inline] + fn try_parse(icmp: Icmpv4, _internal: Internal) -> Fallible { + let mbuf = icmp.mbuf(); + let offset = icmp.payload_offset(); + let body = mbuf.read_data(offset)?; + + Ok(TimeExceeded { icmp, body }) + } + + #[inline] + fn try_push(mut icmp: Icmpv4, _internal: Internal) -> Fallible { + let offset = icmp.payload_offset(); + let mbuf = icmp.mbuf_mut(); + + mbuf.extend(offset, TimeExceededBody::size_of())?; + let body = mbuf.write_data(offset, &TimeExceededBody::default())?; + + Ok(TimeExceeded { icmp, body }) + } + + /// Reconciles the derivable header fields against the changes made to + /// the packet. + /// + /// * the data field in the message body is trimmed if it exceeds the + /// [minimum IPV4 MTU], as we only need enough for port information. + /// * [`checksum`] is computed based on the `TimeExceeded` message. + /// + /// [minimum IPv4 MTU]: IPV4_MIN_MTU + /// [`checksum`]: Icmpv4::checksum + #[inline] + fn reconcile(&mut self) { + let len = self.data_len(); + let offset = self.data_offset(); + + if len > IPV4_MIN_MTU { + let _ = self + .mbuf_mut() + .shrink(offset + IPV4_MIN_MTU, len - IPV4_MIN_MTU); + } + + self.icmp_mut().compute_checksum(); + } +} + +#[derive(Clone, Copy, Debug, Default, SizeOf)] +#[repr(C, packed)] +struct TimeExceededBody { + _unused: u32be, +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::packets::ip::v4::Ipv4; + use crate::packets::Ethernet; + use crate::testils::byte_arrays::IPV4_TCP_PACKET; + use crate::Mbuf; + + #[test] + fn size_of_time_exceeded_body() { + assert_eq!(4, TimeExceededBody::size_of()); + } + + #[capsule::test] + fn push_and_set_time_exceeded() { + let packet = Mbuf::from_bytes(&IPV4_TCP_PACKET).unwrap(); + let ethernet = packet.parse::().unwrap(); + let ipv4 = ethernet.parse::().unwrap(); + let tcp_len = ipv4.payload_len(); + + let mut exceeded = ipv4.push::().unwrap(); + + assert_eq!(4, exceeded.header_len()); + assert_eq!( + TimeExceededBody::size_of() + tcp_len, + exceeded.payload_len() + ); + assert_eq!(Icmpv4Types::TimeExceeded, exceeded.msg_type()); + assert_eq!(0, exceeded.code()); + assert_eq!(tcp_len, exceeded.data().len()); + + exceeded.set_code(1); + assert_eq!(1, exceeded.code()); + + exceeded.reconcile_all(); + assert!(exceeded.checksum() != 0); + } + + #[capsule::test] + fn shrinks_to_ipv4_min_mtu() { + // starts with a buffer with a message body larger than min MTU. + let packet = Mbuf::from_bytes(&[42; 100]).unwrap(); + let ethernet = packet.push::().unwrap(); + let ipv4 = ethernet.push::().unwrap(); + let mut exceeded = ipv4.push::().unwrap(); + assert!(exceeded.data_len() > IPV4_MIN_MTU); + + exceeded.reconcile_all(); + assert_eq!(IPV4_MIN_MTU, exceeded.data_len()); + } + + #[capsule::test] + fn message_body_no_shrink() { + // starts with a buffer with a message body smaller than min MTU. + let packet = Mbuf::from_bytes(&[42; 50]).unwrap(); + let ethernet = packet.push::().unwrap(); + let ipv4 = ethernet.push::().unwrap(); + let mut exceeded = ipv4.push::().unwrap(); + assert!(exceeded.data_len() < IPV4_MIN_MTU); + + exceeded.reconcile_all(); + assert_eq!(50, exceeded.data_len()); + } +} diff --git a/core/src/packets/icmp/v6/mod.rs b/core/src/packets/icmp/v6/mod.rs index d5e681e3..038a5bfa 100644 --- a/core/src/packets/icmp/v6/mod.rs +++ b/core/src/packets/icmp/v6/mod.rs @@ -252,7 +252,7 @@ impl Packet for Icmpv6 { /// * [`checksum`] is computed based on the [`pseudo-header`] and the /// full packet. /// - /// [`checksum`]: Icmpv6::checksum + /// [`checksum`]: Icmpv6Packet::checksum /// [`pseudo-header`]: crate::packets::checksum::PseudoHeader #[inline] fn reconcile(&mut self) { @@ -412,7 +412,7 @@ pub trait Icmpv6Message { /// the packet matches the assigned number before invoking this function. /// /// [`Icmpv6::downcast`]: Icmpv6::downcast - /// [`msg_type`]: Icmpv6::msg_type + /// [`msg_type`]: Icmpv6Packet::msg_type fn try_parse(icmp: Icmpv6, internal: Internal) -> Fallible where Self: Sized; @@ -429,7 +429,7 @@ pub trait Icmpv6Message { /// This function cannot be invoked directly. It is internally used by /// [`Packet::push`]. /// - /// [`msg_type`]: Icmpv6::msg_type + /// [`msg_type`]: Icmpv6Packet::msg_type /// [`Packet::push`]: Packet::push fn try_push(icmp: Icmpv6, internal: Internal) -> Fallible where @@ -439,7 +439,7 @@ pub trait Icmpv6Message { /// the packet. The default implementation computes the [`checksum`] /// based on the pseudo-header and the ICMPv6 message. /// - /// [`checksum`]: Icmpv6::checksum + /// [`checksum`]: Icmpv6Packet::checksum #[inline] fn reconcile(&mut self) { self.icmp_mut().compute_checksum() diff --git a/core/src/packets/icmp/v6/ndp/redirect.rs b/core/src/packets/icmp/v6/ndp/redirect.rs index 2e8bdffa..0c24fc4d 100644 --- a/core/src/packets/icmp/v6/ndp/redirect.rs +++ b/core/src/packets/icmp/v6/ndp/redirect.rs @@ -184,7 +184,7 @@ impl Icmpv6Message for Redirect { /// is set to account for the original IP header and data. /// /// [minimum IPv6 MTU]: IPV6_MIN_MTU - /// [`checksum`]: Icmpv6::checksum + /// [`checksum`]: Icmpv6Packet::checksum /// [`RedirectedHeader`]: RedirectedHeader #[inline] fn reconcile(&mut self) { diff --git a/core/src/packets/icmp/v6/time_exceeded.rs b/core/src/packets/icmp/v6/time_exceeded.rs index ebdb24e0..324d2e73 100644 --- a/core/src/packets/icmp/v6/time_exceeded.rs +++ b/core/src/packets/icmp/v6/time_exceeded.rs @@ -135,7 +135,7 @@ impl Icmpv6Message for TimeExceeded { /// `TimeExceeded` message. /// /// [minimum IPv6 MTU]: IPV6_MIN_MTU - /// [`checksum`]: Icmpv6::checksum + /// [`checksum`]: Icmpv6Packet::checksum #[inline] fn reconcile(&mut self) { let _ = self.envelope_mut().truncate(IPV6_MIN_MTU); diff --git a/core/src/packets/icmp/v6/too_big.rs b/core/src/packets/icmp/v6/too_big.rs index cccda817..7fe227ec 100644 --- a/core/src/packets/icmp/v6/too_big.rs +++ b/core/src/packets/icmp/v6/too_big.rs @@ -160,7 +160,7 @@ impl Icmpv6Message for PacketTooBig { /// `PacketTooBig` message. /// /// [minimum IPv6 MTU]: IPV6_MIN_MTU - /// [`checksum`]: Icmpv6::checksum + /// [`checksum`]: Icmpv6Packet::checksum #[inline] fn reconcile(&mut self) { let _ = self.envelope_mut().truncate(IPV6_MIN_MTU); From ba9e792511d3328b3398395875bff9c3e3cebbbf Mon Sep 17 00:00:00 2001 From: Zeeshan Lakhani Date: Fri, 22 May 2020 11:52:10 -0400 Subject: [PATCH 2/2] change fns to pub --- core/src/packets/icmp/v6/mod.rs | 18 +++++++++--------- core/src/packets/icmp/v6/ndp/redirect.rs | 2 +- core/src/packets/icmp/v6/time_exceeded.rs | 2 +- core/src/packets/icmp/v6/too_big.rs | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/core/src/packets/icmp/v6/mod.rs b/core/src/packets/icmp/v6/mod.rs index 038a5bfa..4d0c3508 100644 --- a/core/src/packets/icmp/v6/mod.rs +++ b/core/src/packets/icmp/v6/mod.rs @@ -94,31 +94,31 @@ impl Icmpv6 { /// Returns the message type. #[inline] - fn msg_type(&self) -> Icmpv6Type { + pub fn msg_type(&self) -> Icmpv6Type { Icmpv6Type::new(self.header().msg_type) } /// Returns the code. #[inline] - fn code(&self) -> u8 { + pub fn code(&self) -> u8 { self.header().code } /// Sets the code. #[inline] - fn set_code(&mut self, code: u8) { + pub fn set_code(&mut self, code: u8) { self.header_mut().code = code } /// Returns the checksum. #[inline] - fn checksum(&self) -> u16 { + pub fn checksum(&self) -> u16 { self.header().checksum.into() } /// Computes the checksum. #[inline] - fn compute_checksum(&mut self) { + pub fn compute_checksum(&mut self) { self.header_mut().checksum = u16be::default(); if let Ok(data) = self.mbuf().read_data_slice(self.offset(), self.len()) { @@ -252,7 +252,7 @@ impl Packet for Icmpv6 { /// * [`checksum`] is computed based on the [`pseudo-header`] and the /// full packet. /// - /// [`checksum`]: Icmpv6Packet::checksum + /// [`checksum`]: Icmpv6::checksum /// [`pseudo-header`]: crate::packets::checksum::PseudoHeader #[inline] fn reconcile(&mut self) { @@ -412,7 +412,7 @@ pub trait Icmpv6Message { /// the packet matches the assigned number before invoking this function. /// /// [`Icmpv6::downcast`]: Icmpv6::downcast - /// [`msg_type`]: Icmpv6Packet::msg_type + /// [`msg_type`]: Icmpv6::msg_type fn try_parse(icmp: Icmpv6, internal: Internal) -> Fallible where Self: Sized; @@ -429,7 +429,7 @@ pub trait Icmpv6Message { /// This function cannot be invoked directly. It is internally used by /// [`Packet::push`]. /// - /// [`msg_type`]: Icmpv6Packet::msg_type + /// [`msg_type`]: Icmpv6::msg_type /// [`Packet::push`]: Packet::push fn try_push(icmp: Icmpv6, internal: Internal) -> Fallible where @@ -439,7 +439,7 @@ pub trait Icmpv6Message { /// the packet. The default implementation computes the [`checksum`] /// based on the pseudo-header and the ICMPv6 message. /// - /// [`checksum`]: Icmpv6Packet::checksum + /// [`checksum`]: Icmpv6::checksum #[inline] fn reconcile(&mut self) { self.icmp_mut().compute_checksum() diff --git a/core/src/packets/icmp/v6/ndp/redirect.rs b/core/src/packets/icmp/v6/ndp/redirect.rs index 0c24fc4d..2e8bdffa 100644 --- a/core/src/packets/icmp/v6/ndp/redirect.rs +++ b/core/src/packets/icmp/v6/ndp/redirect.rs @@ -184,7 +184,7 @@ impl Icmpv6Message for Redirect { /// is set to account for the original IP header and data. /// /// [minimum IPv6 MTU]: IPV6_MIN_MTU - /// [`checksum`]: Icmpv6Packet::checksum + /// [`checksum`]: Icmpv6::checksum /// [`RedirectedHeader`]: RedirectedHeader #[inline] fn reconcile(&mut self) { diff --git a/core/src/packets/icmp/v6/time_exceeded.rs b/core/src/packets/icmp/v6/time_exceeded.rs index 324d2e73..ebdb24e0 100644 --- a/core/src/packets/icmp/v6/time_exceeded.rs +++ b/core/src/packets/icmp/v6/time_exceeded.rs @@ -135,7 +135,7 @@ impl Icmpv6Message for TimeExceeded { /// `TimeExceeded` message. /// /// [minimum IPv6 MTU]: IPV6_MIN_MTU - /// [`checksum`]: Icmpv6Packet::checksum + /// [`checksum`]: Icmpv6::checksum #[inline] fn reconcile(&mut self) { let _ = self.envelope_mut().truncate(IPV6_MIN_MTU); diff --git a/core/src/packets/icmp/v6/too_big.rs b/core/src/packets/icmp/v6/too_big.rs index 7fe227ec..cccda817 100644 --- a/core/src/packets/icmp/v6/too_big.rs +++ b/core/src/packets/icmp/v6/too_big.rs @@ -160,7 +160,7 @@ impl Icmpv6Message for PacketTooBig { /// `PacketTooBig` message. /// /// [minimum IPv6 MTU]: IPV6_MIN_MTU - /// [`checksum`]: Icmpv6Packet::checksum + /// [`checksum`]: Icmpv6::checksum #[inline] fn reconcile(&mut self) { let _ = self.envelope_mut().truncate(IPV6_MIN_MTU);