1use byteorder::{ByteOrder, NetworkEndian};
2use core::{cmp, fmt};
3
4use crate::phy::ChecksumCapabilities;
5use crate::wire::ip::checksum;
6use crate::wire::MldRepr;
7#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
8use crate::wire::NdiscRepr;
9use crate::wire::{IpAddress, IpProtocol, Ipv6Packet, Ipv6Repr};
10use crate::{Error, Result};
11
12enum_with_unknown! {
13 pub enum Message(u8) {
15 DstUnreachable = 0x01,
17 PktTooBig = 0x02,
19 TimeExceeded = 0x03,
21 ParamProblem = 0x04,
23 EchoRequest = 0x80,
25 EchoReply = 0x81,
27 MldQuery = 0x82,
29 RouterSolicit = 0x85,
31 RouterAdvert = 0x86,
33 NeighborSolicit = 0x87,
35 NeighborAdvert = 0x88,
37 Redirect = 0x89,
39 MldReport = 0x8f
41 }
42}
43
44impl Message {
45 pub fn is_error(&self) -> bool {
51 (u8::from(*self) & 0x80) != 0x80
52 }
53
54 pub fn is_ndisc(&self) -> bool {
59 match *self {
60 Message::RouterSolicit
61 | Message::RouterAdvert
62 | Message::NeighborSolicit
63 | Message::NeighborAdvert
64 | Message::Redirect => true,
65 _ => false,
66 }
67 }
68
69 pub fn is_mld(&self) -> bool {
74 match *self {
75 Message::MldQuery | Message::MldReport => true,
76 _ => false,
77 }
78 }
79}
80
81impl fmt::Display for Message {
82 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
83 match *self {
84 Message::DstUnreachable => write!(f, "destination unreachable"),
85 Message::PktTooBig => write!(f, "packet too big"),
86 Message::TimeExceeded => write!(f, "time exceeded"),
87 Message::ParamProblem => write!(f, "parameter problem"),
88 Message::EchoReply => write!(f, "echo reply"),
89 Message::EchoRequest => write!(f, "echo request"),
90 Message::RouterSolicit => write!(f, "router solicitation"),
91 Message::RouterAdvert => write!(f, "router advertisement"),
92 Message::NeighborSolicit => write!(f, "neighbor solicitation"),
93 Message::NeighborAdvert => write!(f, "neighbor advert"),
94 Message::Redirect => write!(f, "redirect"),
95 Message::MldQuery => write!(f, "multicast listener query"),
96 Message::MldReport => write!(f, "multicast listener report"),
97 Message::Unknown(id) => write!(f, "{}", id),
98 }
99 }
100}
101
102enum_with_unknown! {
103 pub enum DstUnreachable(u8) {
105 NoRoute = 0,
107 AdminProhibit = 1,
109 BeyondScope = 2,
111 AddrUnreachable = 3,
113 PortUnreachable = 4,
115 FailedPolicy = 5,
117 RejectRoute = 6
119 }
120}
121
122impl fmt::Display for DstUnreachable {
123 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
124 match *self {
125 DstUnreachable::NoRoute => write!(f, "no route to destination"),
126 DstUnreachable::AdminProhibit => write!(
127 f,
128 "communication with destination administratively prohibited"
129 ),
130 DstUnreachable::BeyondScope => write!(f, "beyond scope of source address"),
131 DstUnreachable::AddrUnreachable => write!(f, "address unreachable"),
132 DstUnreachable::PortUnreachable => write!(f, "port unreachable"),
133 DstUnreachable::FailedPolicy => {
134 write!(f, "source address failed ingress/egress policy")
135 }
136 DstUnreachable::RejectRoute => write!(f, "reject route to destination"),
137 DstUnreachable::Unknown(id) => write!(f, "{}", id),
138 }
139 }
140}
141
142enum_with_unknown! {
143 pub enum ParamProblem(u8) {
145 ErroneousHdrField = 0,
147 UnrecognizedNxtHdr = 1,
149 UnrecognizedOption = 2
151 }
152}
153
154impl fmt::Display for ParamProblem {
155 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
156 match *self {
157 ParamProblem::ErroneousHdrField => write!(f, "erroneous header field."),
158 ParamProblem::UnrecognizedNxtHdr => write!(f, "unrecognized next header type."),
159 ParamProblem::UnrecognizedOption => write!(f, "unrecognized IPv6 option."),
160 ParamProblem::Unknown(id) => write!(f, "{}", id),
161 }
162 }
163}
164
165enum_with_unknown! {
166 pub enum TimeExceeded(u8) {
168 HopLimitExceeded = 0,
170 FragReassemExceeded = 1
172 }
173}
174
175impl fmt::Display for TimeExceeded {
176 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
177 match *self {
178 TimeExceeded::HopLimitExceeded => write!(f, "hop limit exceeded in transit"),
179 TimeExceeded::FragReassemExceeded => write!(f, "fragment reassembly time exceeded"),
180 TimeExceeded::Unknown(id) => write!(f, "{}", id),
181 }
182 }
183}
184
185#[derive(Debug, PartialEq, Clone)]
187#[cfg_attr(feature = "defmt", derive(defmt::Format))]
188pub struct Packet<T: AsRef<[u8]>> {
189 pub(super) buffer: T,
190}
191
192pub(super) mod field {
194 use crate::wire::field::*;
195
196 pub const TYPE: usize = 0;
198 pub const CODE: usize = 1;
199 pub const CHECKSUM: Field = 2..4;
200
201 pub const UNUSED: Field = 4..8;
202 pub const MTU: Field = 4..8;
203 pub const POINTER: Field = 4..8;
204 pub const ECHO_IDENT: Field = 4..6;
205 pub const ECHO_SEQNO: Field = 6..8;
206
207 pub const HEADER_END: usize = 8;
208
209 pub const CUR_HOP_LIMIT: usize = 4;
212 pub const ROUTER_FLAGS: usize = 5;
213 pub const ROUTER_LT: Field = 6..8;
214 pub const REACHABLE_TM: Field = 8..12;
215 pub const RETRANS_TM: Field = 12..16;
216
217 pub const TARGET_ADDR: Field = 8..24;
219
220 pub const NEIGH_FLAGS: usize = 4;
222
223 pub const DEST_ADDR: Field = 24..40;
225
226 pub const MAX_RESP_CODE: Field = 4..6;
231 pub const QUERY_RESV: Field = 6..8;
232 pub const QUERY_MCAST_ADDR: Field = 8..24;
233 pub const SQRV: usize = 24;
234 pub const QQIC: usize = 25;
235 pub const QUERY_NUM_SRCS: Field = 26..28;
236
237 pub const RECORD_RESV: Field = 4..6;
239 pub const NR_MCAST_RCRDS: Field = 6..8;
240
241 pub const RECORD_TYPE: usize = 0;
243 pub const AUX_DATA_LEN: usize = 1;
244 pub const RECORD_NUM_SRCS: Field = 2..4;
245 pub const RECORD_MCAST_ADDR: Field = 4..20;
246}
247
248impl<T: AsRef<[u8]>> Packet<T> {
249 pub fn new_unchecked(buffer: T) -> Packet<T> {
251 Packet { buffer }
252 }
253
254 pub fn new_checked(buffer: T) -> Result<Packet<T>> {
259 let packet = Self::new_unchecked(buffer);
260 packet.check_len()?;
261 Ok(packet)
262 }
263
264 pub fn check_len(&self) -> Result<()> {
267 let len = self.buffer.as_ref().len();
268 if len < field::HEADER_END || len < self.header_len() {
269 Err(Error::Truncated)
270 } else {
271 Ok(())
272 }
273 }
274
275 pub fn into_inner(self) -> T {
277 self.buffer
278 }
279
280 #[inline]
282 pub fn msg_type(&self) -> Message {
283 let data = self.buffer.as_ref();
284 Message::from(data[field::TYPE])
285 }
286
287 #[inline]
289 pub fn msg_code(&self) -> u8 {
290 let data = self.buffer.as_ref();
291 data[field::CODE]
292 }
293
294 #[inline]
296 pub fn checksum(&self) -> u16 {
297 let data = self.buffer.as_ref();
298 NetworkEndian::read_u16(&data[field::CHECKSUM])
299 }
300
301 #[inline]
303 pub fn echo_ident(&self) -> u16 {
304 let data = self.buffer.as_ref();
305 NetworkEndian::read_u16(&data[field::ECHO_IDENT])
306 }
307
308 #[inline]
310 pub fn echo_seq_no(&self) -> u16 {
311 let data = self.buffer.as_ref();
312 NetworkEndian::read_u16(&data[field::ECHO_SEQNO])
313 }
314
315 #[inline]
317 pub fn pkt_too_big_mtu(&self) -> u32 {
318 let data = self.buffer.as_ref();
319 NetworkEndian::read_u32(&data[field::MTU])
320 }
321
322 #[inline]
324 pub fn param_problem_ptr(&self) -> u32 {
325 let data = self.buffer.as_ref();
326 NetworkEndian::read_u32(&data[field::POINTER])
327 }
328
329 pub fn header_len(&self) -> usize {
332 match self.msg_type() {
333 Message::DstUnreachable => field::UNUSED.end,
334 Message::PktTooBig => field::MTU.end,
335 Message::TimeExceeded => field::UNUSED.end,
336 Message::ParamProblem => field::POINTER.end,
337 Message::EchoRequest => field::ECHO_SEQNO.end,
338 Message::EchoReply => field::ECHO_SEQNO.end,
339 Message::RouterSolicit => field::UNUSED.end,
340 Message::RouterAdvert => field::RETRANS_TM.end,
341 Message::NeighborSolicit => field::TARGET_ADDR.end,
342 Message::NeighborAdvert => field::TARGET_ADDR.end,
343 Message::Redirect => field::DEST_ADDR.end,
344 Message::MldQuery => field::QUERY_NUM_SRCS.end,
345 Message::MldReport => field::NR_MCAST_RCRDS.end,
346 _ => field::CHECKSUM.end,
351 }
352 }
353
354 pub fn verify_checksum(&self, src_addr: &IpAddress, dst_addr: &IpAddress) -> bool {
359 if cfg!(fuzzing) {
360 return true;
361 }
362
363 let data = self.buffer.as_ref();
364 checksum::combine(&[
365 checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Icmpv6, data.len() as u32),
366 checksum::data(data),
367 ]) == !0
368 }
369}
370
371impl<'a, T: AsRef<[u8]> + ?Sized> Packet<&'a T> {
372 #[inline]
374 pub fn payload(&self) -> &'a [u8] {
375 let data = self.buffer.as_ref();
376 &data[self.header_len()..]
377 }
378}
379
380impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
381 #[inline]
383 pub fn set_msg_type(&mut self, value: Message) {
384 let data = self.buffer.as_mut();
385 data[field::TYPE] = value.into()
386 }
387
388 #[inline]
390 pub fn set_msg_code(&mut self, value: u8) {
391 let data = self.buffer.as_mut();
392 data[field::CODE] = value
393 }
394
395 #[inline]
403 pub fn clear_reserved(&mut self) {
404 match self.msg_type() {
405 Message::RouterSolicit
406 | Message::NeighborSolicit
407 | Message::NeighborAdvert
408 | Message::Redirect => {
409 let data = self.buffer.as_mut();
410 NetworkEndian::write_u32(&mut data[field::UNUSED], 0);
411 }
412 Message::MldQuery => {
413 let data = self.buffer.as_mut();
414 NetworkEndian::write_u16(&mut data[field::QUERY_RESV], 0);
415 data[field::SQRV] &= 0xf;
416 }
417 Message::MldReport => {
418 let data = self.buffer.as_mut();
419 NetworkEndian::write_u16(&mut data[field::RECORD_RESV], 0);
420 }
421 ty => panic!("Message type `{}` does not have any reserved fields.", ty),
422 }
423 }
424
425 #[inline]
426 pub fn set_checksum(&mut self, value: u16) {
427 let data = self.buffer.as_mut();
428 NetworkEndian::write_u16(&mut data[field::CHECKSUM], value)
429 }
430
431 #[inline]
436 pub fn set_echo_ident(&mut self, value: u16) {
437 let data = self.buffer.as_mut();
438 NetworkEndian::write_u16(&mut data[field::ECHO_IDENT], value)
439 }
440
441 #[inline]
446 pub fn set_echo_seq_no(&mut self, value: u16) {
447 let data = self.buffer.as_mut();
448 NetworkEndian::write_u16(&mut data[field::ECHO_SEQNO], value)
449 }
450
451 #[inline]
456 pub fn set_pkt_too_big_mtu(&mut self, value: u32) {
457 let data = self.buffer.as_mut();
458 NetworkEndian::write_u32(&mut data[field::MTU], value)
459 }
460
461 #[inline]
466 pub fn set_param_problem_ptr(&mut self, value: u32) {
467 let data = self.buffer.as_mut();
468 NetworkEndian::write_u32(&mut data[field::POINTER], value)
469 }
470
471 pub fn fill_checksum(&mut self, src_addr: &IpAddress, dst_addr: &IpAddress) {
473 self.set_checksum(0);
474 let checksum = {
475 let data = self.buffer.as_ref();
476 !checksum::combine(&[
477 checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Icmpv6, data.len() as u32),
478 checksum::data(data),
479 ])
480 };
481 self.set_checksum(checksum)
482 }
483
484 #[inline]
486 pub fn payload_mut(&mut self) -> &mut [u8] {
487 let range = self.header_len()..;
488 let data = self.buffer.as_mut();
489 &mut data[range]
490 }
491}
492
493impl<T: AsRef<[u8]>> AsRef<[u8]> for Packet<T> {
494 fn as_ref(&self) -> &[u8] {
495 self.buffer.as_ref()
496 }
497}
498
499#[derive(Debug, PartialEq, Eq, Clone, Copy)]
501#[cfg_attr(feature = "defmt", derive(defmt::Format))]
502#[non_exhaustive]
503pub enum Repr<'a> {
504 DstUnreachable {
505 reason: DstUnreachable,
506 header: Ipv6Repr,
507 data: &'a [u8],
508 },
509 PktTooBig {
510 mtu: u32,
511 header: Ipv6Repr,
512 data: &'a [u8],
513 },
514 TimeExceeded {
515 reason: TimeExceeded,
516 header: Ipv6Repr,
517 data: &'a [u8],
518 },
519 ParamProblem {
520 reason: ParamProblem,
521 pointer: u32,
522 header: Ipv6Repr,
523 data: &'a [u8],
524 },
525 EchoRequest {
526 ident: u16,
527 seq_no: u16,
528 data: &'a [u8],
529 },
530 EchoReply {
531 ident: u16,
532 seq_no: u16,
533 data: &'a [u8],
534 },
535 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
536 Ndisc(NdiscRepr<'a>),
537 Mld(MldRepr<'a>),
538}
539
540impl<'a> Repr<'a> {
541 pub fn parse<T>(
544 src_addr: &IpAddress,
545 dst_addr: &IpAddress,
546 packet: &Packet<&'a T>,
547 checksum_caps: &ChecksumCapabilities,
548 ) -> Result<Repr<'a>>
549 where
550 T: AsRef<[u8]> + ?Sized,
551 {
552 fn create_packet_from_payload<'a, T>(packet: &Packet<&'a T>) -> Result<(&'a [u8], Ipv6Repr)>
553 where
554 T: AsRef<[u8]> + ?Sized,
555 {
556 let ip_packet = Ipv6Packet::new_checked(packet.payload())?;
557
558 let payload = &packet.payload()[ip_packet.header_len() as usize..];
559 if payload.len() < 8 {
560 return Err(Error::Truncated);
561 }
562 let repr = Ipv6Repr {
563 src_addr: ip_packet.src_addr(),
564 dst_addr: ip_packet.dst_addr(),
565 next_header: ip_packet.next_header(),
566 payload_len: payload.len(),
567 hop_limit: ip_packet.hop_limit(),
568 };
569 Ok((payload, repr))
570 }
571 if checksum_caps.icmpv6.rx() && !packet.verify_checksum(src_addr, dst_addr) {
573 return Err(Error::Checksum);
574 }
575
576 match (packet.msg_type(), packet.msg_code()) {
577 (Message::DstUnreachable, code) => {
578 let (payload, repr) = create_packet_from_payload(packet)?;
579 Ok(Repr::DstUnreachable {
580 reason: DstUnreachable::from(code),
581 header: repr,
582 data: payload,
583 })
584 }
585 (Message::PktTooBig, 0) => {
586 let (payload, repr) = create_packet_from_payload(packet)?;
587 Ok(Repr::PktTooBig {
588 mtu: packet.pkt_too_big_mtu(),
589 header: repr,
590 data: payload,
591 })
592 }
593 (Message::TimeExceeded, code) => {
594 let (payload, repr) = create_packet_from_payload(packet)?;
595 Ok(Repr::TimeExceeded {
596 reason: TimeExceeded::from(code),
597 header: repr,
598 data: payload,
599 })
600 }
601 (Message::ParamProblem, code) => {
602 let (payload, repr) = create_packet_from_payload(packet)?;
603 Ok(Repr::ParamProblem {
604 reason: ParamProblem::from(code),
605 pointer: packet.param_problem_ptr(),
606 header: repr,
607 data: payload,
608 })
609 }
610 (Message::EchoRequest, 0) => Ok(Repr::EchoRequest {
611 ident: packet.echo_ident(),
612 seq_no: packet.echo_seq_no(),
613 data: packet.payload(),
614 }),
615 (Message::EchoReply, 0) => Ok(Repr::EchoReply {
616 ident: packet.echo_ident(),
617 seq_no: packet.echo_seq_no(),
618 data: packet.payload(),
619 }),
620 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
621 (msg_type, 0) if msg_type.is_ndisc() => NdiscRepr::parse(packet).map(Repr::Ndisc),
622 (msg_type, 0) if msg_type.is_mld() => MldRepr::parse(packet).map(Repr::Mld),
623 _ => Err(Error::Unrecognized),
624 }
625 }
626
627 pub fn buffer_len(&self) -> usize {
629 match self {
630 &Repr::DstUnreachable { header, data, .. }
631 | &Repr::PktTooBig { header, data, .. }
632 | &Repr::TimeExceeded { header, data, .. }
633 | &Repr::ParamProblem { header, data, .. } => {
634 field::UNUSED.end + header.buffer_len() + data.len()
635 }
636 &Repr::EchoRequest { data, .. } | &Repr::EchoReply { data, .. } => {
637 field::ECHO_SEQNO.end + data.len()
638 }
639 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
640 &Repr::Ndisc(ndisc) => ndisc.buffer_len(),
641 &Repr::Mld(mld) => mld.buffer_len(),
642 }
643 }
644
645 pub fn emit<T>(
648 &self,
649 src_addr: &IpAddress,
650 dst_addr: &IpAddress,
651 packet: &mut Packet<&mut T>,
652 checksum_caps: &ChecksumCapabilities,
653 ) where
654 T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
655 {
656 fn emit_contained_packet(buffer: &mut [u8], header: Ipv6Repr, data: &[u8]) {
657 let mut ip_packet = Ipv6Packet::new_unchecked(buffer);
658 header.emit(&mut ip_packet);
659 let payload = &mut ip_packet.into_inner()[header.buffer_len()..];
660 payload.copy_from_slice(data);
661 }
662
663 match *self {
664 Repr::DstUnreachable {
665 reason,
666 header,
667 data,
668 } => {
669 packet.set_msg_type(Message::DstUnreachable);
670 packet.set_msg_code(reason.into());
671
672 emit_contained_packet(packet.payload_mut(), header, data);
673 }
674
675 Repr::PktTooBig { mtu, header, data } => {
676 packet.set_msg_type(Message::PktTooBig);
677 packet.set_msg_code(0);
678 packet.set_pkt_too_big_mtu(mtu);
679
680 emit_contained_packet(packet.payload_mut(), header, data);
681 }
682
683 Repr::TimeExceeded {
684 reason,
685 header,
686 data,
687 } => {
688 packet.set_msg_type(Message::TimeExceeded);
689 packet.set_msg_code(reason.into());
690
691 emit_contained_packet(packet.payload_mut(), header, data);
692 }
693
694 Repr::ParamProblem {
695 reason,
696 pointer,
697 header,
698 data,
699 } => {
700 packet.set_msg_type(Message::ParamProblem);
701 packet.set_msg_code(reason.into());
702 packet.set_param_problem_ptr(pointer);
703
704 emit_contained_packet(packet.payload_mut(), header, data);
705 }
706
707 Repr::EchoRequest {
708 ident,
709 seq_no,
710 data,
711 } => {
712 packet.set_msg_type(Message::EchoRequest);
713 packet.set_msg_code(0);
714 packet.set_echo_ident(ident);
715 packet.set_echo_seq_no(seq_no);
716 let data_len = cmp::min(packet.payload_mut().len(), data.len());
717 packet.payload_mut()[..data_len].copy_from_slice(&data[..data_len])
718 }
719
720 Repr::EchoReply {
721 ident,
722 seq_no,
723 data,
724 } => {
725 packet.set_msg_type(Message::EchoReply);
726 packet.set_msg_code(0);
727 packet.set_echo_ident(ident);
728 packet.set_echo_seq_no(seq_no);
729 let data_len = cmp::min(packet.payload_mut().len(), data.len());
730 packet.payload_mut()[..data_len].copy_from_slice(&data[..data_len])
731 }
732
733 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
734 Repr::Ndisc(ndisc) => ndisc.emit(packet),
735
736 Repr::Mld(mld) => mld.emit(packet),
737 }
738
739 if checksum_caps.icmpv6.tx() {
740 packet.fill_checksum(src_addr, dst_addr);
741 } else {
742 packet.set_checksum(0);
744 }
745 }
746}
747
748#[cfg(test)]
749mod test {
750 use super::*;
751 use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2};
752 use crate::wire::{IpProtocol, Ipv6Address, Ipv6Repr};
753
754 static ECHO_PACKET_BYTES: [u8; 12] = [
755 0x80, 0x00, 0x19, 0xb3, 0x12, 0x34, 0xab, 0xcd, 0xaa, 0x00, 0x00, 0xff,
756 ];
757
758 static ECHO_PACKET_PAYLOAD: [u8; 4] = [0xaa, 0x00, 0x00, 0xff];
759
760 static PKT_TOO_BIG_BYTES: [u8; 60] = [
761 0x02, 0x00, 0x0f, 0xc9, 0x00, 0x00, 0x05, 0xdc, 0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x11,
762 0x40, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
763 0x00, 0x01, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
764 0x00, 0x00, 0x02, 0xbf, 0x00, 0x00, 0x35, 0x00, 0x0c, 0x12, 0x4d, 0xaa, 0x00, 0x00, 0xff,
765 ];
766
767 static PKT_TOO_BIG_IP_PAYLOAD: [u8; 52] = [
768 0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x11, 0x40, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
769 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00,
770 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xbf, 0x00, 0x00, 0x35, 0x00,
771 0x0c, 0x12, 0x4d, 0xaa, 0x00, 0x00, 0xff,
772 ];
773
774 static PKT_TOO_BIG_UDP_PAYLOAD: [u8; 12] = [
775 0xbf, 0x00, 0x00, 0x35, 0x00, 0x0c, 0x12, 0x4d, 0xaa, 0x00, 0x00, 0xff,
776 ];
777
778 fn echo_packet_repr() -> Repr<'static> {
779 Repr::EchoRequest {
780 ident: 0x1234,
781 seq_no: 0xabcd,
782 data: &ECHO_PACKET_PAYLOAD,
783 }
784 }
785
786 fn too_big_packet_repr() -> Repr<'static> {
787 Repr::PktTooBig {
788 mtu: 1500,
789 header: Ipv6Repr {
790 src_addr: Ipv6Address([
791 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
792 0x00, 0x00, 0x01,
793 ]),
794 dst_addr: Ipv6Address([
795 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
796 0x00, 0x00, 0x02,
797 ]),
798 next_header: IpProtocol::Udp,
799 payload_len: 12,
800 hop_limit: 0x40,
801 },
802 data: &PKT_TOO_BIG_UDP_PAYLOAD,
803 }
804 }
805
806 #[test]
807 fn test_echo_deconstruct() {
808 let packet = Packet::new_unchecked(&ECHO_PACKET_BYTES[..]);
809 assert_eq!(packet.msg_type(), Message::EchoRequest);
810 assert_eq!(packet.msg_code(), 0);
811 assert_eq!(packet.checksum(), 0x19b3);
812 assert_eq!(packet.echo_ident(), 0x1234);
813 assert_eq!(packet.echo_seq_no(), 0xabcd);
814 assert_eq!(packet.payload(), &ECHO_PACKET_PAYLOAD[..]);
815 assert!(packet.verify_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2));
816 assert!(!packet.msg_type().is_error());
817 }
818
819 #[test]
820 fn test_echo_construct() {
821 let mut bytes = vec![0xa5; 12];
822 let mut packet = Packet::new_unchecked(&mut bytes);
823 packet.set_msg_type(Message::EchoRequest);
824 packet.set_msg_code(0);
825 packet.set_echo_ident(0x1234);
826 packet.set_echo_seq_no(0xabcd);
827 packet
828 .payload_mut()
829 .copy_from_slice(&ECHO_PACKET_PAYLOAD[..]);
830 packet.fill_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2);
831 assert_eq!(&packet.into_inner()[..], &ECHO_PACKET_BYTES[..]);
832 }
833
834 #[test]
835 fn test_echo_repr_parse() {
836 let packet = Packet::new_unchecked(&ECHO_PACKET_BYTES[..]);
837 let repr = Repr::parse(
838 &MOCK_IP_ADDR_1,
839 &MOCK_IP_ADDR_2,
840 &packet,
841 &ChecksumCapabilities::default(),
842 )
843 .unwrap();
844 assert_eq!(repr, echo_packet_repr());
845 }
846
847 #[test]
848 fn test_echo_emit() {
849 let repr = echo_packet_repr();
850 let mut bytes = vec![0xa5; repr.buffer_len()];
851 let mut packet = Packet::new_unchecked(&mut bytes);
852 repr.emit(
853 &MOCK_IP_ADDR_1,
854 &MOCK_IP_ADDR_2,
855 &mut packet,
856 &ChecksumCapabilities::default(),
857 );
858 assert_eq!(&packet.into_inner()[..], &ECHO_PACKET_BYTES[..]);
859 }
860
861 #[test]
862 fn test_too_big_deconstruct() {
863 let packet = Packet::new_unchecked(&PKT_TOO_BIG_BYTES[..]);
864 assert_eq!(packet.msg_type(), Message::PktTooBig);
865 assert_eq!(packet.msg_code(), 0);
866 assert_eq!(packet.checksum(), 0x0fc9);
867 assert_eq!(packet.pkt_too_big_mtu(), 1500);
868 assert_eq!(packet.payload(), &PKT_TOO_BIG_IP_PAYLOAD[..]);
869 assert!(packet.verify_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2));
870 assert!(packet.msg_type().is_error());
871 }
872
873 #[test]
874 fn test_too_big_construct() {
875 let mut bytes = vec![0xa5; 60];
876 let mut packet = Packet::new_unchecked(&mut bytes);
877 packet.set_msg_type(Message::PktTooBig);
878 packet.set_msg_code(0);
879 packet.set_pkt_too_big_mtu(1500);
880 packet
881 .payload_mut()
882 .copy_from_slice(&PKT_TOO_BIG_IP_PAYLOAD[..]);
883 packet.fill_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2);
884 assert_eq!(&packet.into_inner()[..], &PKT_TOO_BIG_BYTES[..]);
885 }
886
887 #[test]
888 fn test_too_big_repr_parse() {
889 let packet = Packet::new_unchecked(&PKT_TOO_BIG_BYTES[..]);
890 let repr = Repr::parse(
891 &MOCK_IP_ADDR_1,
892 &MOCK_IP_ADDR_2,
893 &packet,
894 &ChecksumCapabilities::default(),
895 )
896 .unwrap();
897 assert_eq!(repr, too_big_packet_repr());
898 }
899
900 #[test]
901 fn test_too_big_emit() {
902 let repr = too_big_packet_repr();
903 let mut bytes = vec![0xa5; repr.buffer_len()];
904 let mut packet = Packet::new_unchecked(&mut bytes);
905 repr.emit(
906 &MOCK_IP_ADDR_1,
907 &MOCK_IP_ADDR_2,
908 &mut packet,
909 &ChecksumCapabilities::default(),
910 );
911 assert_eq!(&packet.into_inner()[..], &PKT_TOO_BIG_BYTES[..]);
912 }
913}