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 PacketBuilder, PacketConstraints, ParsablePacket, ParseMetadata, SerializeTarget,
43};
44use zerocopy::byteorder::network_endian::U16;
45use zerocopy::{
46 FromBytes, Immutable, IntoBytes, KnownLayout, Ref, SplitByteSlice, SplitByteSliceMut, Unaligned,
47};
48
49use crate::error::{NotZeroError, ParseError, ParseResult};
50use crate::ip::{IpProtoExt, Ipv4Proto, Ipv6Proto};
51use crate::ipv4::{self, Ipv4PacketRaw};
52use crate::ipv6::Ipv6PacketRaw;
53
54#[derive(Copy, Clone, Default, Debug, KnownLayout, FromBytes, IntoBytes, Immutable, Unaligned)]
55#[repr(C)]
56struct HeaderPrefix {
57 msg_type: u8,
58 code: u8,
59 checksum: [u8; 2],
60 }
65
66impl HeaderPrefix {
67 fn set_msg_type<T: Into<u8>>(&mut self, msg_type: T) {
68 self.msg_type = msg_type.into();
69 }
70}
71
72pub fn peek_message_type<MessageType: TryFrom<u8>>(bytes: &[u8]) -> ParseResult<MessageType> {
85 let (hdr_pfx, _) = Ref::<_, HeaderPrefix>::from_prefix(bytes).map_err(Into::into).map_err(
86 |_: zerocopy::SizeError<_, _>| debug_err!(ParseError::Format, "too few bytes for header"),
87 )?;
88 MessageType::try_from(hdr_pfx.msg_type).map_err(|_| {
89 debug_err!(ParseError::NotSupported, "unrecognized message type: {:x}", hdr_pfx.msg_type,)
90 })
91}
92
93pub trait IcmpIpExt: IpProtoExt {
95 type IcmpPacketTypeRaw<B: SplitByteSliceMut>: IcmpPacketTypeRaw<B, Self>
97 + GenericOverIp<Self, Type = Self::IcmpPacketTypeRaw<B>>
98 + GenericOverIp<Ipv4, Type = Icmpv4PacketRaw<B>>
99 + GenericOverIp<Ipv6, Type = Icmpv6PacketRaw<B>>;
100
101 type IcmpMessageType: IcmpMessageType
106 + GenericOverIp<Self, Type = Self::IcmpMessageType>
107 + GenericOverIp<Ipv4, Type = Icmpv4MessageType>
108 + GenericOverIp<Ipv6, Type = Icmpv6MessageType>;
109
110 type ParameterProblemCode: PartialEq + Send + Sync + Debug;
115
116 type ParameterProblemPointer: PartialEq + Send + Sync + Debug;
120
121 type HeaderLen: PartialEq + Send + Sync + Debug;
125
126 const ICMP_IP_PROTO: <Self as IpProtoExt>::Proto;
132
133 fn header_len(bytes: &[u8]) -> usize;
141
142 const ECHO_REPLY: Self::IcmpMessageType;
144 const ECHO_REQUEST: Self::IcmpMessageType;
146}
147
148impl IcmpIpExt for Ipv4 {
149 type IcmpPacketTypeRaw<B: SplitByteSliceMut> = Icmpv4PacketRaw<B>;
150 type IcmpMessageType = Icmpv4MessageType;
151 type ParameterProblemCode = Icmpv4ParameterProblemCode;
152 type ParameterProblemPointer = u8;
153 type HeaderLen = usize;
154
155 const ICMP_IP_PROTO: Ipv4Proto = Ipv4Proto::Icmp;
156
157 fn header_len(bytes: &[u8]) -> usize {
158 if bytes.len() < ipv4::IPV4_MIN_HDR_LEN {
159 return bytes.len();
160 }
161 let (header_prefix, _) = Ref::<_, ipv4::HeaderPrefix>::from_prefix(bytes).unwrap();
162 cmp::min(header_prefix.ihl() as usize * 4, bytes.len())
163 }
164
165 const ECHO_REPLY: Icmpv4MessageType = Icmpv4MessageType::EchoReply;
166 const ECHO_REQUEST: Icmpv4MessageType = Icmpv4MessageType::EchoRequest;
167}
168
169impl IcmpIpExt for Ipv6 {
170 type IcmpPacketTypeRaw<B: SplitByteSliceMut> = Icmpv6PacketRaw<B>;
171 type IcmpMessageType = Icmpv6MessageType;
172 type ParameterProblemCode = Icmpv6ParameterProblemCode;
173 type ParameterProblemPointer = u32;
174 type HeaderLen = ();
175
176 const ICMP_IP_PROTO: Ipv6Proto = Ipv6Proto::Icmpv6;
177
178 fn header_len(_bytes: &[u8]) -> usize {
181 unimplemented!()
186 }
187
188 const ECHO_REPLY: Icmpv6MessageType = Icmpv6MessageType::EchoReply;
189 const ECHO_REQUEST: Icmpv6MessageType = Icmpv6MessageType::EchoRequest;
190}
191
192pub trait IcmpPacketTypeRaw<B: SplitByteSliceMut, I: Ip>:
196 Sized + ParsablePacket<B, (), Error = ParseError>
197{
198 fn update_checksum_pseudo_header_address(&mut self, old: I::Addr, new: I::Addr);
200
201 fn update_checksum_header_field<F: IntoBytes + Immutable>(&mut self, old: F, new: F);
206
207 fn update_checksum_header_field_u16(&mut self, old: u16, new: u16) {
210 self.update_checksum_header_field(U16::new(old), U16::new(new))
211 }
212
213 fn try_write_checksum(&mut self, src_addr: I::Addr, dst_addr: I::Addr) -> bool;
218
219 fn message_body_mut(&mut self) -> &mut B;
221}
222
223impl<B: SplitByteSliceMut> IcmpPacketTypeRaw<B, Ipv4> for Icmpv4PacketRaw<B> {
224 fn update_checksum_pseudo_header_address(&mut self, old: Ipv4Addr, new: Ipv4Addr) {
225 crate::icmpv4_dispatch!(self: raw, p => p.update_checksum_pseudo_header_address(old, new))
226 }
227
228 fn update_checksum_header_field<F: IntoBytes + Immutable>(&mut self, old: F, new: F) {
229 crate::icmpv4_dispatch!(self: raw, p => p.update_checksum_header_field(old, new))
230 }
231
232 fn try_write_checksum(&mut self, src_addr: Ipv4Addr, dst_addr: Ipv4Addr) -> bool {
233 crate::icmpv4_dispatch!(self: raw, p => p.try_write_checksum(src_addr, dst_addr))
234 }
235
236 fn message_body_mut(&mut self) -> &mut B {
237 crate::icmpv4_dispatch!(self: raw, p => p.message_body_mut())
238 }
239}
240
241impl<I: IcmpIpExt, B: SplitByteSliceMut> GenericOverIp<I> for Icmpv4PacketRaw<B> {
242 type Type = I::IcmpPacketTypeRaw<B>;
243}
244
245impl<B: SplitByteSliceMut> IcmpPacketTypeRaw<B, Ipv6> for Icmpv6PacketRaw<B> {
246 fn update_checksum_pseudo_header_address(&mut self, old: Ipv6Addr, new: Ipv6Addr) {
247 crate::icmpv6_dispatch!(self: raw, p => p.update_checksum_pseudo_header_address(old, new))
248 }
249
250 fn update_checksum_header_field<F: IntoBytes + Immutable>(&mut self, old: F, new: F) {
251 crate::icmpv6_dispatch!(self: raw, p => p.update_checksum_header_field(old, new))
252 }
253
254 fn try_write_checksum(&mut self, src_addr: Ipv6Addr, dst_addr: Ipv6Addr) -> bool {
255 crate::icmpv6_dispatch!(self: raw, p => p.try_write_checksum(src_addr, dst_addr))
256 }
257
258 fn message_body_mut(&mut self) -> &mut B {
259 crate::icmpv6_dispatch!(self: raw, p => p.message_body_mut())
260 }
261}
262
263impl<I: IcmpIpExt, B: SplitByteSliceMut, M: IcmpMessage<I>> IcmpPacketTypeRaw<B, I>
264 for IcmpPacketRaw<I, B, M>
265{
266 fn update_checksum_pseudo_header_address(&mut self, old: I::Addr, new: I::Addr) {
267 match I::VERSION {
268 IpVersion::V4 => {
269 }
271 IpVersion::V6 => {
272 let checksum = &mut self.header.prefix.checksum;
273 *checksum = internet_checksum::update(*checksum, old.bytes(), new.bytes());
274 }
275 }
276 }
277
278 fn update_checksum_header_field<F: IntoBytes + Immutable>(&mut self, old: F, new: F) {
279 let checksum = &mut self.header.prefix.checksum;
280 *checksum = internet_checksum::update(*checksum, old.as_bytes(), new.as_bytes());
281 }
282
283 fn try_write_checksum(&mut self, src_addr: I::Addr, dst_addr: I::Addr) -> bool {
284 self.try_write_checksum(src_addr, dst_addr)
285 }
286
287 fn message_body_mut(&mut self) -> &mut B {
288 self.message_body_mut()
289 }
290}
291
292impl<I: IcmpIpExt, B: SplitByteSliceMut> GenericOverIp<I> for Icmpv6PacketRaw<B> {
293 type Type = I::IcmpPacketTypeRaw<B>;
294}
295
296#[derive(Derivative, Debug, Clone, Copy, PartialEq, Eq)]
298#[derivative(Default(bound = ""))]
299pub struct EmptyMessage<B>(core::marker::PhantomData<B>);
300
301pub trait MessageBody: Sized {
308 type B: SplitByteSlice;
310
311 fn parse(bytes: Self::B) -> ParseResult<Self>;
313
314 fn len(&self) -> usize;
316
317 fn is_empty(&self) -> bool {
321 self.len() == 0
322 }
323
324 fn bytes(&self) -> (&[u8], Option<&[u8]>);
337}
338
339impl<B: SplitByteSlice> MessageBody for EmptyMessage<B> {
340 type B = B;
341
342 fn parse(bytes: B) -> ParseResult<Self> {
343 if !bytes.is_empty() {
344 return debug_err!(Err(ParseError::Format), "unexpected message body");
345 }
346
347 Ok(EmptyMessage::default())
348 }
349
350 fn len(&self) -> usize {
351 0
352 }
353
354 fn bytes(&self) -> (&[u8], Option<&[u8]>) {
355 (&[], None)
356 }
357}
358
359#[derive(Debug)]
361pub struct OriginalPacket<B>(B);
362
363impl<B: SplitByteSlice> OriginalPacket<B> {
364 pub fn body<I: IcmpIpExt>(&self) -> &[u8] {
366 let header_len = I::header_len(&self.0);
368 debug_assert!(header_len <= self.0.len());
369 debug_assert!(I::VERSION.is_v6() || self.0.len() - header_len == 8);
370 &self.0[header_len..]
371 }
372}
373
374impl<B: SplitByteSlice> MessageBody for OriginalPacket<B> {
375 type B = B;
376
377 fn parse(bytes: B) -> ParseResult<OriginalPacket<B>> {
378 Ok(OriginalPacket(bytes))
379 }
380
381 fn len(&self) -> usize {
382 self.0.len()
383 }
384
385 fn bytes(&self) -> (&[u8], Option<&[u8]>) {
386 (&self.0, None)
387 }
388}
389
390impl<B: SplitByteSlice, O: OptionsImpl> MessageBody for Options<B, O> {
391 type B = B;
392 fn parse(bytes: B) -> ParseResult<Options<B, O>> {
393 Self::parse(bytes).map_err(|_e| debug_err!(ParseError::Format, "unable to parse options"))
394 }
395
396 fn len(&self) -> usize {
397 self.bytes().len()
398 }
399
400 fn bytes(&self) -> (&[u8], Option<&[u8]>) {
401 (self.bytes(), None)
402 }
403}
404
405pub trait IcmpMessage<I: IcmpIpExt>:
407 Sized + Copy + FromBytes + IntoBytes + KnownLayout + Immutable + Unaligned
408{
409 const EXPECTS_BODY: bool = true;
411
412 type Code: Into<u8> + Copy + Debug;
419
420 type Body<B: SplitByteSlice>: MessageBody<B = B>;
422
423 const TYPE: I::IcmpMessageType;
428
429 fn code_from_u8(code: u8) -> Option<Self::Code>;
435}
436
437pub trait IcmpMessageType: TryFrom<u8> + Into<u8> + Copy + Debug {
442 fn is_err(self) -> bool;
449}
450
451#[derive(Copy, Clone, Debug, KnownLayout, FromBytes, IntoBytes, Immutable, Unaligned)]
452#[repr(C)]
453struct Header<M> {
454 prefix: HeaderPrefix,
455 message: M,
456}
457
458#[derive(Debug)]
470pub struct IcmpPacketRaw<I: IcmpIpExt, B: SplitByteSlice, M: IcmpMessage<I>> {
471 header: Ref<B, Header<M>>,
472 message_body: B,
473 _marker: PhantomData<I>,
474}
475
476impl<I: IcmpIpExt, B: SplitByteSlice, M: IcmpMessage<I>> IcmpPacketRaw<I, B, M> {
477 pub fn message(&self) -> &M {
479 &self.header.message
480 }
481
482 pub fn message_body(&self) -> &B {
484 &self.message_body
485 }
486}
487
488impl<I: IcmpIpExt, B: SplitByteSliceMut, M: IcmpMessage<I>> IcmpPacketRaw<I, B, M> {
489 pub fn message_mut(&mut self) -> &mut M {
491 &mut self.header.message
492 }
493
494 pub fn message_body_mut(&mut self) -> &mut B {
496 &mut self.message_body
497 }
498
499 pub(crate) fn try_write_checksum(&mut self, src_ip: I::Addr, dst_ip: I::Addr) -> bool {
504 let original_checksum = self.header.prefix.checksum;
506 self.header.prefix.checksum = [0, 0];
507
508 if let Some(checksum) = IcmpPacket::<I, B, M>::compute_checksum(
509 &self.header,
510 &self.message_body,
511 src_ip,
512 dst_ip,
513 ) {
514 self.header.prefix.checksum = checksum;
515 true
516 } else {
517 self.header.prefix.checksum = original_checksum;
518 false
519 }
520 }
521}
522
523#[derive(Debug)]
528pub struct IcmpPacket<I: IcmpIpExt, B: SplitByteSlice, M: IcmpMessage<I>> {
529 header: Ref<B, Header<M>>,
530 message_body: M::Body<B>,
531 _marker: PhantomData<I>,
532}
533
534pub struct IcmpParseArgs<A: IpAddress> {
536 src_ip: A,
537 dst_ip: A,
538}
539
540impl<A: IpAddress> IcmpParseArgs<A> {
541 pub fn new<S: Into<A>, D: Into<A>>(src_ip: S, dst_ip: D) -> IcmpParseArgs<A> {
543 IcmpParseArgs { src_ip: src_ip.into(), dst_ip: dst_ip.into() }
544 }
545}
546
547impl<B: SplitByteSlice, I: IcmpIpExt, M: IcmpMessage<I>> ParsablePacket<B, ()>
548 for IcmpPacketRaw<I, B, M>
549{
550 type Error = ParseError;
551
552 fn parse_metadata(&self) -> ParseMetadata {
553 ParseMetadata::from_packet(Ref::bytes(&self.header).len(), self.message_body.len(), 0)
554 }
555
556 fn parse<BV: BufferView<B>>(mut buffer: BV, _args: ()) -> ParseResult<Self> {
557 let header = buffer.take_obj_front::<Header<M>>().ok_or(ParseError::Format)?;
558 let message_body = buffer.into_rest();
559 if header.prefix.msg_type != M::TYPE.into() {
560 return Err(ParseError::NotExpected);
561 }
562 Ok(IcmpPacketRaw { header, message_body, _marker: PhantomData })
563 }
564}
565
566impl<B: SplitByteSlice, I: IcmpIpExt, M: IcmpMessage<I>>
567 FromRaw<IcmpPacketRaw<I, B, M>, IcmpParseArgs<I::Addr>> for IcmpPacket<I, B, M>
568{
569 type Error = ParseError;
570
571 fn try_from_raw_with(
572 raw: IcmpPacketRaw<I, B, M>,
573 args: IcmpParseArgs<I::Addr>,
574 ) -> ParseResult<Self> {
575 let IcmpPacketRaw { header, message_body, _marker } = raw;
576 if !M::EXPECTS_BODY && !message_body.is_empty() {
577 return Err(ParseError::Format);
578 }
579 let _: M::Code = M::code_from_u8(header.prefix.code).ok_or(ParseError::Format)?;
580 let checksum = Self::compute_checksum(&header, &message_body, args.src_ip, args.dst_ip)
581 .ok_or(ParseError::Format)?;
582 if checksum != [0, 0] {
583 return Err(ParseError::Checksum);
584 }
585 let message_body = M::Body::parse(message_body)?;
586 Ok(IcmpPacket { header, message_body, _marker })
587 }
588}
589
590impl<B: SplitByteSlice, I: IcmpIpExt, M: IcmpMessage<I>> ParsablePacket<B, IcmpParseArgs<I::Addr>>
591 for IcmpPacket<I, B, M>
592{
593 type Error = ParseError;
594
595 fn parse_metadata(&self) -> ParseMetadata {
596 ParseMetadata::from_packet(Ref::bytes(&self.header).len(), self.message_body.len(), 0)
597 }
598
599 fn parse<BV: BufferView<B>>(buffer: BV, args: IcmpParseArgs<I::Addr>) -> ParseResult<Self> {
600 IcmpPacketRaw::parse(buffer, ()).and_then(|p| IcmpPacket::try_from_raw_with(p, args))
601 }
602}
603
604impl<I: IcmpIpExt, B: SplitByteSlice, M: IcmpMessage<I>> IcmpPacket<I, B, M> {
605 pub fn message(&self) -> &M {
607 &self.header.message
608 }
609
610 pub fn body(&self) -> &M::Body<B> {
612 &self.message_body
613 }
614
615 pub fn code(&self) -> M::Code {
620 M::code_from_u8(self.header.prefix.code).unwrap()
622 }
623
624 pub fn builder(&self, src_ip: I::Addr, dst_ip: I::Addr) -> IcmpPacketBuilder<I, M> {
626 IcmpPacketBuilder { src_ip, dst_ip, code: self.code(), msg: *self.message() }
627 }
628}
629
630fn compute_checksum_fragmented<I: IcmpIpExt, BB: packet::Fragment, M: IcmpMessage<I>>(
631 header: &Header<M>,
632 message_body: &FragmentedByteSlice<'_, BB>,
633 src_ip: I::Addr,
634 dst_ip: I::Addr,
635) -> Option<[u8; 2]> {
636 let mut c = Checksum::new();
637 if I::VERSION.is_v6() {
638 c.add_bytes(src_ip.bytes());
639 c.add_bytes(dst_ip.bytes());
640 let icmpv6_len = mem::size_of::<Header<M>>() + message_body.len();
641 let mut len_bytes = [0; 4];
642 NetworkEndian::write_u32(&mut len_bytes, icmpv6_len.try_into().ok()?);
643 c.add_bytes(&len_bytes[..]);
644 c.add_bytes(&[0, 0, 0]);
645 c.add_bytes(&[Ipv6Proto::Icmpv6.into()]);
646 }
647 c.add_bytes(&[header.prefix.msg_type, header.prefix.code]);
648 c.add_bytes(&header.prefix.checksum);
649 c.add_bytes(header.message.as_bytes());
650 for p in message_body.iter_fragments() {
651 c.add_bytes(p);
652 }
653 Some(c.checksum())
654}
655
656impl<I: IcmpIpExt, B: SplitByteSlice, M: IcmpMessage<I>> IcmpPacket<I, B, M> {
657 fn compute_checksum(
662 header: &Header<M>,
663 message_body: &[u8],
664 src_ip: I::Addr,
665 dst_ip: I::Addr,
666 ) -> Option<[u8; 2]> {
667 let mut body = [message_body];
668 compute_checksum_fragmented(header, &body.as_fragmented_byte_slice(), src_ip, dst_ip)
669 }
670}
671
672impl<I: IcmpIpExt, B: SplitByteSlice, M: IcmpMessage<I, Body<B> = OriginalPacket<B>>>
673 IcmpPacket<I, B, M>
674{
675 pub fn original_packet_body(&self) -> &[u8] {
683 self.message_body.body::<I>()
684 }
685
686 pub fn original_packet(&self) -> &OriginalPacket<B> {
694 &self.message_body
695 }
696}
697
698impl<B: SplitByteSlice, M: IcmpMessage<Ipv4, Body<B> = OriginalPacket<B>>> IcmpPacket<Ipv4, B, M> {
699 pub fn with_original_packet<O, F: FnOnce(Result<Ipv4PacketRaw<&[u8]>, &[u8]>) -> O>(
704 &self,
705 f: F,
706 ) -> O {
707 let mut bv = self.message_body.0.deref();
708 f(Ipv4PacketRaw::parse(&mut bv, ()).map_err(|_| self.message_body.0.deref()))
709 }
710}
711
712impl<B: SplitByteSlice, M: IcmpMessage<Ipv6, Body<B> = OriginalPacket<B>>> IcmpPacket<Ipv6, B, M> {
713 pub fn with_original_packet<O, F: FnOnce(Result<Ipv6PacketRaw<&[u8]>, &[u8]>) -> O>(
718 &self,
719 f: F,
720 ) -> O {
721 let mut bv = self.message_body.0.deref();
722 f(Ipv6PacketRaw::parse(&mut bv, ()).map_err(|_| self.message_body.0.deref()))
723 }
724}
725
726impl<I: IcmpIpExt, B: SplitByteSlice, M: IcmpMessage<I, Body<B> = ndp::Options<B>>>
727 IcmpPacket<I, B, M>
728{
729 pub fn ndp_options(&self) -> &ndp::Options<B> {
731 &self.message_body
732 }
733}
734
735#[derive(Debug, PartialEq, Clone)]
737pub struct IcmpPacketBuilder<I: IcmpIpExt, M: IcmpMessage<I>> {
738 src_ip: I::Addr,
739 dst_ip: I::Addr,
740 code: M::Code,
741 msg: M,
742}
743
744impl<I: IcmpIpExt, M: IcmpMessage<I>> IcmpPacketBuilder<I, M> {
745 pub fn new<S: Into<I::Addr>, D: Into<I::Addr>>(
747 src_ip: S,
748 dst_ip: D,
749 code: M::Code,
750 msg: M,
751 ) -> IcmpPacketBuilder<I, M> {
752 IcmpPacketBuilder { src_ip: src_ip.into(), dst_ip: dst_ip.into(), code, msg }
753 }
754
755 pub fn message(&self) -> &M {
757 &self.msg
758 }
759
760 pub fn message_mut(&mut self) -> &mut M {
762 &mut self.msg
763 }
764
765 pub fn set_src_ip(&mut self, addr: I::Addr) {
767 self.src_ip = addr;
768 }
769
770 pub fn set_dst_ip(&mut self, addr: I::Addr) {
772 self.dst_ip = addr;
773 }
774}
775
776impl<I: IcmpIpExt, M: IcmpMessage<I>> PacketBuilder for IcmpPacketBuilder<I, M> {
780 fn constraints(&self) -> PacketConstraints {
781 PacketConstraints::new(mem::size_of::<Header<M>>(), 0, 0, core::u32::MAX as usize)
794 }
795
796 fn serialize(
797 &self,
798 target: &mut SerializeTarget<'_>,
799 message_body: FragmentedBytesMut<'_, '_>,
800 ) {
801 use packet::BufferViewMut;
802
803 let mut prefix = &mut target.header;
805
806 assert!(
807 M::EXPECTS_BODY || message_body.is_empty(),
808 "body provided for message that doesn't take a body"
809 );
810 let mut header =
813 prefix.take_obj_front_zero::<Header<M>>().expect("too few bytes for ICMP message");
814 header.prefix.set_msg_type(M::TYPE);
815 header.prefix.code = self.code.into();
816 header.message = self.msg;
817 let checksum =
818 compute_checksum_fragmented(&header, &message_body, self.src_ip, self.dst_ip)
819 .unwrap_or_else(|| {
820 panic!(
821 "total ICMP packet length of {} overflows 32-bit length field of pseudo-header",
822 Ref::bytes(&header).len() + message_body.len(),
823 )
824 });
825 header.prefix.checksum = checksum;
826 }
827}
828
829#[derive(Copy, Clone, Debug, Eq, PartialEq)]
835pub struct IcmpZeroCode;
836
837impl From<IcmpZeroCode> for u8 {
838 fn from(_: IcmpZeroCode) -> u8 {
839 0
840 }
841}
842
843impl TryFrom<u8> for IcmpZeroCode {
844 type Error = NotZeroError<u8>;
845
846 fn try_from(value: u8) -> Result<Self, NotZeroError<u8>> {
847 if value == 0 {
848 Ok(Self)
849 } else {
850 Err(NotZeroError(value))
851 }
852 }
853}
854
855#[derive(Copy, Clone, Debug, Eq, PartialEq)]
860pub struct IcmpSenderZeroCode;
861
862impl From<IcmpSenderZeroCode> for u8 {
863 fn from(_: IcmpSenderZeroCode) -> u8 {
864 0
865 }
866}
867
868impl From<u8> for IcmpSenderZeroCode {
869 fn from(_: u8) -> Self {
870 Self
871 }
872}
873
874#[doc(hidden)]
879#[derive(
880 Copy, Clone, Debug, Eq, PartialEq, KnownLayout, FromBytes, IntoBytes, Immutable, Unaligned,
881)]
882#[repr(C)]
883pub struct IdAndSeq {
884 id: U16,
885 seq: U16,
886}
887
888impl IdAndSeq {
889 fn new(id: u16, seq: u16) -> IdAndSeq {
890 IdAndSeq { id: U16::new(id), seq: U16::new(seq) }
891 }
892}
893
894#[cfg(test)]
895mod tests {
896 use ip_test_macro::ip_test;
897 use packet::{InnerPacketBuilder, ParseBuffer, Serializer, SliceBufViewMut};
898 use test_case::test_case;
899
900 use super::*;
901
902 #[test]
903 fn test_partial_parse() {
904 let reference_header = Header {
907 prefix: HeaderPrefix {
908 msg_type: <IcmpEchoRequest as IcmpMessage<Ipv4>>::TYPE.into(),
909 code: 0,
910 checksum: [0, 0],
911 },
912 message: IcmpEchoRequest::new(1, 1),
913 };
914
915 let mut buf = &reference_header.as_bytes()[..7];
920 assert_eq!(
921 buf.parse::<IcmpPacketRaw<Ipv4, _, IcmpEchoRequest>>().unwrap_err(),
922 ParseError::Format
923 );
924
925 let mut header = reference_header;
927 header.prefix.msg_type = <IcmpEchoReply as IcmpMessage<Ipv4>>::TYPE.into();
928 let mut buf = header.as_bytes();
929 assert_eq!(
930 buf.parse::<IcmpPacketRaw<Ipv4, _, IcmpEchoRequest>>().unwrap_err(),
931 ParseError::NotExpected
932 );
933
934 let mut header = reference_header;
936 header.prefix.code = 0xFF;
937 let mut buf = header.as_bytes();
938 assert!(buf.parse::<IcmpPacketRaw<Ipv4, _, IcmpEchoRequest>>().is_ok());
939
940 let mut buf = reference_header.as_bytes();
944 assert!(buf.parse::<IcmpPacketRaw<Ipv4, _, IcmpEchoRequest>>().is_ok());
945 let mut header = reference_header;
946 header.prefix.checksum = [1, 1];
947 let mut buf = header.as_bytes();
948 assert!(buf.parse::<IcmpPacketRaw<Ipv4, _, IcmpEchoRequest>>().is_ok());
949 }
950
951 #[ip_test(I)]
952 #[test_case([0,0]; "zeroed_checksum")]
953 #[test_case([123, 234]; "garbage_checksum")]
954 fn test_try_write_checksum<I: IcmpIpExt>(corrupt_checksum: [u8; 2]) {
955 let icmp_message_with_checksum = []
958 .into_serializer()
959 .encapsulate(IcmpPacketBuilder::<I, _>::new(
960 *I::LOOPBACK_ADDRESS,
961 *I::LOOPBACK_ADDRESS,
962 IcmpZeroCode,
963 IcmpEchoRequest::new(1, 1),
964 ))
965 .serialize_vec_outer()
966 .unwrap()
967 .as_ref()
968 .to_vec();
969
970 let mut icmp_message_without_checksum = icmp_message_with_checksum.clone();
972 {
973 let buf = SliceBufViewMut::new(&mut icmp_message_without_checksum);
974 let mut message = IcmpPacketRaw::<I, _, IcmpEchoRequest>::parse_mut(buf, ())
975 .expect("parse packet raw should succeed");
976 message.header.prefix.checksum = corrupt_checksum;
977 }
978 assert_ne!(&icmp_message_with_checksum[..], &icmp_message_without_checksum[..]);
979
980 let buf = SliceBufViewMut::new(&mut icmp_message_without_checksum);
982 let mut message = IcmpPacketRaw::<I, _, IcmpEchoRequest>::parse_mut(buf, ())
983 .expect("parse packet raw should succeed");
984 assert!(message.try_write_checksum(*I::LOOPBACK_ADDRESS, *I::LOOPBACK_ADDRESS));
985 assert_eq!(&icmp_message_with_checksum[..], &icmp_message_without_checksum[..]);
986 }
987}