1#[macro_use]
17mod macros;
18mod common;
19mod icmpv4;
20mod icmpv6;
21pub mod mld;
22pub mod ndp;
23
24#[cfg(test)]
25mod testdata;
26
27pub use self::common::*;
28pub use self::icmpv4::*;
29pub use self::icmpv6::*;
30
31use core::fmt::Debug;
32use core::marker::PhantomData;
33use core::{cmp, mem};
34
35use byteorder::{ByteOrder, NetworkEndian};
36use derivative::Derivative;
37use internet_checksum::Checksum;
38use net_types::ip::{GenericOverIp, Ip, IpAddress, IpVersion, Ipv4, Ipv4Addr, Ipv6, Ipv6Addr};
39use packet::records::options::{Options, OptionsImpl};
40use packet::{
41 AsFragmentedByteSlice, BufferView, FragmentedByteSlice, FragmentedBytesMut, FromRaw,
42 NestablePacketBuilder, NoOpSerializationContext, PacketBuilder, PacketConstraints,
43 ParsablePacket, ParseMetadata, PartialPacketBuilder, SerializationContext, SerializeTarget,
44};
45use zerocopy::byteorder::network_endian::U16;
46use zerocopy::{
47 FromBytes, Immutable, IntoBytes, KnownLayout, Ref, SplitByteSlice, SplitByteSliceMut, Unaligned,
48};
49
50use crate::error::{NotZeroError, ParseError, ParseResult};
51use crate::ip::{IpProtoExt, Ipv4Proto, Ipv6Proto};
52use crate::ipv4::{self, Ipv4PacketRaw};
53use crate::ipv6::Ipv6PacketRaw;
54
55#[derive(Copy, Clone, Default, Debug, KnownLayout, FromBytes, IntoBytes, Immutable, Unaligned)]
56#[repr(C)]
57struct HeaderPrefix {
58 msg_type: u8,
59 code: u8,
60 checksum: [u8; 2],
61 }
66
67impl HeaderPrefix {
68 fn set_msg_type<T: Into<u8>>(&mut self, msg_type: T) {
69 self.msg_type = msg_type.into();
70 }
71}
72
73pub fn peek_message_type<MessageType: TryFrom<u8>>(bytes: &[u8]) -> ParseResult<MessageType> {
86 let (hdr_pfx, _) = Ref::<_, HeaderPrefix>::from_prefix(bytes).map_err(Into::into).map_err(
87 |_: zerocopy::SizeError<_, _>| debug_err!(ParseError::Format, "too few bytes for header"),
88 )?;
89 MessageType::try_from(hdr_pfx.msg_type).map_err(|_| {
90 debug_err!(ParseError::NotSupported, "unrecognized message type: {:x}", hdr_pfx.msg_type,)
91 })
92}
93
94pub trait IcmpIpExt: IpProtoExt {
96 type IcmpPacketTypeRaw<B: SplitByteSliceMut>: IcmpPacketTypeRaw<B, Self>
98 + GenericOverIp<Self, Type = Self::IcmpPacketTypeRaw<B>>
99 + GenericOverIp<Ipv4, Type = Icmpv4PacketRaw<B>>
100 + GenericOverIp<Ipv6, Type = Icmpv6PacketRaw<B>>;
101
102 type IcmpMessageType: IcmpMessageType
107 + GenericOverIp<Self, Type = Self::IcmpMessageType>
108 + GenericOverIp<Ipv4, Type = Icmpv4MessageType>
109 + GenericOverIp<Ipv6, Type = Icmpv6MessageType>;
110
111 type DestUnreachableCode: PartialEq + Send + Sync + Debug;
116
117 type ParameterProblemCode: PartialEq + Send + Sync + Debug;
122
123 type ParameterProblemPointer: PartialEq + Send + Sync + Debug;
127
128 const ICMP_IP_PROTO: <Self as IpProtoExt>::Proto;
134
135 fn header_len(bytes: &[u8]) -> usize;
143
144 const ECHO_REPLY: Self::IcmpMessageType;
146 const ECHO_REQUEST: Self::IcmpMessageType;
148}
149
150impl IcmpIpExt for Ipv4 {
151 type IcmpPacketTypeRaw<B: SplitByteSliceMut> = Icmpv4PacketRaw<B>;
152 type IcmpMessageType = Icmpv4MessageType;
153 type DestUnreachableCode = Icmpv4DestUnreachableCode;
154 type ParameterProblemCode = Icmpv4ParameterProblemCode;
155 type ParameterProblemPointer = u8;
156
157 const ICMP_IP_PROTO: Ipv4Proto = Ipv4Proto::Icmp;
158
159 fn header_len(bytes: &[u8]) -> usize {
160 if bytes.len() < ipv4::IPV4_MIN_HDR_LEN {
161 return bytes.len();
162 }
163 let (header_prefix, _) = Ref::<_, ipv4::HeaderPrefix>::from_prefix(bytes).unwrap();
164 cmp::min(header_prefix.ihl() as usize * 4, bytes.len())
165 }
166
167 const ECHO_REPLY: Icmpv4MessageType = Icmpv4MessageType::EchoReply;
168 const ECHO_REQUEST: Icmpv4MessageType = Icmpv4MessageType::EchoRequest;
169}
170
171impl IcmpIpExt for Ipv6 {
172 type IcmpPacketTypeRaw<B: SplitByteSliceMut> = Icmpv6PacketRaw<B>;
173 type IcmpMessageType = Icmpv6MessageType;
174 type DestUnreachableCode = Icmpv6DestUnreachableCode;
175 type ParameterProblemCode = Icmpv6ParameterProblemCode;
176 type ParameterProblemPointer = u32;
177
178 const ICMP_IP_PROTO: Ipv6Proto = Ipv6Proto::Icmpv6;
179
180 fn header_len(_bytes: &[u8]) -> usize {
183 unimplemented!()
188 }
189
190 const ECHO_REPLY: Icmpv6MessageType = Icmpv6MessageType::EchoReply;
191 const ECHO_REQUEST: Icmpv6MessageType = Icmpv6MessageType::EchoRequest;
192}
193
194pub trait IcmpPacketTypeRaw<B: SplitByteSliceMut, I: Ip>:
198 Sized + ParsablePacket<B, (), Error = ParseError>
199{
200 fn update_checksum_pseudo_header_address(&mut self, old: I::Addr, new: I::Addr);
202
203 fn update_checksum_header_field<F: IntoBytes + Immutable>(&mut self, old: F, new: F);
208
209 fn update_checksum_header_field_u16(&mut self, old: u16, new: u16) {
212 self.update_checksum_header_field(U16::new(old), U16::new(new))
213 }
214
215 fn try_write_checksum(&mut self, src_addr: I::Addr, dst_addr: I::Addr) -> bool;
220
221 fn message_body_mut(&mut self) -> &mut B;
223}
224
225impl<B: SplitByteSliceMut> IcmpPacketTypeRaw<B, Ipv4> for Icmpv4PacketRaw<B> {
226 fn update_checksum_pseudo_header_address(&mut self, old: Ipv4Addr, new: Ipv4Addr) {
227 crate::icmpv4_dispatch!(self: raw, p => p.update_checksum_pseudo_header_address(old, new))
228 }
229
230 fn update_checksum_header_field<F: IntoBytes + Immutable>(&mut self, old: F, new: F) {
231 crate::icmpv4_dispatch!(self: raw, p => p.update_checksum_header_field(old, new))
232 }
233
234 fn try_write_checksum(&mut self, src_addr: Ipv4Addr, dst_addr: Ipv4Addr) -> bool {
235 crate::icmpv4_dispatch!(self: raw, p => p.try_write_checksum(src_addr, dst_addr))
236 }
237
238 fn message_body_mut(&mut self) -> &mut B {
239 crate::icmpv4_dispatch!(self: raw, p => p.message_body_mut())
240 }
241}
242
243impl<I: IcmpIpExt, B: SplitByteSliceMut> GenericOverIp<I> for Icmpv4PacketRaw<B> {
244 type Type = I::IcmpPacketTypeRaw<B>;
245}
246
247impl<B: SplitByteSliceMut> IcmpPacketTypeRaw<B, Ipv6> for Icmpv6PacketRaw<B> {
248 fn update_checksum_pseudo_header_address(&mut self, old: Ipv6Addr, new: Ipv6Addr) {
249 crate::icmpv6_dispatch!(self: raw, p => p.update_checksum_pseudo_header_address(old, new))
250 }
251
252 fn update_checksum_header_field<F: IntoBytes + Immutable>(&mut self, old: F, new: F) {
253 crate::icmpv6_dispatch!(self: raw, p => p.update_checksum_header_field(old, new))
254 }
255
256 fn try_write_checksum(&mut self, src_addr: Ipv6Addr, dst_addr: Ipv6Addr) -> bool {
257 crate::icmpv6_dispatch!(self: raw, p => p.try_write_checksum(src_addr, dst_addr))
258 }
259
260 fn message_body_mut(&mut self) -> &mut B {
261 crate::icmpv6_dispatch!(self: raw, p => p.message_body_mut())
262 }
263}
264
265impl<I: IcmpIpExt, B: SplitByteSliceMut, M: IcmpMessage<I>> IcmpPacketTypeRaw<B, I>
266 for IcmpPacketRaw<I, B, M>
267{
268 fn update_checksum_pseudo_header_address(&mut self, old: I::Addr, new: I::Addr) {
269 match I::VERSION {
270 IpVersion::V4 => {
271 }
273 IpVersion::V6 => {
274 let checksum = &mut self.header.prefix.checksum;
275 *checksum = internet_checksum::update(*checksum, old.bytes(), new.bytes());
276 }
277 }
278 }
279
280 fn update_checksum_header_field<F: IntoBytes + Immutable>(&mut self, old: F, new: F) {
281 let checksum = &mut self.header.prefix.checksum;
282 *checksum = internet_checksum::update(*checksum, old.as_bytes(), new.as_bytes());
283 }
284
285 fn try_write_checksum(&mut self, src_addr: I::Addr, dst_addr: I::Addr) -> bool {
286 self.try_write_checksum(src_addr, dst_addr)
287 }
288
289 fn message_body_mut(&mut self) -> &mut B {
290 self.message_body_mut()
291 }
292}
293
294impl<I: IcmpIpExt, B: SplitByteSliceMut> GenericOverIp<I> for Icmpv6PacketRaw<B> {
295 type Type = I::IcmpPacketTypeRaw<B>;
296}
297
298#[derive(Derivative, Debug, Clone, Copy, PartialEq, Eq)]
300#[derivative(Default(bound = ""))]
301pub struct EmptyMessage<B>(core::marker::PhantomData<B>);
302
303pub trait MessageBody: Sized {
310 type B: SplitByteSlice;
312
313 fn parse(bytes: Self::B) -> ParseResult<Self>;
315
316 fn len(&self) -> usize;
318
319 fn is_empty(&self) -> bool {
323 self.len() == 0
324 }
325
326 fn bytes(&self) -> (&[u8], Option<&[u8]>);
339}
340
341impl<B: SplitByteSlice> MessageBody for EmptyMessage<B> {
342 type B = B;
343
344 fn parse(bytes: B) -> ParseResult<Self> {
345 if !bytes.is_empty() {
346 return debug_err!(Err(ParseError::Format), "unexpected message body");
347 }
348
349 Ok(EmptyMessage::default())
350 }
351
352 fn len(&self) -> usize {
353 0
354 }
355
356 fn bytes(&self) -> (&[u8], Option<&[u8]>) {
357 (&[], None)
358 }
359}
360
361#[derive(Debug)]
363pub struct OriginalPacket<B>(B);
364
365impl<B: SplitByteSlice> OriginalPacket<B> {
366 pub fn body<I: IcmpIpExt>(&self) -> &[u8] {
368 let header_len = I::header_len(&self.0);
370 debug_assert!(header_len <= self.0.len());
371 debug_assert!(I::VERSION.is_v6() || self.0.len() - header_len == 8);
372 &self.0[header_len..]
373 }
374}
375
376impl<B: SplitByteSlice> MessageBody for OriginalPacket<B> {
377 type B = B;
378
379 fn parse(bytes: B) -> ParseResult<OriginalPacket<B>> {
380 Ok(OriginalPacket(bytes))
381 }
382
383 fn len(&self) -> usize {
384 self.0.len()
385 }
386
387 fn bytes(&self) -> (&[u8], Option<&[u8]>) {
388 (&self.0, None)
389 }
390}
391
392impl<B: SplitByteSlice, O: OptionsImpl> MessageBody for Options<B, O> {
393 type B = B;
394 fn parse(bytes: B) -> ParseResult<Options<B, O>> {
395 Self::parse(bytes).map_err(|_e| debug_err!(ParseError::Format, "unable to parse options"))
396 }
397
398 fn len(&self) -> usize {
399 self.bytes().len()
400 }
401
402 fn bytes(&self) -> (&[u8], Option<&[u8]>) {
403 (self.bytes(), None)
404 }
405}
406
407pub trait IcmpMessage<I: IcmpIpExt>:
409 Sized + Copy + FromBytes + IntoBytes + KnownLayout + Immutable + Unaligned
410{
411 const EXPECTS_BODY: bool = true;
413
414 type Code: Into<u8> + Copy + Debug;
421
422 type Body<B: SplitByteSlice>: MessageBody<B = B>;
424
425 const TYPE: I::IcmpMessageType;
430
431 fn code_from_u8(code: u8) -> Option<Self::Code>;
437}
438
439pub trait IcmpMessageType: TryFrom<u8> + Into<u8> + Copy + Debug {
444 fn is_error_or_redirect(self) -> bool;
451}
452
453#[derive(Copy, Clone, Debug, KnownLayout, FromBytes, IntoBytes, Immutable, Unaligned)]
454#[repr(C)]
455struct Header<M> {
456 prefix: HeaderPrefix,
457 message: M,
458}
459
460#[derive(Debug)]
472pub struct IcmpPacketRaw<I: IcmpIpExt, B: SplitByteSlice, M: IcmpMessage<I>> {
473 header: Ref<B, Header<M>>,
474 message_body: B,
475 _marker: PhantomData<I>,
476}
477
478impl<I: IcmpIpExt, B: SplitByteSlice, M: IcmpMessage<I>> IcmpPacketRaw<I, B, M> {
479 pub fn message(&self) -> &M {
481 &self.header.message
482 }
483
484 pub fn message_body(&self) -> &B {
486 &self.message_body
487 }
488}
489
490impl<I: IcmpIpExt, B: SplitByteSliceMut, M: IcmpMessage<I>> IcmpPacketRaw<I, B, M> {
491 pub fn message_mut(&mut self) -> &mut M {
493 &mut self.header.message
494 }
495
496 pub fn message_body_mut(&mut self) -> &mut B {
498 &mut self.message_body
499 }
500
501 pub(crate) fn try_write_checksum(&mut self, src_ip: I::Addr, dst_ip: I::Addr) -> bool {
506 let original_checksum = self.header.prefix.checksum;
508 self.header.prefix.checksum = [0, 0];
509
510 if let Some(checksum) = IcmpPacket::<I, B, M>::compute_checksum(
511 &self.header,
512 &self.message_body,
513 src_ip,
514 dst_ip,
515 ) {
516 self.header.prefix.checksum = checksum;
517 true
518 } else {
519 self.header.prefix.checksum = original_checksum;
520 false
521 }
522 }
523}
524
525#[derive(Debug)]
530pub struct IcmpPacket<I: IcmpIpExt, B: SplitByteSlice, M: IcmpMessage<I>> {
531 header: Ref<B, Header<M>>,
532 message_body: M::Body<B>,
533 _marker: PhantomData<I>,
534}
535
536pub struct IcmpParseArgs<A: IpAddress> {
538 src_ip: A,
539 dst_ip: A,
540}
541
542impl<A: IpAddress> IcmpParseArgs<A> {
543 pub fn new<S: Into<A>, D: Into<A>>(src_ip: S, dst_ip: D) -> IcmpParseArgs<A> {
545 IcmpParseArgs { src_ip: src_ip.into(), dst_ip: dst_ip.into() }
546 }
547}
548
549impl<B: SplitByteSlice, I: IcmpIpExt, M: IcmpMessage<I>> ParsablePacket<B, ()>
550 for IcmpPacketRaw<I, B, M>
551{
552 type Error = ParseError;
553
554 fn parse_metadata(&self) -> ParseMetadata {
555 ParseMetadata::from_packet(Ref::bytes(&self.header).len(), self.message_body.len(), 0)
556 }
557
558 fn parse<BV: BufferView<B>>(mut buffer: BV, _args: ()) -> ParseResult<Self> {
559 let header = buffer.take_obj_front::<Header<M>>().ok_or(ParseError::Format)?;
560 let message_body = buffer.into_rest();
561 if header.prefix.msg_type != M::TYPE.into() {
562 return Err(ParseError::NotExpected);
563 }
564 Ok(IcmpPacketRaw { header, message_body, _marker: PhantomData })
565 }
566}
567
568impl<B: SplitByteSlice, I: IcmpIpExt, M: IcmpMessage<I>>
569 FromRaw<IcmpPacketRaw<I, B, M>, IcmpParseArgs<I::Addr>> for IcmpPacket<I, B, M>
570{
571 type Error = ParseError;
572
573 fn try_from_raw_with(
574 raw: IcmpPacketRaw<I, B, M>,
575 args: IcmpParseArgs<I::Addr>,
576 ) -> ParseResult<Self> {
577 let IcmpPacketRaw { header, message_body, _marker } = raw;
578 if !M::EXPECTS_BODY && !message_body.is_empty() {
579 return Err(ParseError::Format);
580 }
581 let _: M::Code = M::code_from_u8(header.prefix.code).ok_or(ParseError::Format)?;
582 let checksum = Self::compute_checksum(&header, &message_body, args.src_ip, args.dst_ip)
583 .ok_or(ParseError::Format)?;
584 if checksum != [0, 0] {
585 return Err(ParseError::Checksum);
586 }
587 let message_body = M::Body::parse(message_body)?;
588 Ok(IcmpPacket { header, message_body, _marker })
589 }
590}
591
592impl<B: SplitByteSlice, I: IcmpIpExt, M: IcmpMessage<I>> ParsablePacket<B, IcmpParseArgs<I::Addr>>
593 for IcmpPacket<I, B, M>
594{
595 type Error = ParseError;
596
597 fn parse_metadata(&self) -> ParseMetadata {
598 ParseMetadata::from_packet(Ref::bytes(&self.header).len(), self.message_body.len(), 0)
599 }
600
601 fn parse<BV: BufferView<B>>(buffer: BV, args: IcmpParseArgs<I::Addr>) -> ParseResult<Self> {
602 IcmpPacketRaw::parse(buffer, ()).and_then(|p| IcmpPacket::try_from_raw_with(p, args))
603 }
604}
605
606impl<I: IcmpIpExt, B: SplitByteSlice, M: IcmpMessage<I>> IcmpPacket<I, B, M> {
607 pub fn message(&self) -> &M {
609 &self.header.message
610 }
611
612 pub fn body(&self) -> &M::Body<B> {
614 &self.message_body
615 }
616
617 pub fn code(&self) -> M::Code {
622 M::code_from_u8(self.header.prefix.code).unwrap()
624 }
625
626 pub fn builder(&self, src_ip: I::Addr, dst_ip: I::Addr) -> IcmpPacketBuilder<I, M> {
628 IcmpPacketBuilder { src_ip, dst_ip, code: self.code(), msg: *self.message() }
629 }
630}
631
632fn compute_checksum_fragmented<I: IcmpIpExt, BB: packet::Fragment, M: IcmpMessage<I>>(
633 header: &Header<M>,
634 message_body: &FragmentedByteSlice<'_, BB>,
635 src_ip: I::Addr,
636 dst_ip: I::Addr,
637) -> Option<[u8; 2]> {
638 let mut c = Checksum::new();
639 if I::VERSION.is_v6() {
640 c.add_bytes(src_ip.bytes());
641 c.add_bytes(dst_ip.bytes());
642 let icmpv6_len = mem::size_of::<Header<M>>() + message_body.len();
643 let mut len_bytes = [0; 4];
644 NetworkEndian::write_u32(&mut len_bytes, icmpv6_len.try_into().ok()?);
645 c.add_bytes(&len_bytes[..]);
646 c.add_bytes(&[0, 0, 0]);
647 c.add_bytes(&[Ipv6Proto::Icmpv6.into()]);
648 }
649 c.add_bytes(&[header.prefix.msg_type, header.prefix.code]);
650 c.add_bytes(&header.prefix.checksum);
651 c.add_bytes(header.message.as_bytes());
652 for p in message_body.iter_fragments() {
653 c.add_bytes(p);
654 }
655 Some(c.checksum())
656}
657
658impl<I: IcmpIpExt, B: SplitByteSlice, M: IcmpMessage<I>> IcmpPacket<I, B, M> {
659 fn compute_checksum(
664 header: &Header<M>,
665 message_body: &[u8],
666 src_ip: I::Addr,
667 dst_ip: I::Addr,
668 ) -> Option<[u8; 2]> {
669 let mut body = [message_body];
670 compute_checksum_fragmented(header, &body.as_fragmented_byte_slice(), src_ip, dst_ip)
671 }
672}
673
674impl<I: IcmpIpExt, B: SplitByteSlice, M: IcmpMessage<I, Body<B> = OriginalPacket<B>>>
675 IcmpPacket<I, B, M>
676{
677 pub fn original_packet_body(&self) -> &[u8] {
685 self.message_body.body::<I>()
686 }
687
688 pub fn original_packet(&self) -> &OriginalPacket<B> {
696 &self.message_body
697 }
698}
699
700impl<B: SplitByteSlice, M: IcmpMessage<Ipv4, Body<B> = OriginalPacket<B>>> IcmpPacket<Ipv4, B, M> {
701 pub fn with_original_packet<O, F: FnOnce(Result<Ipv4PacketRaw<&[u8]>, &[u8]>) -> O>(
706 &self,
707 f: F,
708 ) -> O {
709 let mut bv = self.message_body.0.deref();
710 f(Ipv4PacketRaw::parse(&mut bv, ()).map_err(|_| self.message_body.0.deref()))
711 }
712}
713
714impl<B: SplitByteSlice, M: IcmpMessage<Ipv6, Body<B> = OriginalPacket<B>>> IcmpPacket<Ipv6, B, M> {
715 pub fn with_original_packet<O, F: FnOnce(Result<Ipv6PacketRaw<&[u8]>, &[u8]>) -> O>(
720 &self,
721 f: F,
722 ) -> O {
723 let mut bv = self.message_body.0.deref();
724 f(Ipv6PacketRaw::parse(&mut bv, ()).map_err(|_| self.message_body.0.deref()))
725 }
726}
727
728impl<I: IcmpIpExt, B: SplitByteSlice, M: IcmpMessage<I, Body<B> = ndp::Options<B>>>
729 IcmpPacket<I, B, M>
730{
731 pub fn ndp_options(&self) -> &ndp::Options<B> {
733 &self.message_body
734 }
735}
736
737pub struct IcmpEnvelope;
739
740pub trait IcmpSerializationContext: SerializationContext {
742 fn envelope_to_state(envelope: IcmpEnvelope) -> Self::ContextState;
744}
745
746impl IcmpSerializationContext for NoOpSerializationContext {
747 fn envelope_to_state(_envelope: IcmpEnvelope) -> Self::ContextState {
748 ()
749 }
750}
751
752#[derive(Debug, PartialEq, Clone)]
754pub struct IcmpPacketBuilder<I: IcmpIpExt, M: IcmpMessage<I>> {
755 src_ip: I::Addr,
756 dst_ip: I::Addr,
757 code: M::Code,
758 msg: M,
759}
760
761impl<I: IcmpIpExt, M: IcmpMessage<I>> IcmpPacketBuilder<I, M> {
762 pub fn new<S: Into<I::Addr>, D: Into<I::Addr>>(
764 src_ip: S,
765 dst_ip: D,
766 code: M::Code,
767 msg: M,
768 ) -> IcmpPacketBuilder<I, M> {
769 IcmpPacketBuilder { src_ip: src_ip.into(), dst_ip: dst_ip.into(), code, msg }
770 }
771
772 pub fn message(&self) -> &M {
774 &self.msg
775 }
776
777 pub fn message_mut(&mut self) -> &mut M {
779 &mut self.msg
780 }
781
782 pub fn set_src_ip(&mut self, addr: I::Addr) {
784 self.src_ip = addr;
785 }
786
787 pub fn set_dst_ip(&mut self, addr: I::Addr) {
789 self.dst_ip = addr;
790 }
791
792 fn serialize_header(
793 &self,
794 mut header: &mut [u8],
795 message_body: Option<FragmentedBytesMut<'_, '_>>,
796 ) {
797 use packet::BufferViewMut;
798
799 let mut prefix = &mut header;
801
802 let mut header =
805 prefix.take_obj_front_zero::<Header<M>>().expect("too few bytes for ICMP message");
806 header.prefix.set_msg_type(M::TYPE);
807 header.prefix.code = self.code.into();
808 header.message = self.msg;
809
810 if let Some(message_body) = message_body {
811 assert!(
812 M::EXPECTS_BODY || message_body.is_empty(),
813 "body provided for message that doesn't take a body"
814 );
815 let checksum = compute_checksum_fragmented(
816 &header,
817 &message_body,
818 self.src_ip,
819 self.dst_ip,
820 )
821 .unwrap_or_else(|| {
822 panic!(
823 "total ICMP packet length of {} overflows 32-bit length field of pseudo-header",
824 Ref::bytes(&header).len() + message_body.len(),
825 )
826 });
827 header.prefix.checksum = checksum;
828 }
829 }
830}
831
832impl<I: IcmpIpExt, M: IcmpMessage<I>> NestablePacketBuilder for IcmpPacketBuilder<I, M> {
836 fn constraints(&self) -> PacketConstraints {
837 PacketConstraints::new(mem::size_of::<Header<M>>(), 0, 0, core::u32::MAX as usize)
850 }
851}
852
853impl<I: IcmpIpExt, M: IcmpMessage<I>, C: IcmpSerializationContext> PacketBuilder<C>
854 for IcmpPacketBuilder<I, M>
855{
856 fn context_state(&self) -> C::ContextState {
857 C::envelope_to_state(IcmpEnvelope)
858 }
859
860 fn serialize(
861 &self,
862 _context: &mut C,
863 target: &mut SerializeTarget<'_>,
864 message_body: FragmentedBytesMut<'_, '_>,
865 ) {
866 self.serialize_header(target.header, Some(message_body));
867 }
868}
869
870impl<I: IcmpIpExt, M: IcmpMessage<I>, C: IcmpSerializationContext> PartialPacketBuilder<C>
871 for IcmpPacketBuilder<I, M>
872{
873 fn partial_serialize(&self, _context: &mut C, _body_len: usize, buffer: &mut [u8]) {
874 self.serialize_header(buffer, None);
875 }
876}
877
878#[derive(Copy, Clone, Debug, Eq, PartialEq)]
884pub struct IcmpZeroCode;
885
886impl From<IcmpZeroCode> for u8 {
887 fn from(_: IcmpZeroCode) -> u8 {
888 0
889 }
890}
891
892impl TryFrom<u8> for IcmpZeroCode {
893 type Error = NotZeroError<u8>;
894
895 fn try_from(value: u8) -> Result<Self, NotZeroError<u8>> {
896 if value == 0 { Ok(Self) } else { Err(NotZeroError(value)) }
897 }
898}
899
900#[derive(Copy, Clone, Debug, Eq, PartialEq)]
905pub struct IcmpSenderZeroCode;
906
907impl From<IcmpSenderZeroCode> for u8 {
908 fn from(_: IcmpSenderZeroCode) -> u8 {
909 0
910 }
911}
912
913impl From<u8> for IcmpSenderZeroCode {
914 fn from(_: u8) -> Self {
915 Self
916 }
917}
918
919#[doc(hidden)]
924#[derive(
925 Copy, Clone, Debug, Eq, PartialEq, KnownLayout, FromBytes, IntoBytes, Immutable, Unaligned,
926)]
927#[repr(C)]
928pub struct IdAndSeq {
929 id: U16,
930 seq: U16,
931}
932
933impl IdAndSeq {
934 fn new(id: u16, seq: u16) -> IdAndSeq {
935 IdAndSeq { id: U16::new(id), seq: U16::new(seq) }
936 }
937}
938
939#[cfg(test)]
940mod tests {
941 use ip_test_macro::ip_test;
942 use packet::{EmptyBuf, NoOpSerializationContext, ParseBuffer, Serializer, SliceBufViewMut};
943 use test_case::test_case;
944
945 use super::*;
946
947 #[test]
948 fn test_partial_parse() {
949 let reference_header = Header {
952 prefix: HeaderPrefix {
953 msg_type: <IcmpEchoRequest as IcmpMessage<Ipv4>>::TYPE.into(),
954 code: 0,
955 checksum: [0, 0],
956 },
957 message: IcmpEchoRequest::new(1, 1),
958 };
959
960 let mut buf = &reference_header.as_bytes()[..7];
965 assert_eq!(
966 buf.parse::<IcmpPacketRaw<Ipv4, _, IcmpEchoRequest>>().unwrap_err(),
967 ParseError::Format
968 );
969
970 let mut header = reference_header;
972 header.prefix.msg_type = <IcmpEchoReply as IcmpMessage<Ipv4>>::TYPE.into();
973 let mut buf = header.as_bytes();
974 assert_eq!(
975 buf.parse::<IcmpPacketRaw<Ipv4, _, IcmpEchoRequest>>().unwrap_err(),
976 ParseError::NotExpected
977 );
978
979 let mut header = reference_header;
981 header.prefix.code = 0xFF;
982 let mut buf = header.as_bytes();
983 assert!(buf.parse::<IcmpPacketRaw<Ipv4, _, IcmpEchoRequest>>().is_ok());
984
985 let mut buf = reference_header.as_bytes();
989 assert!(buf.parse::<IcmpPacketRaw<Ipv4, _, IcmpEchoRequest>>().is_ok());
990 let mut header = reference_header;
991 header.prefix.checksum = [1, 1];
992 let mut buf = header.as_bytes();
993 assert!(buf.parse::<IcmpPacketRaw<Ipv4, _, IcmpEchoRequest>>().is_ok());
994 }
995
996 #[ip_test(I)]
997 #[test_case([0,0]; "zeroed_checksum")]
998 #[test_case([123, 234]; "garbage_checksum")]
999 fn test_try_write_checksum<I: IcmpIpExt>(corrupt_checksum: [u8; 2]) {
1000 let icmp_message_with_checksum = IcmpPacketBuilder::<I, _>::new(
1003 *I::LOOPBACK_ADDRESS,
1004 *I::LOOPBACK_ADDRESS,
1005 IcmpZeroCode,
1006 IcmpEchoRequest::new(1, 1),
1007 )
1008 .wrap_body(EmptyBuf)
1009 .serialize_vec_outer(&mut NoOpSerializationContext)
1010 .unwrap()
1011 .as_ref()
1012 .to_vec();
1013
1014 let mut icmp_message_without_checksum = icmp_message_with_checksum.clone();
1016 {
1017 let buf = SliceBufViewMut::new(&mut icmp_message_without_checksum);
1018 let mut message = IcmpPacketRaw::<I, _, IcmpEchoRequest>::parse_mut(buf, ())
1019 .expect("parse packet raw should succeed");
1020 message.header.prefix.checksum = corrupt_checksum;
1021 }
1022 assert_ne!(&icmp_message_with_checksum[..], &icmp_message_without_checksum[..]);
1023
1024 let buf = SliceBufViewMut::new(&mut icmp_message_without_checksum);
1026 let mut message = IcmpPacketRaw::<I, _, IcmpEchoRequest>::parse_mut(buf, ())
1027 .expect("parse packet raw should succeed");
1028 assert!(message.try_write_checksum(*I::LOOPBACK_ADDRESS, *I::LOOPBACK_ADDRESS));
1029 assert_eq!(&icmp_message_with_checksum[..], &icmp_message_without_checksum[..]);
1030 }
1031}