packet_formats/icmp/common.rs
1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Common ICMP packets.
6
7use core::num::NonZeroU16;
8
9use zerocopy::byteorder::network_endian::U16;
10use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};
11
12use super::{IcmpZeroCode, IdAndSeq, OriginalPacket};
13
14/// An ICMP Destination Unreachable message.
15#[derive(
16 Copy,
17 Clone,
18 Debug,
19 Default,
20 Eq,
21 PartialEq,
22 KnownLayout,
23 FromBytes,
24 IntoBytes,
25 Immutable,
26 Unaligned,
27)]
28#[repr(C)]
29pub struct IcmpDestUnreachable {
30 // Rest of Header in ICMP, unused in ICMPv6.
31 //
32 // RFC 1191 outlines a method for path MTU discovery for IPv4. When sending a
33 // Destination Unreachable message with code = FragmentationRequired (4) (when
34 // the don't fragment flag is set and the packet size is too big to send out some
35 // link due to its MTU being too small), the RFC requires nodes to include the MTU
36 // of the link that was unable to send the packet in bytes 6 and 7 of the message.
37 // The new ICMP Destination Unreachable message with code = Fragmentation Required
38 // packet format now looks like this (RFC 1191 section 4):
39 //
40 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
41 // | Type = 3 | Code = 4 | Checksum |
42 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
43 // | unused = 0 | Next-Hop MTU |
44 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
45 // | Internet Header + 64 bits of Original Datagram Data |
46 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
47 //
48 // Note, the Next-Hop MTU field is still considered unused in all other ICMP
49 // messages and all nodes that do not implement RFC 1191.
50 _unused: [u8; 2],
51 // Used only when DestinationUnreachable Code = FragmentationRequired (4);
52 //
53 // Note, if this value from an incoming ICMP message is `0`, then we assume the
54 // source node of the ICMP message does not implement RFC 1191 and therefore
55 // does not actually use the Next-Hop MTU field and still considers it as
56 // an unused field.
57 next_hop_mtu: U16,
58 /* Body of IcmpDestUnreachable is entirely variable-length, so is stored in
59 * the message_body field in IcmpPacket */
60}
61
62impl IcmpDestUnreachable {
63 /// Create a new ICMP Destination Unreachable message for a message with
64 /// Code = Fragmentation Required (4) which requires a next hop MTU value
65 /// as defined in RFC 1191 section 4.
66 pub fn new_for_frag_req(mtu: NonZeroU16) -> Self {
67 Self { _unused: [0; 2], next_hop_mtu: U16::new(mtu.get()) }
68 }
69
70 /// Get the Next Hop MTU value as defined in RFC 1191 section 4.
71 ///
72 /// Note, this field is considered unused in all Destination Unreachable
73 /// ICMP messages, except for ICMPv4 Destination Unreachable messages with
74 /// Code = Fragmentation Required (4).
75 pub fn next_hop_mtu(&self) -> Option<NonZeroU16> {
76 NonZeroU16::new(self.next_hop_mtu.get())
77 }
78}
79
80/// An ICMP Echo Request message.
81#[derive(
82 Copy, Clone, Debug, Eq, PartialEq, KnownLayout, FromBytes, IntoBytes, Immutable, Unaligned,
83)]
84#[repr(C)]
85pub struct IcmpEchoRequest {
86 pub(super) id_seq: IdAndSeq,
87 /* The rest of of IcmpEchoRequest is variable-length, so is stored in the
88 * message_body field in IcmpPacket */
89}
90
91impl IcmpEchoRequest {
92 /// Constructs a new `IcmpEchoRequest`.
93 pub fn new(id: u16, seq: u16) -> IcmpEchoRequest {
94 IcmpEchoRequest { id_seq: IdAndSeq::new(id, seq) }
95 }
96
97 /// Constructs an Echo Reply to this Echo Request.
98 ///
99 /// `reply` constructs an `IcmpEchoReply` with the same ID and sequence
100 /// number as the original request.
101 pub fn reply(self) -> IcmpEchoReply {
102 IcmpEchoReply { id_seq: self.id_seq }
103 }
104
105 /// The ID of this message.
106 pub fn id(&self) -> u16 {
107 self.id_seq.id.get()
108 }
109
110 /// Sets the ID of this message.
111 ///
112 /// WARNING: If this message is part of a parsed [`IcmpPacket`], the
113 /// packet's checksum must also be updated accordingly.
114 pub fn set_id(&mut self, id: u16) {
115 self.id_seq.id = id.into();
116 }
117
118 /// The sequence number of this message.
119 pub fn seq(&self) -> u16 {
120 self.id_seq.seq.get()
121 }
122}
123
124/// An ICMP Echo Reply message.
125#[derive(
126 Copy, Clone, Debug, Eq, PartialEq, KnownLayout, FromBytes, IntoBytes, Immutable, Unaligned,
127)]
128#[repr(C)]
129pub struct IcmpEchoReply {
130 pub(super) id_seq: IdAndSeq,
131 /* The rest of of IcmpEchoReply is variable-length, so is stored in the
132 * message_body field in IcmpPacket */
133}
134
135impl IcmpEchoReply {
136 /// Constructs a new `IcmpEchoReply`.
137 pub fn new(id: u16, seq: u16) -> Self {
138 Self { id_seq: IdAndSeq::new(id, seq) }
139 }
140
141 /// The ID of this message.
142 pub fn id(&self) -> u16 {
143 self.id_seq.id.get()
144 }
145
146 /// Sets the ID of this message.
147 ///
148 /// WARNING: If this message is part of a parsed [`IcmpPacket`], the
149 /// packet's checksum must also be updated accordingly.
150 pub fn set_id(&mut self, id: u16) {
151 self.id_seq.id = id.into();
152 }
153
154 /// The sequence number of this message.
155 pub fn seq(&self) -> u16 {
156 self.id_seq.seq.get()
157 }
158}
159
160/// An ICMP Time Exceeded message.
161#[derive(
162 Copy,
163 Clone,
164 Default,
165 Debug,
166 Eq,
167 PartialEq,
168 KnownLayout,
169 FromBytes,
170 IntoBytes,
171 Immutable,
172 Unaligned,
173)]
174#[repr(C)]
175pub struct IcmpTimeExceeded {
176 // Rest of Header in ICMP, unused in ICMPv6
177 _unused: [u8; 4],
178 /* Body of IcmpTimeExceeded is entirely variable-length, so is stored in
179 * the message_body field in IcmpPacket */
180}
181
182impl_common_icmp_message!(IcmpEchoReply, ECHO_REPLY, IcmpZeroCode, OriginalPacket<B>);
183impl_common_icmp_message!(IcmpEchoRequest, ECHO_REQUEST, IcmpZeroCode, OriginalPacket<B>);