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, PartialPacketBuilder,
43 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 ParameterProblemCode: PartialEq + Send + Sync + Debug;
116
117 type ParameterProblemPointer: PartialEq + Send + Sync + Debug;
121
122 const ICMP_IP_PROTO: <Self as IpProtoExt>::Proto;
128
129 fn header_len(bytes: &[u8]) -> usize;
137
138 const ECHO_REPLY: Self::IcmpMessageType;
140 const ECHO_REQUEST: Self::IcmpMessageType;
142}
143
144impl IcmpIpExt for Ipv4 {
145 type IcmpPacketTypeRaw<B: SplitByteSliceMut> = Icmpv4PacketRaw<B>;
146 type IcmpMessageType = Icmpv4MessageType;
147 type ParameterProblemCode = Icmpv4ParameterProblemCode;
148 type ParameterProblemPointer = u8;
149
150 const ICMP_IP_PROTO: Ipv4Proto = Ipv4Proto::Icmp;
151
152 fn header_len(bytes: &[u8]) -> usize {
153 if bytes.len() < ipv4::IPV4_MIN_HDR_LEN {
154 return bytes.len();
155 }
156 let (header_prefix, _) = Ref::<_, ipv4::HeaderPrefix>::from_prefix(bytes).unwrap();
157 cmp::min(header_prefix.ihl() as usize * 4, bytes.len())
158 }
159
160 const ECHO_REPLY: Icmpv4MessageType = Icmpv4MessageType::EchoReply;
161 const ECHO_REQUEST: Icmpv4MessageType = Icmpv4MessageType::EchoRequest;
162}
163
164impl IcmpIpExt for Ipv6 {
165 type IcmpPacketTypeRaw<B: SplitByteSliceMut> = Icmpv6PacketRaw<B>;
166 type IcmpMessageType = Icmpv6MessageType;
167 type ParameterProblemCode = Icmpv6ParameterProblemCode;
168 type ParameterProblemPointer = u32;
169
170 const ICMP_IP_PROTO: Ipv6Proto = Ipv6Proto::Icmpv6;
171
172 fn header_len(_bytes: &[u8]) -> usize {
175 unimplemented!()
180 }
181
182 const ECHO_REPLY: Icmpv6MessageType = Icmpv6MessageType::EchoReply;
183 const ECHO_REQUEST: Icmpv6MessageType = Icmpv6MessageType::EchoRequest;
184}
185
186pub trait IcmpPacketTypeRaw<B: SplitByteSliceMut, I: Ip>:
190 Sized + ParsablePacket<B, (), Error = ParseError>
191{
192 fn update_checksum_pseudo_header_address(&mut self, old: I::Addr, new: I::Addr);
194
195 fn update_checksum_header_field<F: IntoBytes + Immutable>(&mut self, old: F, new: F);
200
201 fn update_checksum_header_field_u16(&mut self, old: u16, new: u16) {
204 self.update_checksum_header_field(U16::new(old), U16::new(new))
205 }
206
207 fn try_write_checksum(&mut self, src_addr: I::Addr, dst_addr: I::Addr) -> bool;
212
213 fn message_body_mut(&mut self) -> &mut B;
215}
216
217impl<B: SplitByteSliceMut> IcmpPacketTypeRaw<B, Ipv4> for Icmpv4PacketRaw<B> {
218 fn update_checksum_pseudo_header_address(&mut self, old: Ipv4Addr, new: Ipv4Addr) {
219 crate::icmpv4_dispatch!(self: raw, p => p.update_checksum_pseudo_header_address(old, new))
220 }
221
222 fn update_checksum_header_field<F: IntoBytes + Immutable>(&mut self, old: F, new: F) {
223 crate::icmpv4_dispatch!(self: raw, p => p.update_checksum_header_field(old, new))
224 }
225
226 fn try_write_checksum(&mut self, src_addr: Ipv4Addr, dst_addr: Ipv4Addr) -> bool {
227 crate::icmpv4_dispatch!(self: raw, p => p.try_write_checksum(src_addr, dst_addr))
228 }
229
230 fn message_body_mut(&mut self) -> &mut B {
231 crate::icmpv4_dispatch!(self: raw, p => p.message_body_mut())
232 }
233}
234
235impl<I: IcmpIpExt, B: SplitByteSliceMut> GenericOverIp<I> for Icmpv4PacketRaw<B> {
236 type Type = I::IcmpPacketTypeRaw<B>;
237}
238
239impl<B: SplitByteSliceMut> IcmpPacketTypeRaw<B, Ipv6> for Icmpv6PacketRaw<B> {
240 fn update_checksum_pseudo_header_address(&mut self, old: Ipv6Addr, new: Ipv6Addr) {
241 crate::icmpv6_dispatch!(self: raw, p => p.update_checksum_pseudo_header_address(old, new))
242 }
243
244 fn update_checksum_header_field<F: IntoBytes + Immutable>(&mut self, old: F, new: F) {
245 crate::icmpv6_dispatch!(self: raw, p => p.update_checksum_header_field(old, new))
246 }
247
248 fn try_write_checksum(&mut self, src_addr: Ipv6Addr, dst_addr: Ipv6Addr) -> bool {
249 crate::icmpv6_dispatch!(self: raw, p => p.try_write_checksum(src_addr, dst_addr))
250 }
251
252 fn message_body_mut(&mut self) -> &mut B {
253 crate::icmpv6_dispatch!(self: raw, p => p.message_body_mut())
254 }
255}
256
257impl<I: IcmpIpExt, B: SplitByteSliceMut, M: IcmpMessage<I>> IcmpPacketTypeRaw<B, I>
258 for IcmpPacketRaw<I, B, M>
259{
260 fn update_checksum_pseudo_header_address(&mut self, old: I::Addr, new: I::Addr) {
261 match I::VERSION {
262 IpVersion::V4 => {
263 }
265 IpVersion::V6 => {
266 let checksum = &mut self.header.prefix.checksum;
267 *checksum = internet_checksum::update(*checksum, old.bytes(), new.bytes());
268 }
269 }
270 }
271
272 fn update_checksum_header_field<F: IntoBytes + Immutable>(&mut self, old: F, new: F) {
273 let checksum = &mut self.header.prefix.checksum;
274 *checksum = internet_checksum::update(*checksum, old.as_bytes(), new.as_bytes());
275 }
276
277 fn try_write_checksum(&mut self, src_addr: I::Addr, dst_addr: I::Addr) -> bool {
278 self.try_write_checksum(src_addr, dst_addr)
279 }
280
281 fn message_body_mut(&mut self) -> &mut B {
282 self.message_body_mut()
283 }
284}
285
286impl<I: IcmpIpExt, B: SplitByteSliceMut> GenericOverIp<I> for Icmpv6PacketRaw<B> {
287 type Type = I::IcmpPacketTypeRaw<B>;
288}
289
290#[derive(Derivative, Debug, Clone, Copy, PartialEq, Eq)]
292#[derivative(Default(bound = ""))]
293pub struct EmptyMessage<B>(core::marker::PhantomData<B>);
294
295pub trait MessageBody: Sized {
302 type B: SplitByteSlice;
304
305 fn parse(bytes: Self::B) -> ParseResult<Self>;
307
308 fn len(&self) -> usize;
310
311 fn is_empty(&self) -> bool {
315 self.len() == 0
316 }
317
318 fn bytes(&self) -> (&[u8], Option<&[u8]>);
331}
332
333impl<B: SplitByteSlice> MessageBody for EmptyMessage<B> {
334 type B = B;
335
336 fn parse(bytes: B) -> ParseResult<Self> {
337 if !bytes.is_empty() {
338 return debug_err!(Err(ParseError::Format), "unexpected message body");
339 }
340
341 Ok(EmptyMessage::default())
342 }
343
344 fn len(&self) -> usize {
345 0
346 }
347
348 fn bytes(&self) -> (&[u8], Option<&[u8]>) {
349 (&[], None)
350 }
351}
352
353#[derive(Debug)]
355pub struct OriginalPacket<B>(B);
356
357impl<B: SplitByteSlice> OriginalPacket<B> {
358 pub fn body<I: IcmpIpExt>(&self) -> &[u8] {
360 let header_len = I::header_len(&self.0);
362 debug_assert!(header_len <= self.0.len());
363 debug_assert!(I::VERSION.is_v6() || self.0.len() - header_len == 8);
364 &self.0[header_len..]
365 }
366}
367
368impl<B: SplitByteSlice> MessageBody for OriginalPacket<B> {
369 type B = B;
370
371 fn parse(bytes: B) -> ParseResult<OriginalPacket<B>> {
372 Ok(OriginalPacket(bytes))
373 }
374
375 fn len(&self) -> usize {
376 self.0.len()
377 }
378
379 fn bytes(&self) -> (&[u8], Option<&[u8]>) {
380 (&self.0, None)
381 }
382}
383
384impl<B: SplitByteSlice, O: OptionsImpl> MessageBody for Options<B, O> {
385 type B = B;
386 fn parse(bytes: B) -> ParseResult<Options<B, O>> {
387 Self::parse(bytes).map_err(|_e| debug_err!(ParseError::Format, "unable to parse options"))
388 }
389
390 fn len(&self) -> usize {
391 self.bytes().len()
392 }
393
394 fn bytes(&self) -> (&[u8], Option<&[u8]>) {
395 (self.bytes(), None)
396 }
397}
398
399pub trait IcmpMessage<I: IcmpIpExt>:
401 Sized + Copy + FromBytes + IntoBytes + KnownLayout + Immutable + Unaligned
402{
403 const EXPECTS_BODY: bool = true;
405
406 type Code: Into<u8> + Copy + Debug;
413
414 type Body<B: SplitByteSlice>: MessageBody<B = B>;
416
417 const TYPE: I::IcmpMessageType;
422
423 fn code_from_u8(code: u8) -> Option<Self::Code>;
429}
430
431pub trait IcmpMessageType: TryFrom<u8> + Into<u8> + Copy + Debug {
436 fn is_error_or_redirect(self) -> bool;
443}
444
445#[derive(Copy, Clone, Debug, KnownLayout, FromBytes, IntoBytes, Immutable, Unaligned)]
446#[repr(C)]
447struct Header<M> {
448 prefix: HeaderPrefix,
449 message: M,
450}
451
452#[derive(Debug)]
464pub struct IcmpPacketRaw<I: IcmpIpExt, B: SplitByteSlice, M: IcmpMessage<I>> {
465 header: Ref<B, Header<M>>,
466 message_body: B,
467 _marker: PhantomData<I>,
468}
469
470impl<I: IcmpIpExt, B: SplitByteSlice, M: IcmpMessage<I>> IcmpPacketRaw<I, B, M> {
471 pub fn message(&self) -> &M {
473 &self.header.message
474 }
475
476 pub fn message_body(&self) -> &B {
478 &self.message_body
479 }
480}
481
482impl<I: IcmpIpExt, B: SplitByteSliceMut, M: IcmpMessage<I>> IcmpPacketRaw<I, B, M> {
483 pub fn message_mut(&mut self) -> &mut M {
485 &mut self.header.message
486 }
487
488 pub fn message_body_mut(&mut self) -> &mut B {
490 &mut self.message_body
491 }
492
493 pub(crate) fn try_write_checksum(&mut self, src_ip: I::Addr, dst_ip: I::Addr) -> bool {
498 let original_checksum = self.header.prefix.checksum;
500 self.header.prefix.checksum = [0, 0];
501
502 if let Some(checksum) = IcmpPacket::<I, B, M>::compute_checksum(
503 &self.header,
504 &self.message_body,
505 src_ip,
506 dst_ip,
507 ) {
508 self.header.prefix.checksum = checksum;
509 true
510 } else {
511 self.header.prefix.checksum = original_checksum;
512 false
513 }
514 }
515}
516
517#[derive(Debug)]
522pub struct IcmpPacket<I: IcmpIpExt, B: SplitByteSlice, M: IcmpMessage<I>> {
523 header: Ref<B, Header<M>>,
524 message_body: M::Body<B>,
525 _marker: PhantomData<I>,
526}
527
528pub struct IcmpParseArgs<A: IpAddress> {
530 src_ip: A,
531 dst_ip: A,
532}
533
534impl<A: IpAddress> IcmpParseArgs<A> {
535 pub fn new<S: Into<A>, D: Into<A>>(src_ip: S, dst_ip: D) -> IcmpParseArgs<A> {
537 IcmpParseArgs { src_ip: src_ip.into(), dst_ip: dst_ip.into() }
538 }
539}
540
541impl<B: SplitByteSlice, I: IcmpIpExt, M: IcmpMessage<I>> ParsablePacket<B, ()>
542 for IcmpPacketRaw<I, B, M>
543{
544 type Error = ParseError;
545
546 fn parse_metadata(&self) -> ParseMetadata {
547 ParseMetadata::from_packet(Ref::bytes(&self.header).len(), self.message_body.len(), 0)
548 }
549
550 fn parse<BV: BufferView<B>>(mut buffer: BV, _args: ()) -> ParseResult<Self> {
551 let header = buffer.take_obj_front::<Header<M>>().ok_or(ParseError::Format)?;
552 let message_body = buffer.into_rest();
553 if header.prefix.msg_type != M::TYPE.into() {
554 return Err(ParseError::NotExpected);
555 }
556 Ok(IcmpPacketRaw { header, message_body, _marker: PhantomData })
557 }
558}
559
560impl<B: SplitByteSlice, I: IcmpIpExt, M: IcmpMessage<I>>
561 FromRaw<IcmpPacketRaw<I, B, M>, IcmpParseArgs<I::Addr>> for IcmpPacket<I, B, M>
562{
563 type Error = ParseError;
564
565 fn try_from_raw_with(
566 raw: IcmpPacketRaw<I, B, M>,
567 args: IcmpParseArgs<I::Addr>,
568 ) -> ParseResult<Self> {
569 let IcmpPacketRaw { header, message_body, _marker } = raw;
570 if !M::EXPECTS_BODY && !message_body.is_empty() {
571 return Err(ParseError::Format);
572 }
573 let _: M::Code = M::code_from_u8(header.prefix.code).ok_or(ParseError::Format)?;
574 let checksum = Self::compute_checksum(&header, &message_body, args.src_ip, args.dst_ip)
575 .ok_or(ParseError::Format)?;
576 if checksum != [0, 0] {
577 return Err(ParseError::Checksum);
578 }
579 let message_body = M::Body::parse(message_body)?;
580 Ok(IcmpPacket { header, message_body, _marker })
581 }
582}
583
584impl<B: SplitByteSlice, I: IcmpIpExt, M: IcmpMessage<I>> ParsablePacket<B, IcmpParseArgs<I::Addr>>
585 for IcmpPacket<I, B, M>
586{
587 type Error = ParseError;
588
589 fn parse_metadata(&self) -> ParseMetadata {
590 ParseMetadata::from_packet(Ref::bytes(&self.header).len(), self.message_body.len(), 0)
591 }
592
593 fn parse<BV: BufferView<B>>(buffer: BV, args: IcmpParseArgs<I::Addr>) -> ParseResult<Self> {
594 IcmpPacketRaw::parse(buffer, ()).and_then(|p| IcmpPacket::try_from_raw_with(p, args))
595 }
596}
597
598impl<I: IcmpIpExt, B: SplitByteSlice, M: IcmpMessage<I>> IcmpPacket<I, B, M> {
599 pub fn message(&self) -> &M {
601 &self.header.message
602 }
603
604 pub fn body(&self) -> &M::Body<B> {
606 &self.message_body
607 }
608
609 pub fn code(&self) -> M::Code {
614 M::code_from_u8(self.header.prefix.code).unwrap()
616 }
617
618 pub fn builder(&self, src_ip: I::Addr, dst_ip: I::Addr) -> IcmpPacketBuilder<I, M> {
620 IcmpPacketBuilder { src_ip, dst_ip, code: self.code(), msg: *self.message() }
621 }
622}
623
624fn compute_checksum_fragmented<I: IcmpIpExt, BB: packet::Fragment, M: IcmpMessage<I>>(
625 header: &Header<M>,
626 message_body: &FragmentedByteSlice<'_, BB>,
627 src_ip: I::Addr,
628 dst_ip: I::Addr,
629) -> Option<[u8; 2]> {
630 let mut c = Checksum::new();
631 if I::VERSION.is_v6() {
632 c.add_bytes(src_ip.bytes());
633 c.add_bytes(dst_ip.bytes());
634 let icmpv6_len = mem::size_of::<Header<M>>() + message_body.len();
635 let mut len_bytes = [0; 4];
636 NetworkEndian::write_u32(&mut len_bytes, icmpv6_len.try_into().ok()?);
637 c.add_bytes(&len_bytes[..]);
638 c.add_bytes(&[0, 0, 0]);
639 c.add_bytes(&[Ipv6Proto::Icmpv6.into()]);
640 }
641 c.add_bytes(&[header.prefix.msg_type, header.prefix.code]);
642 c.add_bytes(&header.prefix.checksum);
643 c.add_bytes(header.message.as_bytes());
644 for p in message_body.iter_fragments() {
645 c.add_bytes(p);
646 }
647 Some(c.checksum())
648}
649
650impl<I: IcmpIpExt, B: SplitByteSlice, M: IcmpMessage<I>> IcmpPacket<I, B, M> {
651 fn compute_checksum(
656 header: &Header<M>,
657 message_body: &[u8],
658 src_ip: I::Addr,
659 dst_ip: I::Addr,
660 ) -> Option<[u8; 2]> {
661 let mut body = [message_body];
662 compute_checksum_fragmented(header, &body.as_fragmented_byte_slice(), src_ip, dst_ip)
663 }
664}
665
666impl<I: IcmpIpExt, B: SplitByteSlice, M: IcmpMessage<I, Body<B> = OriginalPacket<B>>>
667 IcmpPacket<I, B, M>
668{
669 pub fn original_packet_body(&self) -> &[u8] {
677 self.message_body.body::<I>()
678 }
679
680 pub fn original_packet(&self) -> &OriginalPacket<B> {
688 &self.message_body
689 }
690}
691
692impl<B: SplitByteSlice, M: IcmpMessage<Ipv4, Body<B> = OriginalPacket<B>>> IcmpPacket<Ipv4, B, M> {
693 pub fn with_original_packet<O, F: FnOnce(Result<Ipv4PacketRaw<&[u8]>, &[u8]>) -> O>(
698 &self,
699 f: F,
700 ) -> O {
701 let mut bv = self.message_body.0.deref();
702 f(Ipv4PacketRaw::parse(&mut bv, ()).map_err(|_| self.message_body.0.deref()))
703 }
704}
705
706impl<B: SplitByteSlice, M: IcmpMessage<Ipv6, Body<B> = OriginalPacket<B>>> IcmpPacket<Ipv6, B, M> {
707 pub fn with_original_packet<O, F: FnOnce(Result<Ipv6PacketRaw<&[u8]>, &[u8]>) -> O>(
712 &self,
713 f: F,
714 ) -> O {
715 let mut bv = self.message_body.0.deref();
716 f(Ipv6PacketRaw::parse(&mut bv, ()).map_err(|_| self.message_body.0.deref()))
717 }
718}
719
720impl<I: IcmpIpExt, B: SplitByteSlice, M: IcmpMessage<I, Body<B> = ndp::Options<B>>>
721 IcmpPacket<I, B, M>
722{
723 pub fn ndp_options(&self) -> &ndp::Options<B> {
725 &self.message_body
726 }
727}
728
729#[derive(Debug, PartialEq, Clone)]
731pub struct IcmpPacketBuilder<I: IcmpIpExt, M: IcmpMessage<I>> {
732 src_ip: I::Addr,
733 dst_ip: I::Addr,
734 code: M::Code,
735 msg: M,
736}
737
738impl<I: IcmpIpExt, M: IcmpMessage<I>> IcmpPacketBuilder<I, M> {
739 pub fn new<S: Into<I::Addr>, D: Into<I::Addr>>(
741 src_ip: S,
742 dst_ip: D,
743 code: M::Code,
744 msg: M,
745 ) -> IcmpPacketBuilder<I, M> {
746 IcmpPacketBuilder { src_ip: src_ip.into(), dst_ip: dst_ip.into(), code, msg }
747 }
748
749 pub fn message(&self) -> &M {
751 &self.msg
752 }
753
754 pub fn message_mut(&mut self) -> &mut M {
756 &mut self.msg
757 }
758
759 pub fn set_src_ip(&mut self, addr: I::Addr) {
761 self.src_ip = addr;
762 }
763
764 pub fn set_dst_ip(&mut self, addr: I::Addr) {
766 self.dst_ip = addr;
767 }
768
769 fn serialize_header(
770 &self,
771 mut header: &mut [u8],
772 message_body: Option<FragmentedBytesMut<'_, '_>>,
773 ) {
774 use packet::BufferViewMut;
775
776 let mut prefix = &mut header;
778
779 let mut header =
782 prefix.take_obj_front_zero::<Header<M>>().expect("too few bytes for ICMP message");
783 header.prefix.set_msg_type(M::TYPE);
784 header.prefix.code = self.code.into();
785 header.message = self.msg;
786
787 if let Some(message_body) = message_body {
788 assert!(
789 M::EXPECTS_BODY || message_body.is_empty(),
790 "body provided for message that doesn't take a body"
791 );
792 let checksum = compute_checksum_fragmented(
793 &header,
794 &message_body,
795 self.src_ip,
796 self.dst_ip,
797 )
798 .unwrap_or_else(|| {
799 panic!(
800 "total ICMP packet length of {} overflows 32-bit length field of pseudo-header",
801 Ref::bytes(&header).len() + message_body.len(),
802 )
803 });
804 header.prefix.checksum = checksum;
805 }
806 }
807}
808
809impl<I: IcmpIpExt, M: IcmpMessage<I>> PacketBuilder for IcmpPacketBuilder<I, M> {
813 fn constraints(&self) -> PacketConstraints {
814 PacketConstraints::new(mem::size_of::<Header<M>>(), 0, 0, core::u32::MAX as usize)
827 }
828
829 fn serialize(
830 &self,
831 target: &mut SerializeTarget<'_>,
832 message_body: FragmentedBytesMut<'_, '_>,
833 ) {
834 self.serialize_header(target.header, Some(message_body));
835 }
836}
837
838impl<I: IcmpIpExt, M: IcmpMessage<I>> PartialPacketBuilder for IcmpPacketBuilder<I, M> {
839 fn partial_serialize(&self, _body_len: usize, buffer: &mut [u8]) {
840 self.serialize_header(buffer, None);
841 }
842}
843
844#[derive(Copy, Clone, Debug, Eq, PartialEq)]
850pub struct IcmpZeroCode;
851
852impl From<IcmpZeroCode> for u8 {
853 fn from(_: IcmpZeroCode) -> u8 {
854 0
855 }
856}
857
858impl TryFrom<u8> for IcmpZeroCode {
859 type Error = NotZeroError<u8>;
860
861 fn try_from(value: u8) -> Result<Self, NotZeroError<u8>> {
862 if value == 0 { Ok(Self) } else { Err(NotZeroError(value)) }
863 }
864}
865
866#[derive(Copy, Clone, Debug, Eq, PartialEq)]
871pub struct IcmpSenderZeroCode;
872
873impl From<IcmpSenderZeroCode> for u8 {
874 fn from(_: IcmpSenderZeroCode) -> u8 {
875 0
876 }
877}
878
879impl From<u8> for IcmpSenderZeroCode {
880 fn from(_: u8) -> Self {
881 Self
882 }
883}
884
885#[doc(hidden)]
890#[derive(
891 Copy, Clone, Debug, Eq, PartialEq, KnownLayout, FromBytes, IntoBytes, Immutable, Unaligned,
892)]
893#[repr(C)]
894pub struct IdAndSeq {
895 id: U16,
896 seq: U16,
897}
898
899impl IdAndSeq {
900 fn new(id: u16, seq: u16) -> IdAndSeq {
901 IdAndSeq { id: U16::new(id), seq: U16::new(seq) }
902 }
903}
904
905#[cfg(test)]
906mod tests {
907 use ip_test_macro::ip_test;
908 use packet::{EmptyBuf, ParseBuffer, Serializer, SliceBufViewMut};
909 use test_case::test_case;
910
911 use super::*;
912
913 #[test]
914 fn test_partial_parse() {
915 let reference_header = Header {
918 prefix: HeaderPrefix {
919 msg_type: <IcmpEchoRequest as IcmpMessage<Ipv4>>::TYPE.into(),
920 code: 0,
921 checksum: [0, 0],
922 },
923 message: IcmpEchoRequest::new(1, 1),
924 };
925
926 let mut buf = &reference_header.as_bytes()[..7];
931 assert_eq!(
932 buf.parse::<IcmpPacketRaw<Ipv4, _, IcmpEchoRequest>>().unwrap_err(),
933 ParseError::Format
934 );
935
936 let mut header = reference_header;
938 header.prefix.msg_type = <IcmpEchoReply as IcmpMessage<Ipv4>>::TYPE.into();
939 let mut buf = header.as_bytes();
940 assert_eq!(
941 buf.parse::<IcmpPacketRaw<Ipv4, _, IcmpEchoRequest>>().unwrap_err(),
942 ParseError::NotExpected
943 );
944
945 let mut header = reference_header;
947 header.prefix.code = 0xFF;
948 let mut buf = header.as_bytes();
949 assert!(buf.parse::<IcmpPacketRaw<Ipv4, _, IcmpEchoRequest>>().is_ok());
950
951 let mut buf = reference_header.as_bytes();
955 assert!(buf.parse::<IcmpPacketRaw<Ipv4, _, IcmpEchoRequest>>().is_ok());
956 let mut header = reference_header;
957 header.prefix.checksum = [1, 1];
958 let mut buf = header.as_bytes();
959 assert!(buf.parse::<IcmpPacketRaw<Ipv4, _, IcmpEchoRequest>>().is_ok());
960 }
961
962 #[ip_test(I)]
963 #[test_case([0,0]; "zeroed_checksum")]
964 #[test_case([123, 234]; "garbage_checksum")]
965 fn test_try_write_checksum<I: IcmpIpExt>(corrupt_checksum: [u8; 2]) {
966 let icmp_message_with_checksum = IcmpPacketBuilder::<I, _>::new(
969 *I::LOOPBACK_ADDRESS,
970 *I::LOOPBACK_ADDRESS,
971 IcmpZeroCode,
972 IcmpEchoRequest::new(1, 1),
973 )
974 .wrap_body(EmptyBuf)
975 .serialize_vec_outer()
976 .unwrap()
977 .as_ref()
978 .to_vec();
979
980 let mut icmp_message_without_checksum = icmp_message_with_checksum.clone();
982 {
983 let buf = SliceBufViewMut::new(&mut icmp_message_without_checksum);
984 let mut message = IcmpPacketRaw::<I, _, IcmpEchoRequest>::parse_mut(buf, ())
985 .expect("parse packet raw should succeed");
986 message.header.prefix.checksum = corrupt_checksum;
987 }
988 assert_ne!(&icmp_message_with_checksum[..], &icmp_message_without_checksum[..]);
989
990 let buf = SliceBufViewMut::new(&mut icmp_message_without_checksum);
992 let mut message = IcmpPacketRaw::<I, _, IcmpEchoRequest>::parse_mut(buf, ())
993 .expect("parse packet raw should succeed");
994 assert!(message.try_write_checksum(*I::LOOPBACK_ADDRESS, *I::LOOPBACK_ADDRESS));
995 assert_eq!(&icmp_message_with_checksum[..], &icmp_message_without_checksum[..]);
996 }
997}