Skip to content

Commit 8dd1dd9

Browse files
plumenatorAdenylatcyclase
andauthored
Impl mod destination_unreachable (#128)
Co-authored-by: Adenylatcyclase <[email protected]> Co-authored-by: Karthik Ravikanti <[email protected]>
1 parent e4f542b commit 8dd1dd9

File tree

2 files changed

+225
-0
lines changed

2 files changed

+225
-0
lines changed
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
/*
2+
* Copyright 2019 Comcast Cable Communications Management, LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* SPDX-License-Identifier: Apache-2.0
17+
*/
18+
19+
use crate::packets::icmp::v6::{Icmpv6, Icmpv6Message, Icmpv6Packet, Icmpv6Type, Icmpv6Types};
20+
use crate::packets::ip::v6::{Ipv6Packet, IPV6_MIN_MTU};
21+
use crate::packets::types::u32be;
22+
use crate::packets::{Internal, Packet};
23+
use crate::SizeOf;
24+
use anyhow::Result;
25+
use std::fmt;
26+
use std::ptr::NonNull;
27+
28+
/// Destination Unreachable Message defined in [IETF RFC 4443].
29+
///
30+
/// ```
31+
/// 0 1 2 3
32+
/// 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
33+
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
34+
/// | Type | Code | Checksum |
35+
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
36+
/// | Unused |
37+
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
38+
/// | As much of invoking packet |
39+
/// + as possible without the ICMPv6 packet +
40+
/// | exceeding the minimum IPv6 MTU [IPv6] |
41+
/// ```
42+
///
43+
/// [IETF RFC 4443]: https://tools.ietf.org/html/rfc4443#section-3.1
44+
#[derive(Icmpv6Packet)]
45+
pub struct DestinationUnreachable<E: Ipv6Packet> {
46+
icmp: Icmpv6<E>,
47+
body: NonNull<DestinationUnreachableBody>,
48+
}
49+
50+
impl<E: Ipv6Packet> DestinationUnreachable<E> {
51+
/// Returns the invoking packet as a `u8` slice.
52+
#[inline]
53+
pub fn data(&self) -> &[u8] {
54+
let offset = self.payload_offset() + DestinationUnreachableBody::size_of();
55+
let len = self.payload_len() - DestinationUnreachableBody::size_of();
56+
57+
if let Ok(data) = self.icmp().mbuf().read_data_slice(offset, len) {
58+
unsafe { &*data.as_ptr() }
59+
} else {
60+
&[]
61+
}
62+
}
63+
}
64+
65+
impl<E: Ipv6Packet> fmt::Debug for DestinationUnreachable<E> {
66+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67+
f.debug_struct("DestinationUnreachable")
68+
.field("type", &format!("{}", self.msg_type()))
69+
.field("code", &self.code())
70+
.field("checksum", &format!("0x{:04x}", self.checksum()))
71+
.field("$offset", &self.offset())
72+
.field("$len", &self.len())
73+
.field("$header_len", &self.header_len())
74+
.finish()
75+
}
76+
}
77+
78+
impl<E: Ipv6Packet> Icmpv6Message for DestinationUnreachable<E> {
79+
type Envelope = E;
80+
81+
#[inline]
82+
fn msg_type() -> Icmpv6Type {
83+
Icmpv6Types::DestinationUnreachable
84+
}
85+
86+
#[inline]
87+
fn icmp(&self) -> &Icmpv6<Self::Envelope> {
88+
&self.icmp
89+
}
90+
91+
#[inline]
92+
fn icmp_mut(&mut self) -> &mut Icmpv6<Self::Envelope> {
93+
&mut self.icmp
94+
}
95+
96+
#[inline]
97+
fn into_icmp(self) -> Icmpv6<Self::Envelope> {
98+
self.icmp
99+
}
100+
101+
#[inline]
102+
unsafe fn clone(&self, internal: Internal) -> Self {
103+
DestinationUnreachable {
104+
icmp: self.icmp.clone(internal),
105+
body: self.body,
106+
}
107+
}
108+
109+
/// Parses the ICMPv6 packet's payload as destination unreachable.
110+
///
111+
/// # Errors
112+
///
113+
/// Returns an error if the payload does not have sufficient data for
114+
/// the destination unreachable message body.
115+
#[inline]
116+
fn try_parse(icmp: Icmpv6<Self::Envelope>, _internal: Internal) -> Result<Self> {
117+
let mbuf = icmp.mbuf();
118+
let offset = icmp.payload_offset();
119+
let body = mbuf.read_data(offset)?;
120+
121+
Ok(DestinationUnreachable { icmp, body })
122+
}
123+
124+
/// Prepends a new destination unreachable message to the beginning of the ICMPv6's
125+
/// payload.
126+
///
127+
/// # Errors
128+
///
129+
/// Returns an error if the buffer does not have enough free space.
130+
#[inline]
131+
fn try_push(mut icmp: Icmpv6<Self::Envelope>, _internal: Internal) -> Result<Self> {
132+
let offset = icmp.payload_offset();
133+
let mbuf = icmp.mbuf_mut();
134+
135+
mbuf.extend(offset, DestinationUnreachableBody::size_of())?;
136+
let body = mbuf.write_data(offset, &DestinationUnreachableBody::default())?;
137+
138+
Ok(DestinationUnreachable { icmp, body })
139+
}
140+
141+
/// Reconciles the derivable header fields against the changes made to
142+
/// the packet.
143+
///
144+
/// * the whole packet is truncated so it doesn't exceed the [minimum
145+
/// IPv6 MTU].
146+
/// * [`checksum`] is computed based on the pseudo-header and the
147+
/// `DestinationUnreachable` message.
148+
///
149+
/// [minimum IPv6 MTU]: IPV6_MIN_MTU
150+
/// [`checksum`]: Icmpv6::checksum
151+
#[inline]
152+
fn reconcile(&mut self) {
153+
let _ = self.envelope_mut().truncate(IPV6_MIN_MTU);
154+
self.icmp_mut().compute_checksum();
155+
}
156+
}
157+
158+
#[derive(Clone, Copy, Debug, Default, SizeOf)]
159+
#[repr(C, packed)]
160+
struct DestinationUnreachableBody {
161+
_unused: u32be,
162+
}
163+
164+
#[cfg(test)]
165+
mod tests {
166+
use super::*;
167+
use crate::packets::ip::v6::Ipv6;
168+
use crate::packets::Ethernet;
169+
use crate::testils::byte_arrays::IPV6_TCP_PACKET;
170+
use crate::Mbuf;
171+
172+
#[test]
173+
fn size_of_destination_unreachable_body() {
174+
assert_eq!(4, DestinationUnreachableBody::size_of());
175+
}
176+
177+
#[capsule::test]
178+
fn push_and_set_destination_unreachable() {
179+
let packet = Mbuf::from_bytes(&IPV6_TCP_PACKET).unwrap();
180+
let ethernet = packet.parse::<Ethernet>().unwrap();
181+
let ipv6 = ethernet.parse::<Ipv6>().unwrap();
182+
let tcp_len = ipv6.payload_len();
183+
184+
let mut unreachable = ipv6.push::<DestinationUnreachable<Ipv6>>().unwrap();
185+
186+
assert_eq!(4, unreachable.header_len());
187+
assert_eq!(
188+
DestinationUnreachableBody::size_of() + tcp_len,
189+
unreachable.payload_len()
190+
);
191+
assert_eq!(Icmpv6Types::DestinationUnreachable, unreachable.msg_type());
192+
assert_eq!(0, unreachable.code());
193+
assert_eq!(tcp_len, unreachable.data().len());
194+
195+
unreachable.set_code(1);
196+
assert_eq!(1, unreachable.code());
197+
198+
unreachable.reconcile_all();
199+
assert!(unreachable.checksum() != 0);
200+
}
201+
202+
#[capsule::test]
203+
fn truncate_to_ipv6_min_mtu() {
204+
// starts with a buffer larger than min MTU.
205+
let packet = Mbuf::from_bytes(&[42; 1600]).unwrap();
206+
let ethernet = packet.push::<Ethernet>().unwrap();
207+
let ipv6 = ethernet.push::<Ipv6>().unwrap();
208+
209+
// the max packet len is MTU + Ethernet header
210+
let max_len = IPV6_MIN_MTU + 14;
211+
212+
let mut unreachable = ipv6.push::<DestinationUnreachable<Ipv6>>().unwrap();
213+
assert!(unreachable.mbuf().data_len() > max_len);
214+
215+
unreachable.reconcile_all();
216+
assert_eq!(max_len, unreachable.mbuf().data_len());
217+
}
218+
}

core/src/packets/icmp/v6/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@
1818

1919
//! Internet Control Message Protocol for IPv6.
2020
21+
mod destination_unreachable;
2122
mod echo_reply;
2223
mod echo_request;
2324
pub mod ndp;
2425
mod time_exceeded;
2526
mod too_big;
2627

28+
pub use self::destination_unreachable::*;
2729
pub use self::echo_reply::*;
2830
pub use self::echo_request::*;
2931
pub use self::time_exceeded::*;
@@ -293,6 +295,11 @@ pub mod Icmpv6Types {
293295
/// [Time Exceeded]: crate::packets::icmp::v6::TimeExceeded
294296
pub const TimeExceeded: Icmpv6Type = Icmpv6Type(3);
295297

298+
/// Message type for [Destination Unreachable].
299+
///
300+
/// [Destination Unreachable]: crate::packets::icmp::v6::DestinationUnreachable
301+
pub const DestinationUnreachable: Icmpv6Type = Icmpv6Type(1);
302+
296303
/// Message type for [Echo Request].
297304
///
298305
/// [Echo Request]: crate::packets::icmp::v6::EchoRequest

0 commit comments

Comments
 (0)