1use core::fmt;
8
9use net_types::ip::{GenericOverIp, Ipv6, Ipv6Addr};
10use packet::{BufferView, ParsablePacket, ParseMetadata};
11use zerocopy::byteorder::network_endian::U32;
12use zerocopy::{
13 FromBytes, Immutable, IntoBytes, KnownLayout, SplitByteSlice, SplitByteSliceMut, Unaligned,
14};
15
16use crate::error::{ParseError, ParseResult};
17
18use super::common::{IcmpDestUnreachable, IcmpEchoReply, IcmpEchoRequest, IcmpTimeExceeded};
19use super::{
20 mld, ndp, peek_message_type, HeaderPrefix, IcmpIpExt, IcmpMessageType, IcmpPacket,
21 IcmpPacketRaw, IcmpParseArgs, IcmpZeroCode, OriginalPacket,
22};
23
24#[macro_export]
35macro_rules! icmpv6_dispatch {
36 (__internal__, $x:ident, $variable:pat => $expression:expr, $($typ:ty),+) => {
37 {
38 $(
39 use $typ::*;
40 )+
41
42 match $x {
43 DestUnreachable($variable) => $expression,
44 PacketTooBig($variable) => $expression,
45 TimeExceeded($variable) => $expression,
46 ParameterProblem($variable) => $expression,
47 EchoRequest($variable) => $expression,
48 EchoReply($variable) => $expression,
49 Ndp(RouterSolicitation($variable)) => $expression,
50 Ndp(RouterAdvertisement($variable)) => $expression,
51 Ndp(NeighborSolicitation($variable)) => $expression,
52 Ndp(NeighborAdvertisement($variable)) => $expression,
53 Ndp(Redirect($variable)) => $expression,
54 Mld(MulticastListenerQuery($variable)) => $expression,
55 Mld(MulticastListenerReport($variable)) => $expression,
56 Mld(MulticastListenerDone($variable)) => $expression,
57 Mld(MulticastListenerQueryV2($variable)) => $expression,
58 Mld(MulticastListenerReportV2($variable)) => $expression,
59 }
60 }
61 };
62 ($x:ident : raw, $variable:pat => $expression:expr) => {
63 $crate::icmpv6_dispatch!(__internal__, $x,
64 $variable => $expression,
65 $crate::icmp::Icmpv6PacketRaw,
66 $crate::icmp::mld::MldPacketRaw,
67 $crate::icmp::ndp::NdpPacketRaw)
68 };
69 ($x:ident, $variable:pat => $expression:expr) => {
70 $crate::icmpv6_dispatch!(__internal__, $x,
71 $variable => $expression,
72 $crate::icmp::Icmpv6Packet,
73 $crate::icmp::mld::MldPacket,
74 $crate::icmp::ndp::NdpPacket)
75 };
76}
77
78#[allow(missing_docs)]
86pub enum Icmpv6Packet<B: SplitByteSlice> {
87 DestUnreachable(IcmpPacket<Ipv6, B, IcmpDestUnreachable>),
88 PacketTooBig(IcmpPacket<Ipv6, B, Icmpv6PacketTooBig>),
89 TimeExceeded(IcmpPacket<Ipv6, B, IcmpTimeExceeded>),
90 ParameterProblem(IcmpPacket<Ipv6, B, Icmpv6ParameterProblem>),
91 EchoRequest(IcmpPacket<Ipv6, B, IcmpEchoRequest>),
92 EchoReply(IcmpPacket<Ipv6, B, IcmpEchoReply>),
93 Ndp(ndp::NdpPacket<B>),
94 Mld(mld::MldPacket<B>),
95}
96
97impl<B: SplitByteSlice + fmt::Debug> fmt::Debug for Icmpv6Packet<B> {
98 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99 use self::Icmpv6Packet::*;
100 use mld::MldPacket::*;
101 use ndp::NdpPacket::*;
102 match self {
103 DestUnreachable(ref p) => f.debug_tuple("DestUnreachable").field(p).finish(),
104 PacketTooBig(ref p) => f.debug_tuple("PacketTooBig").field(p).finish(),
105 TimeExceeded(ref p) => f.debug_tuple("TimeExceeded").field(p).finish(),
106 ParameterProblem(ref p) => f.debug_tuple("ParameterProblem").field(p).finish(),
107 EchoRequest(ref p) => f.debug_tuple("EchoRequest").field(p).finish(),
108 EchoReply(ref p) => f.debug_tuple("EchoReply").field(p).finish(),
109 Ndp(RouterSolicitation(ref p)) => f.debug_tuple("RouterSolicitation").field(p).finish(),
110 Ndp(RouterAdvertisement(ref p)) => {
111 f.debug_tuple("RouterAdvertisement").field(p).finish()
112 }
113 Ndp(NeighborSolicitation(ref p)) => {
114 f.debug_tuple("NeighborSolicitation").field(p).finish()
115 }
116 Ndp(NeighborAdvertisement(ref p)) => {
117 f.debug_tuple("NeighborAdvertisement").field(p).finish()
118 }
119 Ndp(Redirect(ref p)) => f.debug_tuple("Redirect").field(p).finish(),
120 Mld(MulticastListenerQuery(ref p)) => {
121 f.debug_tuple("MulticastListenerQuery").field(p).finish()
122 }
123 Mld(MulticastListenerReport(ref p)) => {
124 f.debug_tuple("MulticastListenerReport").field(p).finish()
125 }
126 Mld(MulticastListenerDone(ref p)) => {
127 f.debug_tuple("MulticastListenerDone").field(p).finish()
128 }
129 Mld(MulticastListenerQueryV2(ref p)) => {
130 f.debug_tuple("MulticastListenerQueryV2").field(p).finish()
131 }
132 Mld(MulticastListenerReportV2(ref p)) => {
133 f.debug_tuple("MulticastListenerReportV2").field(p).finish()
134 }
135 }
136 }
137}
138
139impl<B: SplitByteSlice> ParsablePacket<B, IcmpParseArgs<Ipv6Addr>> for Icmpv6Packet<B> {
140 type Error = ParseError;
141
142 fn parse_metadata(&self) -> ParseMetadata {
143 icmpv6_dispatch!(self, p => p.parse_metadata())
144 }
145
146 fn parse<BV: BufferView<B>>(buffer: BV, args: IcmpParseArgs<Ipv6Addr>) -> ParseResult<Self> {
147 use self::Icmpv6Packet::*;
148 use mld::MldPacket::*;
149 use ndp::NdpPacket::*;
150
151 macro_rules! mtch {
152 ($buffer:expr, $args:expr, $pkt_name:ident, $( ($msg_variant:ident, $len_pat:pat) => $pkt_variant:expr => $type:ty,)*) => {
153 match ( peek_message_type($buffer.as_ref())?, $buffer.len() ) {
154 $( (Icmpv6MessageType::$msg_variant, $len_pat) => {
155 let $pkt_name = <IcmpPacket<Ipv6, B, $type> as ParsablePacket<_, _>>::parse($buffer, $args)?;
156 $pkt_variant
157 })*
158 }
159 }
160 }
161
162 Ok(mtch!(
163 buffer,
164 args,
165 packet,
166 (DestUnreachable, ..) => DestUnreachable(packet) => IcmpDestUnreachable,
167 (PacketTooBig, ..) => PacketTooBig(packet) => Icmpv6PacketTooBig,
168 (TimeExceeded, ..) => TimeExceeded(packet) => IcmpTimeExceeded,
169 (ParameterProblem, ..) => ParameterProblem(packet) => Icmpv6ParameterProblem,
170 (EchoRequest, ..) => EchoRequest(packet) => IcmpEchoRequest,
171 (EchoReply, ..) => EchoReply(packet) => IcmpEchoReply,
172 (RouterSolicitation, ..) => Ndp(RouterSolicitation(packet)) => ndp::RouterSolicitation,
173 (RouterAdvertisement, ..) => Ndp(RouterAdvertisement(packet)) => ndp::RouterAdvertisement,
174 (NeighborSolicitation, ..) => Ndp(NeighborSolicitation(packet)) => ndp::NeighborSolicitation,
175 (NeighborAdvertisement, ..) => Ndp(NeighborAdvertisement(packet)) => ndp::NeighborAdvertisement,
176 (Redirect, ..) => Ndp(Redirect(packet)) => ndp::Redirect,
177 (MulticastListenerQuery, 0..=27 ) => Mld(MulticastListenerQuery(packet)) => mld::MulticastListenerQuery,
178 (MulticastListenerQuery, 28..) => Mld(MulticastListenerQueryV2(packet)) => mld::MulticastListenerQueryV2,
179 (MulticastListenerReport, ..) => Mld(MulticastListenerReport(packet)) => mld::MulticastListenerReport,
180 (MulticastListenerReportV2, ..) => Mld(MulticastListenerReportV2(packet)) => mld::MulticastListenerReportV2,
181 (MulticastListenerDone, ..) => Mld(MulticastListenerDone(packet)) => mld::MulticastListenerDone,
182 ))
183 }
184}
185
186#[allow(missing_docs)]
194pub enum Icmpv6PacketRaw<B: SplitByteSlice> {
195 DestUnreachable(IcmpPacketRaw<Ipv6, B, IcmpDestUnreachable>),
196 PacketTooBig(IcmpPacketRaw<Ipv6, B, Icmpv6PacketTooBig>),
197 TimeExceeded(IcmpPacketRaw<Ipv6, B, IcmpTimeExceeded>),
198 ParameterProblem(IcmpPacketRaw<Ipv6, B, Icmpv6ParameterProblem>),
199 EchoRequest(IcmpPacketRaw<Ipv6, B, IcmpEchoRequest>),
200 EchoReply(IcmpPacketRaw<Ipv6, B, IcmpEchoReply>),
201 Ndp(ndp::NdpPacketRaw<B>),
202 Mld(mld::MldPacketRaw<B>),
203}
204
205impl<B: SplitByteSliceMut> Icmpv6PacketRaw<B> {
206 pub(super) fn header_prefix_mut(&mut self) -> &mut HeaderPrefix {
207 icmpv6_dispatch!(self: raw, p => &mut p.header.prefix)
208 }
209
210 pub(crate) fn overwrite_checksum(&mut self, checksum: [u8; 2]) -> [u8; 2] {
212 core::mem::replace(&mut self.header_prefix_mut().checksum, checksum)
213 }
214
215 pub fn try_write_checksum(&mut self, src_ip: Ipv6Addr, dst_ip: Ipv6Addr) -> bool {
220 icmpv6_dispatch!(self: raw, p => p.try_write_checksum(src_ip, dst_ip))
221 }
222}
223
224impl<B: SplitByteSlice> ParsablePacket<B, ()> for Icmpv6PacketRaw<B> {
225 type Error = ParseError;
226
227 fn parse_metadata(&self) -> ParseMetadata {
228 icmpv6_dispatch!(self: raw, p => p.parse_metadata())
229 }
230
231 fn parse<BV: BufferView<B>>(buffer: BV, _args: ()) -> ParseResult<Self> {
232 use self::Icmpv6PacketRaw::*;
233 use mld::MldPacketRaw::*;
234 use ndp::NdpPacketRaw::*;
235
236 macro_rules! mtch {
237 ($buffer:expr, $pkt_name:ident, $( ($msg_variant:ident, $len_pat:pat) => $pkt_variant:expr => $type:ty,)*) => {
238 match ( peek_message_type($buffer.as_ref())?, $buffer.len() ) {
239 $( (Icmpv6MessageType::$msg_variant, $len_pat) => {
240 let $pkt_name = <IcmpPacketRaw<Ipv6, B, $type> as ParsablePacket<_, _>>::parse($buffer, ())?;
241 $pkt_variant
242 })*
243 }
244 }
245 }
246
247 Ok(mtch!(
248 buffer,
249 packet,
250 (DestUnreachable, ..) => DestUnreachable(packet) => IcmpDestUnreachable,
251 (PacketTooBig, ..) => PacketTooBig(packet) => Icmpv6PacketTooBig,
252 (TimeExceeded, ..) => TimeExceeded(packet) => IcmpTimeExceeded,
253 (ParameterProblem, ..) => ParameterProblem(packet) => Icmpv6ParameterProblem,
254 (EchoRequest, ..) => EchoRequest(packet) => IcmpEchoRequest,
255 (EchoReply, ..) => EchoReply(packet) => IcmpEchoReply,
256 (RouterSolicitation, ..) => Ndp(RouterSolicitation(packet)) => ndp::RouterSolicitation,
257 (RouterAdvertisement, ..) => Ndp(RouterAdvertisement(packet)) => ndp::RouterAdvertisement,
258 (NeighborSolicitation, ..) => Ndp(NeighborSolicitation(packet)) => ndp::NeighborSolicitation,
259 (NeighborAdvertisement, ..) => Ndp(NeighborAdvertisement(packet)) => ndp::NeighborAdvertisement,
260 (Redirect, ..) => Ndp(Redirect(packet)) => ndp::Redirect,
261 (MulticastListenerQuery, 0..=27 ) => Mld(MulticastListenerQuery(packet)) => mld::MulticastListenerQuery,
262 (MulticastListenerQuery, 28..) => Mld(MulticastListenerQueryV2(packet)) => mld::MulticastListenerQueryV2,
263 (MulticastListenerReport, ..) => Mld(MulticastListenerReport(packet)) => mld::MulticastListenerReport,
264 (MulticastListenerReportV2, ..) => Mld(MulticastListenerReportV2(packet)) => mld::MulticastListenerReportV2,
265 (MulticastListenerDone, ..) => Mld(MulticastListenerDone(packet)) => mld::MulticastListenerDone,
266 ))
267 }
268}
269
270create_protocol_enum!(
271 #[allow(missing_docs)]
272 #[derive(Copy, Clone, PartialEq, Eq)]
273 pub enum Icmpv6MessageType: u8 {
274 DestUnreachable, 1, "Destination Unreachable";
275 PacketTooBig, 2, "Packet Too Big";
276 TimeExceeded, 3, "Time Exceeded";
277 ParameterProblem, 4, "Parameter Problem";
278 EchoRequest, 128, "Echo Request";
279 EchoReply, 129, "Echo Reply";
280
281 RouterSolicitation, 133, "Router Solicitation";
283 RouterAdvertisement, 134, "Router Advertisement";
284 NeighborSolicitation, 135, "Neighbor Solicitation";
285 NeighborAdvertisement, 136, "Neighbor Advertisement";
286 Redirect, 137, "Redirect";
287
288 MulticastListenerQuery, 130, "Multicast Listener Query";
291 MulticastListenerReport, 131, "Multicast Listener Report";
292 MulticastListenerDone, 132, "Multicast Listener Done";
293
294 MulticastListenerReportV2, 143, "Multicast Listener Report V2";
296 }
297);
298
299impl<I: IcmpIpExt> GenericOverIp<I> for Icmpv6MessageType {
300 type Type = I::IcmpMessageType;
301}
302
303impl IcmpMessageType for Icmpv6MessageType {
304 fn is_err(self) -> bool {
305 use Icmpv6MessageType::*;
306 [DestUnreachable, PacketTooBig, TimeExceeded, ParameterProblem].contains(&self)
307 }
308}
309
310create_protocol_enum!(
311 #[allow(missing_docs)]
312 #[derive(Copy, Clone, PartialEq, Eq)]
313 pub enum Icmpv6DestUnreachableCode: u8 {
314 NoRoute, 0, "No Route";
315 CommAdministrativelyProhibited, 1, "Comm Administratively Prohibited";
316 BeyondScope, 2, "Beyond Scope";
317 AddrUnreachable, 3, "Address Unreachable";
318 PortUnreachable, 4, "Port Unreachable";
319 SrcAddrFailedPolicy, 5, "Source Address Failed Policy";
320 RejectRoute, 6, "Reject Route";
321 }
322);
323
324impl_icmp_message!(
325 Ipv6,
326 IcmpDestUnreachable,
327 DestUnreachable,
328 Icmpv6DestUnreachableCode,
329 OriginalPacket<B>
330);
331
332#[derive(
334 Copy, Clone, Debug, KnownLayout, FromBytes, IntoBytes, Immutable, Unaligned, PartialEq,
335)]
336#[repr(C)]
337pub struct Icmpv6PacketTooBig {
338 mtu: U32,
339}
340
341impl Icmpv6PacketTooBig {
342 pub fn new(mtu: u32) -> Icmpv6PacketTooBig {
344 Icmpv6PacketTooBig { mtu: U32::new(mtu) }
345 }
346
347 pub fn mtu(&self) -> u32 {
349 self.mtu.get()
350 }
351}
352
353impl_icmp_message!(Ipv6, Icmpv6PacketTooBig, PacketTooBig, IcmpZeroCode, OriginalPacket<B>);
354
355create_protocol_enum!(
356 #[allow(missing_docs)]
357 #[derive(Copy, Clone, PartialEq, Eq)]
358 pub enum Icmpv6TimeExceededCode: u8 {
359 HopLimitExceeded, 0, "Hop Limit Exceeded";
360 FragmentReassemblyTimeExceeded, 1, "Fragment Reassembly Time Exceeded";
361 }
362);
363
364impl_icmp_message!(Ipv6, IcmpTimeExceeded, TimeExceeded, Icmpv6TimeExceededCode, OriginalPacket<B>);
365
366create_protocol_enum!(
367 #[allow(missing_docs)]
368 #[derive(Copy, Clone, PartialEq, Eq)]
369 pub enum Icmpv6ParameterProblemCode: u8 {
370 ErroneousHeaderField, 0, "Erroneous Header Field";
371 UnrecognizedNextHeaderType, 1, "Unrecognized Next Header Type";
372 UnrecognizedIpv6Option, 2, "Unrecognized IPv6 Option";
373 }
374);
375
376#[derive(
378 Copy, Clone, Debug, Eq, PartialEq, KnownLayout, FromBytes, IntoBytes, Immutable, Unaligned,
379)]
380#[repr(C)]
381pub struct Icmpv6ParameterProblem {
382 pointer: U32,
383}
384
385impl Icmpv6ParameterProblem {
386 pub fn new(pointer: u32) -> Icmpv6ParameterProblem {
388 Icmpv6ParameterProblem { pointer: U32::new(pointer) }
389 }
390
391 pub fn pointer(self) -> u32 {
393 self.pointer.get()
394 }
395}
396
397impl_icmp_message!(
398 Ipv6,
399 Icmpv6ParameterProblem,
400 ParameterProblem,
401 Icmpv6ParameterProblemCode,
402 OriginalPacket<B>
403);
404
405#[cfg(test)]
406mod tests {
407 use core::fmt::Debug;
408 use packet::{InnerPacketBuilder, ParseBuffer, Serializer};
409
410 use super::*;
411 use crate::icmp::{IcmpMessage, MessageBody};
412 use crate::ipv6::{Ipv6Header, Ipv6Packet, Ipv6PacketBuilder};
413
414 fn serialize_to_bytes<B: SplitByteSlice + Debug, M: IcmpMessage<Ipv6> + Debug>(
415 src_ip: Ipv6Addr,
416 dst_ip: Ipv6Addr,
417 icmp: &IcmpPacket<Ipv6, B, M>,
418 builder: Ipv6PacketBuilder,
419 ) -> Vec<u8> {
420 let (header, body) = icmp.message_body.bytes();
421 let body = if let Some(b) = body { b } else { &[] };
422 let complete_msg = &[header, body].concat();
423
424 complete_msg
425 .into_serializer()
426 .encapsulate(icmp.builder(src_ip, dst_ip))
427 .encapsulate(builder)
428 .serialize_vec_outer()
429 .unwrap()
430 .as_ref()
431 .to_vec()
432 }
433
434 fn test_parse_and_serialize<
435 M: IcmpMessage<Ipv6> + Debug,
436 F: for<'a> FnOnce(&IcmpPacket<Ipv6, &'a [u8], M>),
437 >(
438 mut req: &[u8],
439 check: F,
440 ) {
441 let orig_req = req;
442
443 let ip = req.parse::<Ipv6Packet<_>>().unwrap();
444 let mut body = ip.body();
445 let icmp = body
446 .parse_with::<_, IcmpPacket<_, _, M>>(IcmpParseArgs::new(ip.src_ip(), ip.dst_ip()))
447 .unwrap();
448 check(&icmp);
449
450 let data = serialize_to_bytes(ip.src_ip(), ip.dst_ip(), &icmp, ip.builder());
451 assert_eq!(&data[..], orig_req);
452 }
453
454 #[test]
455 fn test_parse_and_serialize_echo_request() {
456 use crate::testdata::icmp_echo_v6::*;
457 test_parse_and_serialize::<IcmpEchoRequest, _>(REQUEST_IP_PACKET_BYTES, |icmp| {
458 let (inner_header, inner_body) = icmp.message_body.bytes();
459 assert!(inner_body.is_none());
460 let complete_msg = inner_header;
461 assert_eq!(complete_msg, ECHO_DATA);
462 assert_eq!(icmp.message().id_seq.id.get(), IDENTIFIER);
463 assert_eq!(icmp.message().id_seq.seq.get(), SEQUENCE_NUM);
464 });
465 }
466}