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