1use byteorder::{ByteOrder, NetworkEndian};
2use core::{cmp, fmt};
3
4use super::{Error, Result};
5use crate::phy::ChecksumCapabilities;
6use crate::wire::ip::checksum;
7use crate::wire::MldRepr;
8#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
9use crate::wire::NdiscRepr;
10#[cfg(feature = "proto-rpl")]
11use crate::wire::RplRepr;
12use crate::wire::{IpProtocol, Ipv6Address, Ipv6Packet, Ipv6Repr};
13use crate::wire::{IPV6_HEADER_LEN, IPV6_MIN_MTU};
14
15const MAX_ERROR_PACKET_LEN: usize = IPV6_MIN_MTU - IPV6_HEADER_LEN;
17
18enum_with_unknown! {
19 pub enum Message(u8) {
21 DstUnreachable = 0x01,
23 PktTooBig = 0x02,
25 TimeExceeded = 0x03,
27 ParamProblem = 0x04,
29 EchoRequest = 0x80,
31 EchoReply = 0x81,
33 MldQuery = 0x82,
35 RouterSolicit = 0x85,
37 RouterAdvert = 0x86,
39 NeighborSolicit = 0x87,
41 NeighborAdvert = 0x88,
43 Redirect = 0x89,
45 MldReport = 0x8f,
47 RplControl = 0x9b,
49 }
50}
51
52impl Message {
53 pub fn is_error(&self) -> bool {
59 (u8::from(*self) & 0x80) != 0x80
60 }
61
62 pub const fn is_ndisc(&self) -> bool {
67 match *self {
68 Message::RouterSolicit
69 | Message::RouterAdvert
70 | Message::NeighborSolicit
71 | Message::NeighborAdvert
72 | Message::Redirect => true,
73 _ => false,
74 }
75 }
76
77 pub const fn is_mld(&self) -> bool {
82 match *self {
83 Message::MldQuery | Message::MldReport => true,
84 _ => false,
85 }
86 }
87}
88
89impl fmt::Display for Message {
90 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
91 match *self {
92 Message::DstUnreachable => write!(f, "destination unreachable"),
93 Message::PktTooBig => write!(f, "packet too big"),
94 Message::TimeExceeded => write!(f, "time exceeded"),
95 Message::ParamProblem => write!(f, "parameter problem"),
96 Message::EchoReply => write!(f, "echo reply"),
97 Message::EchoRequest => write!(f, "echo request"),
98 Message::RouterSolicit => write!(f, "router solicitation"),
99 Message::RouterAdvert => write!(f, "router advertisement"),
100 Message::NeighborSolicit => write!(f, "neighbor solicitation"),
101 Message::NeighborAdvert => write!(f, "neighbor advert"),
102 Message::Redirect => write!(f, "redirect"),
103 Message::MldQuery => write!(f, "multicast listener query"),
104 Message::MldReport => write!(f, "multicast listener report"),
105 Message::RplControl => write!(f, "RPL control message"),
106 Message::Unknown(id) => write!(f, "{id}"),
107 }
108 }
109}
110
111enum_with_unknown! {
112 pub enum DstUnreachable(u8) {
114 NoRoute = 0,
116 AdminProhibit = 1,
118 BeyondScope = 2,
120 AddrUnreachable = 3,
122 PortUnreachable = 4,
124 FailedPolicy = 5,
126 RejectRoute = 6
128 }
129}
130
131impl fmt::Display for DstUnreachable {
132 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
133 match *self {
134 DstUnreachable::NoRoute => write!(f, "no route to destination"),
135 DstUnreachable::AdminProhibit => write!(
136 f,
137 "communication with destination administratively prohibited"
138 ),
139 DstUnreachable::BeyondScope => write!(f, "beyond scope of source address"),
140 DstUnreachable::AddrUnreachable => write!(f, "address unreachable"),
141 DstUnreachable::PortUnreachable => write!(f, "port unreachable"),
142 DstUnreachable::FailedPolicy => {
143 write!(f, "source address failed ingress/egress policy")
144 }
145 DstUnreachable::RejectRoute => write!(f, "reject route to destination"),
146 DstUnreachable::Unknown(id) => write!(f, "{id}"),
147 }
148 }
149}
150
151enum_with_unknown! {
152 pub enum ParamProblem(u8) {
154 ErroneousHdrField = 0,
156 UnrecognizedNxtHdr = 1,
158 UnrecognizedOption = 2
160 }
161}
162
163impl fmt::Display for ParamProblem {
164 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
165 match *self {
166 ParamProblem::ErroneousHdrField => write!(f, "erroneous header field."),
167 ParamProblem::UnrecognizedNxtHdr => write!(f, "unrecognized next header type."),
168 ParamProblem::UnrecognizedOption => write!(f, "unrecognized IPv6 option."),
169 ParamProblem::Unknown(id) => write!(f, "{id}"),
170 }
171 }
172}
173
174enum_with_unknown! {
175 pub enum TimeExceeded(u8) {
177 HopLimitExceeded = 0,
179 FragReassemExceeded = 1
181 }
182}
183
184impl fmt::Display for TimeExceeded {
185 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
186 match *self {
187 TimeExceeded::HopLimitExceeded => write!(f, "hop limit exceeded in transit"),
188 TimeExceeded::FragReassemExceeded => write!(f, "fragment reassembly time exceeded"),
189 TimeExceeded::Unknown(id) => write!(f, "{id}"),
190 }
191 }
192}
193
194#[derive(Debug, PartialEq, Eq, Clone)]
196#[cfg_attr(feature = "defmt", derive(defmt::Format))]
197pub struct Packet<T: AsRef<[u8]>> {
198 pub(super) buffer: T,
199}
200
201pub(super) mod field {
203 use crate::wire::field::*;
204
205 pub const TYPE: usize = 0;
207 pub const CODE: usize = 1;
208 pub const CHECKSUM: Field = 2..4;
209
210 pub const UNUSED: Field = 4..8;
211 pub const MTU: Field = 4..8;
212 pub const POINTER: Field = 4..8;
213 pub const ECHO_IDENT: Field = 4..6;
214 pub const ECHO_SEQNO: Field = 6..8;
215
216 pub const HEADER_END: usize = 8;
217
218 pub const CUR_HOP_LIMIT: usize = 4;
221 pub const ROUTER_FLAGS: usize = 5;
222 pub const ROUTER_LT: Field = 6..8;
223 pub const REACHABLE_TM: Field = 8..12;
224 pub const RETRANS_TM: Field = 12..16;
225
226 pub const TARGET_ADDR: Field = 8..24;
228
229 pub const NEIGH_FLAGS: usize = 4;
231
232 pub const DEST_ADDR: Field = 24..40;
234
235 pub const MAX_RESP_CODE: Field = 4..6;
240 pub const QUERY_RESV: Field = 6..8;
241 pub const QUERY_MCAST_ADDR: Field = 8..24;
242 pub const SQRV: usize = 24;
243 pub const QQIC: usize = 25;
244 pub const QUERY_NUM_SRCS: Field = 26..28;
245
246 pub const RECORD_RESV: Field = 4..6;
248 pub const NR_MCAST_RCRDS: Field = 6..8;
249
250 pub const RECORD_TYPE: usize = 0;
252 pub const AUX_DATA_LEN: usize = 1;
253 pub const RECORD_NUM_SRCS: Field = 2..4;
254 pub const RECORD_MCAST_ADDR: Field = 4..20;
255}
256
257impl<T: AsRef<[u8]>> Packet<T> {
258 pub const fn new_unchecked(buffer: T) -> Packet<T> {
260 Packet { buffer }
261 }
262
263 pub fn new_checked(buffer: T) -> Result<Packet<T>> {
268 let packet = Self::new_unchecked(buffer);
269 packet.check_len()?;
270 Ok(packet)
271 }
272
273 pub fn check_len(&self) -> Result<()> {
276 let len = self.buffer.as_ref().len();
277
278 if len < 4 {
279 return Err(Error);
280 }
281
282 match self.msg_type() {
283 Message::DstUnreachable
284 | Message::PktTooBig
285 | Message::TimeExceeded
286 | Message::ParamProblem
287 | Message::EchoRequest
288 | Message::EchoReply
289 | Message::MldQuery
290 | Message::RouterSolicit
291 | Message::RouterAdvert
292 | Message::NeighborSolicit
293 | Message::NeighborAdvert
294 | Message::Redirect
295 | Message::MldReport => {
296 if len < field::HEADER_END || len < self.header_len() {
297 return Err(Error);
298 }
299 }
300 #[cfg(feature = "proto-rpl")]
301 Message::RplControl => match super::rpl::RplControlMessage::from(self.msg_code()) {
302 super::rpl::RplControlMessage::DodagInformationSolicitation => {
303 if len < 6 {
305 return Err(Error);
306 }
307 }
308 super::rpl::RplControlMessage::DodagInformationObject => {
309 if len < 28 {
311 return Err(Error);
312 }
313 }
314 super::rpl::RplControlMessage::DestinationAdvertisementObject => {
315 if len < 8 || (self.dao_dodag_id_present() && len < 24) {
317 return Err(Error);
318 }
319 }
320 super::rpl::RplControlMessage::DestinationAdvertisementObjectAck => {
321 if len < 8 || (self.dao_dodag_id_present() && len < 24) {
323 return Err(Error);
324 }
325 }
326 super::rpl::RplControlMessage::SecureDodagInformationSolicitation
327 | super::rpl::RplControlMessage::SecureDodagInformationObject
328 | super::rpl::RplControlMessage::SecureDestinationAdvertisementObject
329 | super::rpl::RplControlMessage::SecureDestinationAdvertisementObjectAck
330 | super::rpl::RplControlMessage::ConsistencyCheck => return Err(Error),
331 super::rpl::RplControlMessage::Unknown(_) => return Err(Error),
332 },
333 #[cfg(not(feature = "proto-rpl"))]
334 Message::RplControl => return Err(Error),
335 Message::Unknown(_) => return Err(Error),
336 }
337
338 Ok(())
339 }
340
341 pub fn into_inner(self) -> T {
343 self.buffer
344 }
345
346 #[inline]
348 pub fn msg_type(&self) -> Message {
349 let data = self.buffer.as_ref();
350 Message::from(data[field::TYPE])
351 }
352
353 #[inline]
355 pub fn msg_code(&self) -> u8 {
356 let data = self.buffer.as_ref();
357 data[field::CODE]
358 }
359
360 #[inline]
362 pub fn checksum(&self) -> u16 {
363 let data = self.buffer.as_ref();
364 NetworkEndian::read_u16(&data[field::CHECKSUM])
365 }
366
367 #[inline]
369 pub fn echo_ident(&self) -> u16 {
370 let data = self.buffer.as_ref();
371 NetworkEndian::read_u16(&data[field::ECHO_IDENT])
372 }
373
374 #[inline]
376 pub fn echo_seq_no(&self) -> u16 {
377 let data = self.buffer.as_ref();
378 NetworkEndian::read_u16(&data[field::ECHO_SEQNO])
379 }
380
381 #[inline]
383 pub fn pkt_too_big_mtu(&self) -> u32 {
384 let data = self.buffer.as_ref();
385 NetworkEndian::read_u32(&data[field::MTU])
386 }
387
388 #[inline]
390 pub fn param_problem_ptr(&self) -> u32 {
391 let data = self.buffer.as_ref();
392 NetworkEndian::read_u32(&data[field::POINTER])
393 }
394
395 pub fn header_len(&self) -> usize {
398 match self.msg_type() {
399 Message::DstUnreachable => field::UNUSED.end,
400 Message::PktTooBig => field::MTU.end,
401 Message::TimeExceeded => field::UNUSED.end,
402 Message::ParamProblem => field::POINTER.end,
403 Message::EchoRequest => field::ECHO_SEQNO.end,
404 Message::EchoReply => field::ECHO_SEQNO.end,
405 Message::RouterSolicit => field::UNUSED.end,
406 Message::RouterAdvert => field::RETRANS_TM.end,
407 Message::NeighborSolicit => field::TARGET_ADDR.end,
408 Message::NeighborAdvert => field::TARGET_ADDR.end,
409 Message::Redirect => field::DEST_ADDR.end,
410 Message::MldQuery => field::QUERY_NUM_SRCS.end,
411 Message::MldReport => field::NR_MCAST_RCRDS.end,
412 _ => field::CHECKSUM.end,
417 }
418 }
419
420 pub fn verify_checksum(&self, src_addr: &Ipv6Address, dst_addr: &Ipv6Address) -> bool {
425 if cfg!(fuzzing) {
426 return true;
427 }
428
429 let data = self.buffer.as_ref();
430 checksum::combine(&[
431 checksum::pseudo_header_v6(src_addr, dst_addr, IpProtocol::Icmpv6, data.len() as u32),
432 checksum::data(data),
433 ]) == !0
434 }
435}
436
437impl<'a, T: AsRef<[u8]> + ?Sized> Packet<&'a T> {
438 #[inline]
440 pub fn payload(&self) -> &'a [u8] {
441 let data = self.buffer.as_ref();
442 &data[self.header_len()..]
443 }
444}
445
446impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
447 #[inline]
449 pub fn set_msg_type(&mut self, value: Message) {
450 let data = self.buffer.as_mut();
451 data[field::TYPE] = value.into()
452 }
453
454 #[inline]
456 pub fn set_msg_code(&mut self, value: u8) {
457 let data = self.buffer.as_mut();
458 data[field::CODE] = value
459 }
460
461 #[inline]
469 pub fn clear_reserved(&mut self) {
470 match self.msg_type() {
471 Message::RouterSolicit
472 | Message::NeighborSolicit
473 | Message::NeighborAdvert
474 | Message::Redirect => {
475 let data = self.buffer.as_mut();
476 NetworkEndian::write_u32(&mut data[field::UNUSED], 0);
477 }
478 Message::MldQuery => {
479 let data = self.buffer.as_mut();
480 NetworkEndian::write_u16(&mut data[field::QUERY_RESV], 0);
481 data[field::SQRV] &= 0xf;
482 }
483 Message::MldReport => {
484 let data = self.buffer.as_mut();
485 NetworkEndian::write_u16(&mut data[field::RECORD_RESV], 0);
486 }
487 ty => panic!("Message type `{ty}` does not have any reserved fields."),
488 }
489 }
490
491 #[inline]
492 pub fn set_checksum(&mut self, value: u16) {
493 let data = self.buffer.as_mut();
494 NetworkEndian::write_u16(&mut data[field::CHECKSUM], value)
495 }
496
497 #[inline]
502 pub fn set_echo_ident(&mut self, value: u16) {
503 let data = self.buffer.as_mut();
504 NetworkEndian::write_u16(&mut data[field::ECHO_IDENT], value)
505 }
506
507 #[inline]
512 pub fn set_echo_seq_no(&mut self, value: u16) {
513 let data = self.buffer.as_mut();
514 NetworkEndian::write_u16(&mut data[field::ECHO_SEQNO], value)
515 }
516
517 #[inline]
522 pub fn set_pkt_too_big_mtu(&mut self, value: u32) {
523 let data = self.buffer.as_mut();
524 NetworkEndian::write_u32(&mut data[field::MTU], value)
525 }
526
527 #[inline]
532 pub fn set_param_problem_ptr(&mut self, value: u32) {
533 let data = self.buffer.as_mut();
534 NetworkEndian::write_u32(&mut data[field::POINTER], value)
535 }
536
537 pub fn fill_checksum(&mut self, src_addr: &Ipv6Address, dst_addr: &Ipv6Address) {
539 self.set_checksum(0);
540 let checksum = {
541 let data = self.buffer.as_ref();
542 !checksum::combine(&[
543 checksum::pseudo_header_v6(
544 src_addr,
545 dst_addr,
546 IpProtocol::Icmpv6,
547 data.len() as u32,
548 ),
549 checksum::data(data),
550 ])
551 };
552 self.set_checksum(checksum)
553 }
554
555 #[inline]
557 pub fn payload_mut(&mut self) -> &mut [u8] {
558 let range = self.header_len()..;
559 let data = self.buffer.as_mut();
560 &mut data[range]
561 }
562}
563
564impl<T: AsRef<[u8]>> AsRef<[u8]> for Packet<T> {
565 fn as_ref(&self) -> &[u8] {
566 self.buffer.as_ref()
567 }
568}
569
570#[derive(Debug, PartialEq, Eq, Clone, Copy)]
572#[cfg_attr(feature = "defmt", derive(defmt::Format))]
573#[non_exhaustive]
574pub enum Repr<'a> {
575 DstUnreachable {
576 reason: DstUnreachable,
577 header: Ipv6Repr,
578 data: &'a [u8],
579 },
580 PktTooBig {
581 mtu: u32,
582 header: Ipv6Repr,
583 data: &'a [u8],
584 },
585 TimeExceeded {
586 reason: TimeExceeded,
587 header: Ipv6Repr,
588 data: &'a [u8],
589 },
590 ParamProblem {
591 reason: ParamProblem,
592 pointer: u32,
593 header: Ipv6Repr,
594 data: &'a [u8],
595 },
596 EchoRequest {
597 ident: u16,
598 seq_no: u16,
599 data: &'a [u8],
600 },
601 EchoReply {
602 ident: u16,
603 seq_no: u16,
604 data: &'a [u8],
605 },
606 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
607 Ndisc(NdiscRepr<'a>),
608 Mld(MldRepr<'a>),
609 #[cfg(feature = "proto-rpl")]
610 Rpl(RplRepr<'a>),
611}
612
613impl<'a> Repr<'a> {
614 pub fn parse<T>(
617 src_addr: &Ipv6Address,
618 dst_addr: &Ipv6Address,
619 packet: &Packet<&'a T>,
620 checksum_caps: &ChecksumCapabilities,
621 ) -> Result<Repr<'a>>
622 where
623 T: AsRef<[u8]> + ?Sized,
624 {
625 packet.check_len()?;
626
627 fn create_packet_from_payload<'a, T>(packet: &Packet<&'a T>) -> Result<(&'a [u8], Ipv6Repr)>
628 where
629 T: AsRef<[u8]> + ?Sized,
630 {
631 let ip_packet = if packet.payload().len() >= IPV6_HEADER_LEN {
635 Ipv6Packet::new_unchecked(packet.payload())
636 } else {
637 return Err(Error);
638 };
639
640 let payload = &packet.payload()[ip_packet.header_len()..];
641 let repr = Ipv6Repr {
642 src_addr: ip_packet.src_addr(),
643 dst_addr: ip_packet.dst_addr(),
644 next_header: ip_packet.next_header(),
645 payload_len: ip_packet.payload_len().into(),
646 hop_limit: ip_packet.hop_limit(),
647 };
648 Ok((payload, repr))
649 }
650 if checksum_caps.icmpv6.rx() && !packet.verify_checksum(src_addr, dst_addr) {
652 return Err(Error);
653 }
654
655 match (packet.msg_type(), packet.msg_code()) {
656 (Message::DstUnreachable, code) => {
657 let (payload, repr) = create_packet_from_payload(packet)?;
658 Ok(Repr::DstUnreachable {
659 reason: DstUnreachable::from(code),
660 header: repr,
661 data: payload,
662 })
663 }
664 (Message::PktTooBig, 0) => {
665 let (payload, repr) = create_packet_from_payload(packet)?;
666 Ok(Repr::PktTooBig {
667 mtu: packet.pkt_too_big_mtu(),
668 header: repr,
669 data: payload,
670 })
671 }
672 (Message::TimeExceeded, code) => {
673 let (payload, repr) = create_packet_from_payload(packet)?;
674 Ok(Repr::TimeExceeded {
675 reason: TimeExceeded::from(code),
676 header: repr,
677 data: payload,
678 })
679 }
680 (Message::ParamProblem, code) => {
681 let (payload, repr) = create_packet_from_payload(packet)?;
682 Ok(Repr::ParamProblem {
683 reason: ParamProblem::from(code),
684 pointer: packet.param_problem_ptr(),
685 header: repr,
686 data: payload,
687 })
688 }
689 (Message::EchoRequest, 0) => Ok(Repr::EchoRequest {
690 ident: packet.echo_ident(),
691 seq_no: packet.echo_seq_no(),
692 data: packet.payload(),
693 }),
694 (Message::EchoReply, 0) => Ok(Repr::EchoReply {
695 ident: packet.echo_ident(),
696 seq_no: packet.echo_seq_no(),
697 data: packet.payload(),
698 }),
699 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
700 (msg_type, 0) if msg_type.is_ndisc() => NdiscRepr::parse(packet).map(Repr::Ndisc),
701 (msg_type, 0) if msg_type.is_mld() => MldRepr::parse(packet).map(Repr::Mld),
702 #[cfg(feature = "proto-rpl")]
703 (Message::RplControl, _) => RplRepr::parse(packet).map(Repr::Rpl),
704 _ => Err(Error),
705 }
706 }
707
708 pub fn buffer_len(&self) -> usize {
710 match self {
711 &Repr::DstUnreachable { header, data, .. }
712 | &Repr::PktTooBig { header, data, .. }
713 | &Repr::TimeExceeded { header, data, .. }
714 | &Repr::ParamProblem { header, data, .. } => cmp::min(
715 field::UNUSED.end + header.buffer_len() + data.len(),
716 MAX_ERROR_PACKET_LEN,
717 ),
718 &Repr::EchoRequest { data, .. } | &Repr::EchoReply { data, .. } => {
719 field::ECHO_SEQNO.end + data.len()
720 }
721 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
722 &Repr::Ndisc(ndisc) => ndisc.buffer_len(),
723 &Repr::Mld(mld) => mld.buffer_len(),
724 #[cfg(feature = "proto-rpl")]
725 Repr::Rpl(rpl) => rpl.buffer_len(),
726 }
727 }
728
729 pub fn emit<T>(
732 &self,
733 src_addr: &Ipv6Address,
734 dst_addr: &Ipv6Address,
735 packet: &mut Packet<&mut T>,
736 checksum_caps: &ChecksumCapabilities,
737 ) where
738 T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
739 {
740 fn emit_contained_packet<T>(packet: &mut Packet<&mut T>, header: Ipv6Repr, data: &[u8])
741 where
742 T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
743 {
744 let icmp_header_len = packet.header_len();
745 let mut ip_packet = Ipv6Packet::new_unchecked(packet.payload_mut());
746 header.emit(&mut ip_packet);
747 let payload = &mut ip_packet.into_inner()[header.buffer_len()..];
748 let payload_len = cmp::min(
751 data.len(),
752 MAX_ERROR_PACKET_LEN - icmp_header_len - IPV6_HEADER_LEN,
753 );
754 payload[..payload_len].copy_from_slice(&data[..payload_len]);
755 }
756
757 match *self {
758 Repr::DstUnreachable {
759 reason,
760 header,
761 data,
762 } => {
763 packet.set_msg_type(Message::DstUnreachable);
764 packet.set_msg_code(reason.into());
765
766 emit_contained_packet(packet, header, data);
767 }
768
769 Repr::PktTooBig { mtu, header, data } => {
770 packet.set_msg_type(Message::PktTooBig);
771 packet.set_msg_code(0);
772 packet.set_pkt_too_big_mtu(mtu);
773
774 emit_contained_packet(packet, header, data);
775 }
776
777 Repr::TimeExceeded {
778 reason,
779 header,
780 data,
781 } => {
782 packet.set_msg_type(Message::TimeExceeded);
783 packet.set_msg_code(reason.into());
784
785 emit_contained_packet(packet, header, data);
786 }
787
788 Repr::ParamProblem {
789 reason,
790 pointer,
791 header,
792 data,
793 } => {
794 packet.set_msg_type(Message::ParamProblem);
795 packet.set_msg_code(reason.into());
796 packet.set_param_problem_ptr(pointer);
797
798 emit_contained_packet(packet, header, data);
799 }
800
801 Repr::EchoRequest {
802 ident,
803 seq_no,
804 data,
805 } => {
806 packet.set_msg_type(Message::EchoRequest);
807 packet.set_msg_code(0);
808 packet.set_echo_ident(ident);
809 packet.set_echo_seq_no(seq_no);
810 let data_len = cmp::min(packet.payload_mut().len(), data.len());
811 packet.payload_mut()[..data_len].copy_from_slice(&data[..data_len])
812 }
813
814 Repr::EchoReply {
815 ident,
816 seq_no,
817 data,
818 } => {
819 packet.set_msg_type(Message::EchoReply);
820 packet.set_msg_code(0);
821 packet.set_echo_ident(ident);
822 packet.set_echo_seq_no(seq_no);
823 let data_len = cmp::min(packet.payload_mut().len(), data.len());
824 packet.payload_mut()[..data_len].copy_from_slice(&data[..data_len])
825 }
826
827 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
828 Repr::Ndisc(ndisc) => ndisc.emit(packet),
829
830 Repr::Mld(mld) => mld.emit(packet),
831
832 #[cfg(feature = "proto-rpl")]
833 Repr::Rpl(ref rpl) => rpl.emit(packet),
834 }
835
836 if checksum_caps.icmpv6.tx() {
837 packet.fill_checksum(src_addr, dst_addr);
838 } else {
839 packet.set_checksum(0);
841 }
842 }
843}
844
845#[cfg(test)]
846mod test {
847 use super::*;
848 use crate::wire::{IpProtocol, Ipv6Address, Ipv6Repr};
849
850 const MOCK_IP_ADDR_1: Ipv6Address = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1);
851 const MOCK_IP_ADDR_2: Ipv6Address = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 2);
852
853 static ECHO_PACKET_BYTES: [u8; 12] = [
854 0x80, 0x00, 0x19, 0xb3, 0x12, 0x34, 0xab, 0xcd, 0xaa, 0x00, 0x00, 0xff,
855 ];
856
857 static ECHO_PACKET_PAYLOAD: [u8; 4] = [0xaa, 0x00, 0x00, 0xff];
858
859 static PKT_TOO_BIG_BYTES: [u8; 60] = [
860 0x02, 0x00, 0x0f, 0xc9, 0x00, 0x00, 0x05, 0xdc, 0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x11,
861 0x40, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
862 0x00, 0x01, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
863 0x00, 0x00, 0x02, 0xbf, 0x00, 0x00, 0x35, 0x00, 0x0c, 0x12, 0x4d, 0xaa, 0x00, 0x00, 0xff,
864 ];
865
866 static PKT_TOO_BIG_IP_PAYLOAD: [u8; 52] = [
867 0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x11, 0x40, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
868 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00,
869 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xbf, 0x00, 0x00, 0x35, 0x00,
870 0x0c, 0x12, 0x4d, 0xaa, 0x00, 0x00, 0xff,
871 ];
872
873 static PKT_TOO_BIG_UDP_PAYLOAD: [u8; 12] = [
874 0xbf, 0x00, 0x00, 0x35, 0x00, 0x0c, 0x12, 0x4d, 0xaa, 0x00, 0x00, 0xff,
875 ];
876
877 fn echo_packet_repr() -> Repr<'static> {
878 Repr::EchoRequest {
879 ident: 0x1234,
880 seq_no: 0xabcd,
881 data: &ECHO_PACKET_PAYLOAD,
882 }
883 }
884
885 fn too_big_packet_repr() -> Repr<'static> {
886 Repr::PktTooBig {
887 mtu: 1500,
888 header: Ipv6Repr {
889 src_addr: Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1),
890 dst_addr: Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 2),
891 next_header: IpProtocol::Udp,
892 payload_len: 12,
893 hop_limit: 0x40,
894 },
895 data: &PKT_TOO_BIG_UDP_PAYLOAD,
896 }
897 }
898
899 #[test]
900 fn test_echo_deconstruct() {
901 let packet = Packet::new_unchecked(&ECHO_PACKET_BYTES[..]);
902 assert_eq!(packet.msg_type(), Message::EchoRequest);
903 assert_eq!(packet.msg_code(), 0);
904 assert_eq!(packet.checksum(), 0x19b3);
905 assert_eq!(packet.echo_ident(), 0x1234);
906 assert_eq!(packet.echo_seq_no(), 0xabcd);
907 assert_eq!(packet.payload(), &ECHO_PACKET_PAYLOAD[..]);
908 assert!(packet.verify_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2));
909 assert!(!packet.msg_type().is_error());
910 }
911
912 #[test]
913 fn test_echo_construct() {
914 let mut bytes = vec![0xa5; 12];
915 let mut packet = Packet::new_unchecked(&mut bytes);
916 packet.set_msg_type(Message::EchoRequest);
917 packet.set_msg_code(0);
918 packet.set_echo_ident(0x1234);
919 packet.set_echo_seq_no(0xabcd);
920 packet
921 .payload_mut()
922 .copy_from_slice(&ECHO_PACKET_PAYLOAD[..]);
923 packet.fill_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2);
924 assert_eq!(&*packet.into_inner(), &ECHO_PACKET_BYTES[..]);
925 }
926
927 #[test]
928 fn test_echo_repr_parse() {
929 let packet = Packet::new_unchecked(&ECHO_PACKET_BYTES[..]);
930 let repr = Repr::parse(
931 &MOCK_IP_ADDR_1,
932 &MOCK_IP_ADDR_2,
933 &packet,
934 &ChecksumCapabilities::default(),
935 )
936 .unwrap();
937 assert_eq!(repr, echo_packet_repr());
938 }
939
940 #[test]
941 fn test_echo_emit() {
942 let repr = echo_packet_repr();
943 let mut bytes = vec![0xa5; repr.buffer_len()];
944 let mut packet = Packet::new_unchecked(&mut bytes);
945 repr.emit(
946 &MOCK_IP_ADDR_1,
947 &MOCK_IP_ADDR_2,
948 &mut packet,
949 &ChecksumCapabilities::default(),
950 );
951 assert_eq!(&*packet.into_inner(), &ECHO_PACKET_BYTES[..]);
952 }
953
954 #[test]
955 fn test_too_big_deconstruct() {
956 let packet = Packet::new_unchecked(&PKT_TOO_BIG_BYTES[..]);
957 assert_eq!(packet.msg_type(), Message::PktTooBig);
958 assert_eq!(packet.msg_code(), 0);
959 assert_eq!(packet.checksum(), 0x0fc9);
960 assert_eq!(packet.pkt_too_big_mtu(), 1500);
961 assert_eq!(packet.payload(), &PKT_TOO_BIG_IP_PAYLOAD[..]);
962 assert!(packet.verify_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2));
963 assert!(packet.msg_type().is_error());
964 }
965
966 #[test]
967 fn test_too_big_construct() {
968 let mut bytes = vec![0xa5; 60];
969 let mut packet = Packet::new_unchecked(&mut bytes);
970 packet.set_msg_type(Message::PktTooBig);
971 packet.set_msg_code(0);
972 packet.set_pkt_too_big_mtu(1500);
973 packet
974 .payload_mut()
975 .copy_from_slice(&PKT_TOO_BIG_IP_PAYLOAD[..]);
976 packet.fill_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2);
977 assert_eq!(&*packet.into_inner(), &PKT_TOO_BIG_BYTES[..]);
978 }
979
980 #[test]
981 fn test_too_big_repr_parse() {
982 let packet = Packet::new_unchecked(&PKT_TOO_BIG_BYTES[..]);
983 let repr = Repr::parse(
984 &MOCK_IP_ADDR_1,
985 &MOCK_IP_ADDR_2,
986 &packet,
987 &ChecksumCapabilities::default(),
988 )
989 .unwrap();
990 assert_eq!(repr, too_big_packet_repr());
991 }
992
993 #[test]
994 fn test_too_big_emit() {
995 let repr = too_big_packet_repr();
996 let mut bytes = vec![0xa5; repr.buffer_len()];
997 let mut packet = Packet::new_unchecked(&mut bytes);
998 repr.emit(
999 &MOCK_IP_ADDR_1,
1000 &MOCK_IP_ADDR_2,
1001 &mut packet,
1002 &ChecksumCapabilities::default(),
1003 );
1004 assert_eq!(&*packet.into_inner(), &PKT_TOO_BIG_BYTES[..]);
1005 }
1006
1007 #[test]
1008 fn test_buffer_length_is_truncated_to_mtu() {
1009 let repr = Repr::PktTooBig {
1010 mtu: 1280,
1011 header: Ipv6Repr {
1012 src_addr: Ipv6Address::UNSPECIFIED,
1013 dst_addr: Ipv6Address::UNSPECIFIED,
1014 next_header: IpProtocol::Tcp,
1015 hop_limit: 64,
1016 payload_len: 1280,
1017 },
1018 data: &vec![0; 9999],
1019 };
1020 assert_eq!(repr.buffer_len(), 1280 - IPV6_HEADER_LEN);
1021 }
1022
1023 #[test]
1024 fn test_mtu_truncated_payload_roundtrip() {
1025 let ip_packet_repr = Ipv6Repr {
1026 src_addr: Ipv6Address::UNSPECIFIED,
1027 dst_addr: Ipv6Address::UNSPECIFIED,
1028 next_header: IpProtocol::Tcp,
1029 hop_limit: 64,
1030 payload_len: IPV6_MIN_MTU - IPV6_HEADER_LEN,
1031 };
1032 let mut ip_packet = Ipv6Packet::new_unchecked(vec![0; IPV6_MIN_MTU]);
1033 ip_packet_repr.emit(&mut ip_packet);
1034
1035 let repr1 = Repr::PktTooBig {
1036 mtu: IPV6_MIN_MTU as u32,
1037 header: ip_packet_repr,
1038 data: &ip_packet.as_ref()[IPV6_HEADER_LEN..],
1039 };
1040 let repr1 = Repr::PktTooBig {
1043 mtu: IPV6_MIN_MTU as u32,
1044 header: ip_packet_repr,
1045 data: &ip_packet.as_ref()[IPV6_HEADER_LEN..repr1.buffer_len() - field::UNUSED.end],
1046 };
1047 let mut data = vec![0; MAX_ERROR_PACKET_LEN];
1048 let mut packet = Packet::new_unchecked(&mut data);
1049 repr1.emit(
1050 &MOCK_IP_ADDR_1,
1051 &MOCK_IP_ADDR_2,
1052 &mut packet,
1053 &ChecksumCapabilities::default(),
1054 );
1055
1056 let packet = Packet::new_unchecked(&data);
1057 let repr2 = Repr::parse(
1058 &MOCK_IP_ADDR_1,
1059 &MOCK_IP_ADDR_2,
1060 &packet,
1061 &ChecksumCapabilities::default(),
1062 )
1063 .unwrap();
1064
1065 assert_eq!(repr1, repr2);
1066 }
1067
1068 #[test]
1069 fn test_truncated_payload_ipv6_header_parse_fails() {
1070 let repr = too_big_packet_repr();
1071 let mut bytes = vec![0xa5; repr.buffer_len()];
1072 let mut packet = Packet::new_unchecked(&mut bytes);
1073 repr.emit(
1074 &MOCK_IP_ADDR_1,
1075 &MOCK_IP_ADDR_2,
1076 &mut packet,
1077 &ChecksumCapabilities::default(),
1078 );
1079 let packet = Packet::new_unchecked(&bytes[..field::HEADER_END + IPV6_HEADER_LEN - 1]);
1080 assert!(Repr::parse(
1081 &MOCK_IP_ADDR_1,
1082 &MOCK_IP_ADDR_2,
1083 &packet,
1084 &ChecksumCapabilities::ignored(),
1085 )
1086 .is_err());
1087 }
1088}