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
214impl<B: SplitByteSliceMut> IcmpPacketTypeRaw<B, Ipv4> for Icmpv4PacketRaw<B> {
215 fn update_checksum_pseudo_header_address(&mut self, old: Ipv4Addr, new: Ipv4Addr) {
216 crate::icmpv4_dispatch!(self: raw, p => p.update_checksum_pseudo_header_address(old, new))
217 }
218
219 fn update_checksum_header_field<F: IntoBytes + Immutable>(&mut self, old: F, new: F) {
220 crate::icmpv4_dispatch!(self: raw, p => p.update_checksum_header_field(old, new))
221 }
222}
223
224impl<I: IcmpIpExt, B: SplitByteSliceMut> GenericOverIp<I> for Icmpv4PacketRaw<B> {
225 type Type = I::IcmpPacketTypeRaw<B>;
226}
227
228impl<B: SplitByteSliceMut> IcmpPacketTypeRaw<B, Ipv6> for Icmpv6PacketRaw<B> {
229 fn update_checksum_pseudo_header_address(&mut self, old: Ipv6Addr, new: Ipv6Addr) {
230 crate::icmpv6_dispatch!(self: raw, p => p.update_checksum_pseudo_header_address(old, new))
231 }
232
233 fn update_checksum_header_field<F: IntoBytes + Immutable>(&mut self, old: F, new: F) {
234 crate::icmpv6_dispatch!(self: raw, p => p.update_checksum_header_field(old, new))
235 }
236}
237
238impl<I: IcmpIpExt, B: SplitByteSliceMut, M: IcmpMessage<I>> IcmpPacketTypeRaw<B, I>
239 for IcmpPacketRaw<I, B, M>
240{
241 fn update_checksum_pseudo_header_address(&mut self, old: I::Addr, new: I::Addr) {
242 match I::VERSION {
243 IpVersion::V4 => {
244 }
246 IpVersion::V6 => {
247 let checksum = &mut self.header.prefix.checksum;
248 *checksum = internet_checksum::update(*checksum, old.bytes(), new.bytes());
249 }
250 }
251 }
252
253 fn update_checksum_header_field<F: IntoBytes + Immutable>(&mut self, old: F, new: F) {
254 let checksum = &mut self.header.prefix.checksum;
255 *checksum = internet_checksum::update(*checksum, old.as_bytes(), new.as_bytes());
256 }
257}
258
259impl<I: IcmpIpExt, B: SplitByteSliceMut> GenericOverIp<I> for Icmpv6PacketRaw<B> {
260 type Type = I::IcmpPacketTypeRaw<B>;
261}
262
263#[derive(Derivative, Debug, Clone, Copy, PartialEq, Eq)]
265#[derivative(Default(bound = ""))]
266pub struct EmptyMessage<B>(core::marker::PhantomData<B>);
267
268pub trait MessageBody: Sized {
275 type B: SplitByteSlice;
277
278 fn parse(bytes: Self::B) -> ParseResult<Self>;
280
281 fn len(&self) -> usize;
283
284 fn is_empty(&self) -> bool {
288 self.len() == 0
289 }
290
291 fn bytes(&self) -> (&[u8], Option<&[u8]>);
304}
305
306impl<B: SplitByteSlice> MessageBody for EmptyMessage<B> {
307 type B = B;
308
309 fn parse(bytes: B) -> ParseResult<Self> {
310 if !bytes.is_empty() {
311 return debug_err!(Err(ParseError::Format), "unexpected message body");
312 }
313
314 Ok(EmptyMessage::default())
315 }
316
317 fn len(&self) -> usize {
318 0
319 }
320
321 fn bytes(&self) -> (&[u8], Option<&[u8]>) {
322 (&[], None)
323 }
324}
325
326#[derive(Debug)]
328pub struct OriginalPacket<B>(B);
329
330impl<B: SplitByteSlice> OriginalPacket<B> {
331 pub fn body<I: IcmpIpExt>(&self) -> &[u8] {
333 let header_len = I::header_len(&self.0);
335 debug_assert!(header_len <= self.0.len());
336 debug_assert!(I::VERSION.is_v6() || self.0.len() - header_len == 8);
337 &self.0[header_len..]
338 }
339}
340
341impl<B: SplitByteSlice> MessageBody for OriginalPacket<B> {
342 type B = B;
343
344 fn parse(bytes: B) -> ParseResult<OriginalPacket<B>> {
345 Ok(OriginalPacket(bytes))
346 }
347
348 fn len(&self) -> usize {
349 self.0.len()
350 }
351
352 fn bytes(&self) -> (&[u8], Option<&[u8]>) {
353 (&self.0, None)
354 }
355}
356
357impl<B: SplitByteSlice, O: OptionsImpl> MessageBody for Options<B, O> {
358 type B = B;
359 fn parse(bytes: B) -> ParseResult<Options<B, O>> {
360 Self::parse(bytes).map_err(|_e| debug_err!(ParseError::Format, "unable to parse options"))
361 }
362
363 fn len(&self) -> usize {
364 self.bytes().len()
365 }
366
367 fn bytes(&self) -> (&[u8], Option<&[u8]>) {
368 (self.bytes(), None)
369 }
370}
371
372pub trait IcmpMessage<I: IcmpIpExt>:
374 Sized + Copy + FromBytes + IntoBytes + KnownLayout + Immutable + Unaligned
375{
376 const EXPECTS_BODY: bool = true;
378
379 type Code: Into<u8> + Copy + Debug;
386
387 type Body<B: SplitByteSlice>: MessageBody<B = B>;
389
390 const TYPE: I::IcmpMessageType;
395
396 fn code_from_u8(code: u8) -> Option<Self::Code>;
402}
403
404pub trait IcmpMessageType: TryFrom<u8> + Into<u8> + Copy + Debug {
409 fn is_err(self) -> bool;
416}
417
418#[derive(Copy, Clone, Debug, KnownLayout, FromBytes, IntoBytes, Immutable, Unaligned)]
419#[repr(C)]
420struct Header<M> {
421 prefix: HeaderPrefix,
422 message: M,
423}
424
425#[derive(Debug)]
437pub struct IcmpPacketRaw<I: IcmpIpExt, B: SplitByteSlice, M: IcmpMessage<I>> {
438 header: Ref<B, Header<M>>,
439 message_body: B,
440 _marker: PhantomData<I>,
441}
442
443impl<I: IcmpIpExt, B: SplitByteSlice, M: IcmpMessage<I>> IcmpPacketRaw<I, B, M> {
444 pub fn message(&self) -> &M {
446 &self.header.message
447 }
448}
449
450impl<I: IcmpIpExt, B: SplitByteSliceMut, M: IcmpMessage<I>> IcmpPacketRaw<I, B, M> {
451 pub fn message_mut(&mut self) -> &mut M {
453 &mut self.header.message
454 }
455
456 pub(crate) fn try_write_checksum(&mut self, src_ip: I::Addr, dst_ip: I::Addr) -> bool {
461 let original_checksum = self.header.prefix.checksum;
463 self.header.prefix.checksum = [0, 0];
464
465 if let Some(checksum) = IcmpPacket::<I, B, M>::compute_checksum(
466 &self.header,
467 &self.message_body,
468 src_ip,
469 dst_ip,
470 ) {
471 self.header.prefix.checksum = checksum;
472 true
473 } else {
474 self.header.prefix.checksum = original_checksum;
475 false
476 }
477 }
478}
479
480#[derive(Debug)]
485pub struct IcmpPacket<I: IcmpIpExt, B: SplitByteSlice, M: IcmpMessage<I>> {
486 header: Ref<B, Header<M>>,
487 message_body: M::Body<B>,
488 _marker: PhantomData<I>,
489}
490
491pub struct IcmpParseArgs<A: IpAddress> {
493 src_ip: A,
494 dst_ip: A,
495}
496
497impl<A: IpAddress> IcmpParseArgs<A> {
498 pub fn new<S: Into<A>, D: Into<A>>(src_ip: S, dst_ip: D) -> IcmpParseArgs<A> {
500 IcmpParseArgs { src_ip: src_ip.into(), dst_ip: dst_ip.into() }
501 }
502}
503
504impl<B: SplitByteSlice, I: IcmpIpExt, M: IcmpMessage<I>> ParsablePacket<B, ()>
505 for IcmpPacketRaw<I, B, M>
506{
507 type Error = ParseError;
508
509 fn parse_metadata(&self) -> ParseMetadata {
510 ParseMetadata::from_packet(Ref::bytes(&self.header).len(), self.message_body.len(), 0)
511 }
512
513 fn parse<BV: BufferView<B>>(mut buffer: BV, _args: ()) -> ParseResult<Self> {
514 let header = buffer.take_obj_front::<Header<M>>().ok_or(ParseError::Format)?;
515 let message_body = buffer.into_rest();
516 if header.prefix.msg_type != M::TYPE.into() {
517 return Err(ParseError::NotExpected);
518 }
519 Ok(IcmpPacketRaw { header, message_body, _marker: PhantomData })
520 }
521}
522
523impl<B: SplitByteSlice, I: IcmpIpExt, M: IcmpMessage<I>>
524 FromRaw<IcmpPacketRaw<I, B, M>, IcmpParseArgs<I::Addr>> for IcmpPacket<I, B, M>
525{
526 type Error = ParseError;
527
528 fn try_from_raw_with(
529 raw: IcmpPacketRaw<I, B, M>,
530 args: IcmpParseArgs<I::Addr>,
531 ) -> ParseResult<Self> {
532 let IcmpPacketRaw { header, message_body, _marker } = raw;
533 if !M::EXPECTS_BODY && !message_body.is_empty() {
534 return Err(ParseError::Format);
535 }
536 let _: M::Code = M::code_from_u8(header.prefix.code).ok_or(ParseError::Format)?;
537 let checksum = Self::compute_checksum(&header, &message_body, args.src_ip, args.dst_ip)
538 .ok_or(ParseError::Format)?;
539 if checksum != [0, 0] {
540 return Err(ParseError::Checksum);
541 }
542 let message_body = M::Body::parse(message_body)?;
543 Ok(IcmpPacket { header, message_body, _marker })
544 }
545}
546
547impl<B: SplitByteSlice, I: IcmpIpExt, M: IcmpMessage<I>> ParsablePacket<B, IcmpParseArgs<I::Addr>>
548 for IcmpPacket<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>>(buffer: BV, args: IcmpParseArgs<I::Addr>) -> ParseResult<Self> {
557 IcmpPacketRaw::parse(buffer, ()).and_then(|p| IcmpPacket::try_from_raw_with(p, args))
558 }
559}
560
561impl<I: IcmpIpExt, B: SplitByteSlice, M: IcmpMessage<I>> IcmpPacket<I, B, M> {
562 pub fn message(&self) -> &M {
564 &self.header.message
565 }
566
567 pub fn body(&self) -> &M::Body<B> {
569 &self.message_body
570 }
571
572 pub fn code(&self) -> M::Code {
577 M::code_from_u8(self.header.prefix.code).unwrap()
579 }
580
581 pub fn builder(&self, src_ip: I::Addr, dst_ip: I::Addr) -> IcmpPacketBuilder<I, M> {
583 IcmpPacketBuilder { src_ip, dst_ip, code: self.code(), msg: *self.message() }
584 }
585}
586
587fn compute_checksum_fragmented<I: IcmpIpExt, BB: packet::Fragment, M: IcmpMessage<I>>(
588 header: &Header<M>,
589 message_body: &FragmentedByteSlice<'_, BB>,
590 src_ip: I::Addr,
591 dst_ip: I::Addr,
592) -> Option<[u8; 2]> {
593 let mut c = Checksum::new();
594 if I::VERSION.is_v6() {
595 c.add_bytes(src_ip.bytes());
596 c.add_bytes(dst_ip.bytes());
597 let icmpv6_len = mem::size_of::<Header<M>>() + message_body.len();
598 let mut len_bytes = [0; 4];
599 NetworkEndian::write_u32(&mut len_bytes, icmpv6_len.try_into().ok()?);
600 c.add_bytes(&len_bytes[..]);
601 c.add_bytes(&[0, 0, 0]);
602 c.add_bytes(&[Ipv6Proto::Icmpv6.into()]);
603 }
604 c.add_bytes(&[header.prefix.msg_type, header.prefix.code]);
605 c.add_bytes(&header.prefix.checksum);
606 c.add_bytes(header.message.as_bytes());
607 for p in message_body.iter_fragments() {
608 c.add_bytes(p);
609 }
610 Some(c.checksum())
611}
612
613impl<I: IcmpIpExt, B: SplitByteSlice, M: IcmpMessage<I>> IcmpPacket<I, B, M> {
614 fn compute_checksum(
619 header: &Header<M>,
620 message_body: &[u8],
621 src_ip: I::Addr,
622 dst_ip: I::Addr,
623 ) -> Option<[u8; 2]> {
624 let mut body = [message_body];
625 compute_checksum_fragmented(header, &body.as_fragmented_byte_slice(), src_ip, dst_ip)
626 }
627}
628
629impl<I: IcmpIpExt, B: SplitByteSlice, M: IcmpMessage<I, Body<B> = OriginalPacket<B>>>
630 IcmpPacket<I, B, M>
631{
632 pub fn original_packet_body(&self) -> &[u8] {
640 self.message_body.body::<I>()
641 }
642
643 pub fn original_packet(&self) -> &OriginalPacket<B> {
651 &self.message_body
652 }
653}
654
655impl<B: SplitByteSlice, M: IcmpMessage<Ipv4, Body<B> = OriginalPacket<B>>> IcmpPacket<Ipv4, B, M> {
656 pub fn with_original_packet<O, F: FnOnce(Result<Ipv4PacketRaw<&[u8]>, &[u8]>) -> O>(
661 &self,
662 f: F,
663 ) -> O {
664 let mut bv = self.message_body.0.deref();
665 f(Ipv4PacketRaw::parse(&mut bv, ()).map_err(|_| self.message_body.0.deref()))
666 }
667}
668
669impl<B: SplitByteSlice, M: IcmpMessage<Ipv6, Body<B> = OriginalPacket<B>>> IcmpPacket<Ipv6, B, M> {
670 pub fn with_original_packet<O, F: FnOnce(Result<Ipv6PacketRaw<&[u8]>, &[u8]>) -> O>(
675 &self,
676 f: F,
677 ) -> O {
678 let mut bv = self.message_body.0.deref();
679 f(Ipv6PacketRaw::parse(&mut bv, ()).map_err(|_| self.message_body.0.deref()))
680 }
681}
682
683impl<I: IcmpIpExt, B: SplitByteSlice, M: IcmpMessage<I, Body<B> = ndp::Options<B>>>
684 IcmpPacket<I, B, M>
685{
686 pub fn ndp_options(&self) -> &ndp::Options<B> {
688 &self.message_body
689 }
690}
691
692#[derive(Debug, PartialEq, Clone)]
694pub struct IcmpPacketBuilder<I: IcmpIpExt, M: IcmpMessage<I>> {
695 src_ip: I::Addr,
696 dst_ip: I::Addr,
697 code: M::Code,
698 msg: M,
699}
700
701impl<I: IcmpIpExt, M: IcmpMessage<I>> IcmpPacketBuilder<I, M> {
702 pub fn new<S: Into<I::Addr>, D: Into<I::Addr>>(
704 src_ip: S,
705 dst_ip: D,
706 code: M::Code,
707 msg: M,
708 ) -> IcmpPacketBuilder<I, M> {
709 IcmpPacketBuilder { src_ip: src_ip.into(), dst_ip: dst_ip.into(), code, msg }
710 }
711
712 pub fn message(&self) -> &M {
714 &self.msg
715 }
716
717 pub fn message_mut(&mut self) -> &mut M {
719 &mut self.msg
720 }
721
722 pub fn set_src_ip(&mut self, addr: I::Addr) {
724 self.src_ip = addr;
725 }
726
727 pub fn set_dst_ip(&mut self, addr: I::Addr) {
729 self.dst_ip = addr;
730 }
731}
732
733impl<I: IcmpIpExt, M: IcmpMessage<I>> PacketBuilder for IcmpPacketBuilder<I, M> {
737 fn constraints(&self) -> PacketConstraints {
738 PacketConstraints::new(mem::size_of::<Header<M>>(), 0, 0, core::u32::MAX as usize)
751 }
752
753 fn serialize(
754 &self,
755 target: &mut SerializeTarget<'_>,
756 message_body: FragmentedBytesMut<'_, '_>,
757 ) {
758 use packet::BufferViewMut;
759
760 let mut prefix = &mut target.header;
762
763 assert!(
764 M::EXPECTS_BODY || message_body.is_empty(),
765 "body provided for message that doesn't take a body"
766 );
767 let mut header =
770 prefix.take_obj_front_zero::<Header<M>>().expect("too few bytes for ICMP message");
771 header.prefix.set_msg_type(M::TYPE);
772 header.prefix.code = self.code.into();
773 header.message = self.msg;
774 let checksum =
775 compute_checksum_fragmented(&header, &message_body, self.src_ip, self.dst_ip)
776 .unwrap_or_else(|| {
777 panic!(
778 "total ICMP packet length of {} overflows 32-bit length field of pseudo-header",
779 Ref::bytes(&header).len() + message_body.len(),
780 )
781 });
782 header.prefix.checksum = checksum;
783 }
784}
785
786#[derive(Copy, Clone, Debug, Eq, PartialEq)]
792pub struct IcmpZeroCode;
793
794impl From<IcmpZeroCode> for u8 {
795 fn from(_: IcmpZeroCode) -> u8 {
796 0
797 }
798}
799
800impl TryFrom<u8> for IcmpZeroCode {
801 type Error = NotZeroError<u8>;
802
803 fn try_from(value: u8) -> Result<Self, NotZeroError<u8>> {
804 if value == 0 {
805 Ok(Self)
806 } else {
807 Err(NotZeroError(value))
808 }
809 }
810}
811
812#[derive(Copy, Clone, Debug, Eq, PartialEq)]
817pub struct IcmpSenderZeroCode;
818
819impl From<IcmpSenderZeroCode> for u8 {
820 fn from(_: IcmpSenderZeroCode) -> u8 {
821 0
822 }
823}
824
825impl From<u8> for IcmpSenderZeroCode {
826 fn from(_: u8) -> Self {
827 Self
828 }
829}
830
831#[doc(hidden)]
836#[derive(
837 Copy, Clone, Debug, Eq, PartialEq, KnownLayout, FromBytes, IntoBytes, Immutable, Unaligned,
838)]
839#[repr(C)]
840pub struct IdAndSeq {
841 id: U16,
842 seq: U16,
843}
844
845impl IdAndSeq {
846 fn new(id: u16, seq: u16) -> IdAndSeq {
847 IdAndSeq { id: U16::new(id), seq: U16::new(seq) }
848 }
849}
850
851#[cfg(test)]
852mod tests {
853 use ip_test_macro::ip_test;
854 use packet::{InnerPacketBuilder, ParseBuffer, Serializer, SliceBufViewMut};
855 use test_case::test_case;
856
857 use super::*;
858
859 #[test]
860 fn test_partial_parse() {
861 let reference_header = Header {
864 prefix: HeaderPrefix {
865 msg_type: <IcmpEchoRequest as IcmpMessage<Ipv4>>::TYPE.into(),
866 code: 0,
867 checksum: [0, 0],
868 },
869 message: IcmpEchoRequest::new(1, 1),
870 };
871
872 let mut buf = &reference_header.as_bytes()[..7];
877 assert_eq!(
878 buf.parse::<IcmpPacketRaw<Ipv4, _, IcmpEchoRequest>>().unwrap_err(),
879 ParseError::Format
880 );
881
882 let mut header = reference_header;
884 header.prefix.msg_type = <IcmpEchoReply as IcmpMessage<Ipv4>>::TYPE.into();
885 let mut buf = header.as_bytes();
886 assert_eq!(
887 buf.parse::<IcmpPacketRaw<Ipv4, _, IcmpEchoRequest>>().unwrap_err(),
888 ParseError::NotExpected
889 );
890
891 let mut header = reference_header;
893 header.prefix.code = 0xFF;
894 let mut buf = header.as_bytes();
895 assert!(buf.parse::<IcmpPacketRaw<Ipv4, _, IcmpEchoRequest>>().is_ok());
896
897 let mut buf = reference_header.as_bytes();
901 assert!(buf.parse::<IcmpPacketRaw<Ipv4, _, IcmpEchoRequest>>().is_ok());
902 let mut header = reference_header;
903 header.prefix.checksum = [1, 1];
904 let mut buf = header.as_bytes();
905 assert!(buf.parse::<IcmpPacketRaw<Ipv4, _, IcmpEchoRequest>>().is_ok());
906 }
907
908 #[ip_test(I)]
909 #[test_case([0,0]; "zeroed_checksum")]
910 #[test_case([123, 234]; "garbage_checksum")]
911 fn test_try_write_checksum<I: IcmpIpExt>(corrupt_checksum: [u8; 2]) {
912 let icmp_message_with_checksum = []
915 .into_serializer()
916 .encapsulate(IcmpPacketBuilder::<I, _>::new(
917 *I::LOOPBACK_ADDRESS,
918 *I::LOOPBACK_ADDRESS,
919 IcmpZeroCode,
920 IcmpEchoRequest::new(1, 1),
921 ))
922 .serialize_vec_outer()
923 .unwrap()
924 .as_ref()
925 .to_vec();
926
927 let mut icmp_message_without_checksum = icmp_message_with_checksum.clone();
929 {
930 let buf = SliceBufViewMut::new(&mut icmp_message_without_checksum);
931 let mut message = IcmpPacketRaw::<I, _, IcmpEchoRequest>::parse_mut(buf, ())
932 .expect("parse packet raw should succeed");
933 message.header.prefix.checksum = corrupt_checksum;
934 }
935 assert_ne!(&icmp_message_with_checksum[..], &icmp_message_without_checksum[..]);
936
937 let buf = SliceBufViewMut::new(&mut icmp_message_without_checksum);
939 let mut message = IcmpPacketRaw::<I, _, IcmpEchoRequest>::parse_mut(buf, ())
940 .expect("parse packet raw should succeed");
941 assert!(message.try_write_checksum(*I::LOOPBACK_ADDRESS, *I::LOOPBACK_ADDRESS));
942 assert_eq!(&icmp_message_with_checksum[..], &icmp_message_without_checksum[..]);
943 }
944}