1use super::{Error, NextHeader, Result, DISPATCH_EXT_HEADER, DISPATCH_UDP_HEADER};
5use crate::{
6 phy::ChecksumCapabilities,
7 wire::{ip::checksum, ipv6, udp::Repr as UdpRepr, IpProtocol},
8};
9use byteorder::{ByteOrder, NetworkEndian};
10use ipv6::Address;
11
12macro_rules! get_field {
13 ($name:ident, $mask:expr, $shift:expr) => {
14 fn $name(&self) -> u8 {
15 let data = self.buffer.as_ref();
16 let raw = &data[0];
17 ((raw >> $shift) & $mask) as u8
18 }
19 };
20}
21
22macro_rules! set_field {
23 ($name:ident, $mask:expr, $shift:expr) => {
24 fn $name(&mut self, val: u8) {
25 let data = self.buffer.as_mut();
26 let mut raw = data[0];
27 raw = (raw & !($mask << $shift)) | (val << $shift);
28 data[0] = raw;
29 }
30 };
31}
32
33#[derive(Debug, Clone)]
34#[cfg_attr(feature = "defmt", derive(defmt::Format))]
35pub enum NhcPacket {
52 ExtHeader,
53 UdpHeader,
54}
55
56impl NhcPacket {
57 pub fn dispatch(buffer: impl AsRef<[u8]>) -> Result<Self> {
64 let raw = buffer.as_ref();
65 if raw.is_empty() {
66 return Err(Error);
67 }
68
69 if raw[0] >> 4 == DISPATCH_EXT_HEADER {
70 Ok(Self::ExtHeader)
72 } else if raw[0] >> 3 == DISPATCH_UDP_HEADER {
73 Ok(Self::UdpHeader)
75 } else {
76 Err(Error)
77 }
78 }
79}
80
81#[derive(Debug, PartialEq, Eq, Clone, Copy)]
82#[cfg_attr(feature = "defmt", derive(defmt::Format))]
83pub enum ExtHeaderId {
84 HopByHopHeader,
85 RoutingHeader,
86 FragmentHeader,
87 DestinationOptionsHeader,
88 MobilityHeader,
89 Header,
90 Reserved,
91}
92
93impl From<ExtHeaderId> for IpProtocol {
94 fn from(val: ExtHeaderId) -> Self {
95 match val {
96 ExtHeaderId::HopByHopHeader => Self::HopByHop,
97 ExtHeaderId::RoutingHeader => Self::Ipv6Route,
98 ExtHeaderId::FragmentHeader => Self::Ipv6Frag,
99 ExtHeaderId::DestinationOptionsHeader => Self::Ipv6Opts,
100 ExtHeaderId::MobilityHeader => Self::Unknown(0),
101 ExtHeaderId::Header => Self::Unknown(0),
102 ExtHeaderId::Reserved => Self::Unknown(0),
103 }
104 }
105}
106
107#[derive(Debug, Clone)]
109#[cfg_attr(feature = "defmt", derive(defmt::Format))]
110pub struct ExtHeaderPacket<T: AsRef<[u8]>> {
111 buffer: T,
112}
113
114impl<T: AsRef<[u8]>> ExtHeaderPacket<T> {
115 pub const fn new_unchecked(buffer: T) -> Self {
117 ExtHeaderPacket { buffer }
118 }
119
120 pub fn new_checked(buffer: T) -> Result<Self> {
125 let packet = Self::new_unchecked(buffer);
126 packet.check_len()?;
127
128 if packet.eid_field() > 7 {
129 return Err(Error);
130 }
131
132 Ok(packet)
133 }
134
135 pub fn check_len(&self) -> Result<()> {
138 let buffer = self.buffer.as_ref();
139
140 if buffer.is_empty() {
141 return Err(Error);
142 }
143
144 let mut len = 2;
145 len += self.next_header_size();
146
147 if len <= buffer.len() {
148 Ok(())
149 } else {
150 Err(Error)
151 }
152 }
153
154 pub fn into_inner(self) -> T {
156 self.buffer
157 }
158
159 get_field!(dispatch_field, 0b1111, 4);
160 get_field!(eid_field, 0b111, 1);
161 get_field!(nh_field, 0b1, 0);
162
163 pub fn extension_header_id(&self) -> ExtHeaderId {
165 match self.eid_field() {
166 0 => ExtHeaderId::HopByHopHeader,
167 1 => ExtHeaderId::RoutingHeader,
168 2 => ExtHeaderId::FragmentHeader,
169 3 => ExtHeaderId::DestinationOptionsHeader,
170 4 => ExtHeaderId::MobilityHeader,
171 5 | 6 => ExtHeaderId::Reserved,
172 7 => ExtHeaderId::Header,
173 _ => unreachable!(),
174 }
175 }
176
177 pub fn length(&self) -> u8 {
179 self.buffer.as_ref()[1 + self.next_header_size()]
180 }
181
182 pub fn next_header(&self) -> NextHeader {
184 if self.nh_field() == 1 {
185 NextHeader::Compressed
186 } else {
187 NextHeader::Uncompressed(IpProtocol::from(self.buffer.as_ref()[1]))
189 }
190 }
191
192 fn next_header_size(&self) -> usize {
194 match self.nh_field() {
196 0 => 1,
197 1 => 0,
198 _ => unreachable!(),
199 }
200 }
201}
202
203impl<'a, T: AsRef<[u8]> + ?Sized> ExtHeaderPacket<&'a T> {
204 pub fn payload(&self) -> &'a [u8] {
206 let start = 2 + self.next_header_size();
207 let len = self.length() as usize;
208 &self.buffer.as_ref()[start..][..len]
209 }
210}
211
212impl<T: AsRef<[u8]> + AsMut<[u8]>> ExtHeaderPacket<T> {
213 pub fn payload_mut(&mut self) -> &mut [u8] {
215 let start = 2 + self.next_header_size();
216 let len = self.length() as usize;
217 &mut self.buffer.as_mut()[start..][..len]
218 }
219
220 fn set_dispatch_field(&mut self) {
222 let data = self.buffer.as_mut();
223 data[0] = (data[0] & !(0b1111 << 4)) | (DISPATCH_EXT_HEADER << 4);
224 }
225
226 set_field!(set_eid_field, 0b111, 1);
227 set_field!(set_nh_field, 0b1, 0);
228
229 fn set_extension_header_id(&mut self, ext_header_id: ExtHeaderId) {
231 let id = match ext_header_id {
232 ExtHeaderId::HopByHopHeader => 0,
233 ExtHeaderId::RoutingHeader => 1,
234 ExtHeaderId::FragmentHeader => 2,
235 ExtHeaderId::DestinationOptionsHeader => 3,
236 ExtHeaderId::MobilityHeader => 4,
237 ExtHeaderId::Reserved => 5,
238 ExtHeaderId::Header => 7,
239 };
240
241 self.set_eid_field(id);
242 }
243
244 fn set_next_header(&mut self, next_header: NextHeader) {
246 match next_header {
247 NextHeader::Compressed => self.set_nh_field(0b1),
248 NextHeader::Uncompressed(nh) => {
249 self.set_nh_field(0b0);
250
251 let start = 1;
252 let data = self.buffer.as_mut();
253 data[start] = nh.into();
254 }
255 }
256 }
257
258 fn set_length(&mut self, length: u8) {
260 let start = 1 + self.next_header_size();
261
262 let data = self.buffer.as_mut();
263 data[start] = length;
264 }
265}
266
267#[derive(Debug, PartialEq, Eq, Clone, Copy)]
269#[cfg_attr(feature = "defmt", derive(defmt::Format))]
270pub struct ExtHeaderRepr {
271 pub ext_header_id: ExtHeaderId,
272 pub next_header: NextHeader,
273 pub length: u8,
274}
275
276impl ExtHeaderRepr {
277 pub fn parse<T: AsRef<[u8]> + ?Sized>(packet: &ExtHeaderPacket<&T>) -> Result<Self> {
279 packet.check_len()?;
281
282 if packet.dispatch_field() != DISPATCH_EXT_HEADER {
283 return Err(Error);
284 }
285
286 Ok(Self {
287 ext_header_id: packet.extension_header_id(),
288 next_header: packet.next_header(),
289 length: packet.length(),
290 })
291 }
292
293 pub fn buffer_len(&self) -> usize {
295 let mut len = 1; if self.next_header != NextHeader::Compressed {
298 len += 1;
299 }
300
301 len += 1; len
304 }
305
306 pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(&self, packet: &mut ExtHeaderPacket<T>) {
308 packet.set_dispatch_field();
309 packet.set_extension_header_id(self.ext_header_id);
310 packet.set_next_header(self.next_header);
311 packet.set_length(self.length);
312 }
313}
314
315#[cfg(test)]
316mod tests {
317 use super::*;
318
319 use crate::wire::{Ipv6RoutingHeader, Ipv6RoutingRepr};
320
321 #[cfg(feature = "proto-rpl")]
322 use crate::wire::{
323 Ipv6Option, Ipv6OptionRepr, Ipv6OptionsIterator, RplHopByHopRepr, RplInstanceId,
324 };
325
326 #[cfg(feature = "proto-rpl")]
327 const RPL_HOP_BY_HOP_PACKET: [u8; 9] = [0xe0, 0x3a, 0x06, 0x63, 0x04, 0x00, 0x1e, 0x03, 0x00];
328
329 const ROUTING_SR_PACKET: [u8; 32] = [
330 0xe3, 0x1e, 0x03, 0x03, 0x99, 0x30, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05,
331 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00,
332 0x00, 0x00,
333 ];
334
335 #[test]
336 #[cfg(feature = "proto-rpl")]
337 fn test_rpl_hop_by_hop_option_deconstruct() {
338 let header = ExtHeaderPacket::new_checked(&RPL_HOP_BY_HOP_PACKET).unwrap();
339 assert_eq!(
340 header.next_header(),
341 NextHeader::Uncompressed(IpProtocol::Icmpv6)
342 );
343 assert_eq!(header.extension_header_id(), ExtHeaderId::HopByHopHeader);
344
345 let options = header.payload();
346 let mut options = Ipv6OptionsIterator::new(options);
347 let rpl_repr = options.next().unwrap();
348 let rpl_repr = rpl_repr.unwrap();
349
350 match rpl_repr {
351 Ipv6OptionRepr::Rpl(rpl) => {
352 assert_eq!(
353 rpl,
354 RplHopByHopRepr {
355 down: false,
356 rank_error: false,
357 forwarding_error: false,
358 instance_id: RplInstanceId::from(0x1e),
359 sender_rank: 0x0300,
360 }
361 );
362 }
363 _ => unreachable!(),
364 }
365 }
366
367 #[test]
368 #[cfg(feature = "proto-rpl")]
369 fn test_rpl_hop_by_hop_option_emit() {
370 let repr = Ipv6OptionRepr::Rpl(RplHopByHopRepr {
371 down: false,
372 rank_error: false,
373 forwarding_error: false,
374 instance_id: RplInstanceId::from(0x1e),
375 sender_rank: 0x0300,
376 });
377
378 let ext_hdr = ExtHeaderRepr {
379 ext_header_id: ExtHeaderId::HopByHopHeader,
380 next_header: NextHeader::Uncompressed(IpProtocol::Icmpv6),
381 length: repr.buffer_len() as u8,
382 };
383
384 let mut buffer = vec![0u8; ext_hdr.buffer_len() + repr.buffer_len()];
385 ext_hdr.emit(&mut ExtHeaderPacket::new_unchecked(
386 &mut buffer[..ext_hdr.buffer_len()],
387 ));
388 repr.emit(&mut Ipv6Option::new_unchecked(
389 &mut buffer[ext_hdr.buffer_len()..],
390 ));
391
392 assert_eq!(&buffer[..], RPL_HOP_BY_HOP_PACKET);
393 }
394
395 #[test]
396 fn test_source_routing_deconstruct() {
397 let header = ExtHeaderPacket::new_checked(&ROUTING_SR_PACKET).unwrap();
398 assert_eq!(header.next_header(), NextHeader::Compressed);
399 assert_eq!(header.extension_header_id(), ExtHeaderId::RoutingHeader);
400
401 let routing_hdr = Ipv6RoutingHeader::new_checked(header.payload()).unwrap();
402 let repr = Ipv6RoutingRepr::parse(&routing_hdr).unwrap();
403 assert_eq!(
404 repr,
405 Ipv6RoutingRepr::Rpl {
406 segments_left: 3,
407 cmpr_i: 9,
408 cmpr_e: 9,
409 pad: 3,
410 addresses: &[
411 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00,
412 0x06, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00
413 ],
414 }
415 );
416 }
417
418 #[test]
419 fn test_source_routing_emit() {
420 let routing_hdr = Ipv6RoutingRepr::Rpl {
421 segments_left: 3,
422 cmpr_i: 9,
423 cmpr_e: 9,
424 pad: 3,
425 addresses: &[
426 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06,
427 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00,
428 ],
429 };
430
431 let ext_hdr = ExtHeaderRepr {
432 ext_header_id: ExtHeaderId::RoutingHeader,
433 next_header: NextHeader::Compressed,
434 length: routing_hdr.buffer_len() as u8,
435 };
436
437 let mut buffer = vec![0u8; ext_hdr.buffer_len() + routing_hdr.buffer_len()];
438 ext_hdr.emit(&mut ExtHeaderPacket::new_unchecked(
439 &mut buffer[..ext_hdr.buffer_len()],
440 ));
441 routing_hdr.emit(&mut Ipv6RoutingHeader::new_unchecked(
442 &mut buffer[ext_hdr.buffer_len()..],
443 ));
444
445 assert_eq!(&buffer[..], ROUTING_SR_PACKET);
446 }
447}
448
449#[derive(Debug, Clone)]
465#[cfg_attr(feature = "defmt", derive(defmt::Format))]
466pub struct UdpNhcPacket<T: AsRef<[u8]>> {
467 buffer: T,
468}
469
470impl<T: AsRef<[u8]>> UdpNhcPacket<T> {
471 pub const fn new_unchecked(buffer: T) -> Self {
473 Self { buffer }
474 }
475
476 pub fn new_checked(buffer: T) -> Result<Self> {
481 let packet = Self::new_unchecked(buffer);
482 packet.check_len()?;
483 Ok(packet)
484 }
485
486 pub fn check_len(&self) -> Result<()> {
489 let buffer = self.buffer.as_ref();
490
491 if buffer.is_empty() {
492 return Err(Error);
493 }
494
495 let index = 1 + self.ports_size() + self.checksum_size();
496 if index > buffer.len() {
497 return Err(Error);
498 }
499
500 Ok(())
501 }
502
503 pub fn into_inner(self) -> T {
505 self.buffer
506 }
507
508 get_field!(dispatch_field, 0b11111, 3);
509 get_field!(checksum_field, 0b1, 2);
510 get_field!(ports_field, 0b11, 0);
511
512 const fn nhc_fields_start(&self) -> usize {
514 1
515 }
516
517 pub fn src_port(&self) -> u16 {
519 match self.ports_field() {
520 0b00 | 0b01 => {
521 let data = self.buffer.as_ref();
523 let start = self.nhc_fields_start();
524
525 NetworkEndian::read_u16(&data[start..start + 2])
526 }
527 0b10 => {
528 let data = self.buffer.as_ref();
530 let start = self.nhc_fields_start();
531
532 0xf000 + data[start] as u16
533 }
534 0b11 => {
535 let data = self.buffer.as_ref();
537 let start = self.nhc_fields_start();
538
539 0xf0b0 + (data[start] >> 4) as u16
540 }
541 _ => unreachable!(),
542 }
543 }
544
545 pub fn dst_port(&self) -> u16 {
547 match self.ports_field() {
548 0b00 => {
549 let data = self.buffer.as_ref();
551 let idx = self.nhc_fields_start();
552
553 NetworkEndian::read_u16(&data[idx + 2..idx + 4])
554 }
555 0b01 => {
556 let data = self.buffer.as_ref();
558 let idx = self.nhc_fields_start();
559
560 0xf000 + data[idx] as u16
561 }
562 0b10 => {
563 let data = self.buffer.as_ref();
565 let idx = self.nhc_fields_start();
566
567 NetworkEndian::read_u16(&data[idx + 1..idx + 1 + 2])
568 }
569 0b11 => {
570 let data = self.buffer.as_ref();
572 let start = self.nhc_fields_start();
573
574 0xf0b0 + (data[start] & 0xff) as u16
575 }
576 _ => unreachable!(),
577 }
578 }
579
580 pub fn checksum(&self) -> Option<u16> {
582 if self.checksum_field() == 0b0 {
583 let data = self.buffer.as_ref();
585 let start = self.nhc_fields_start() + self.ports_size();
586 Some(NetworkEndian::read_u16(&data[start..start + 2]))
587 } else {
588 None
590 }
591 }
592
593 pub(crate) fn checksum_size(&self) -> usize {
595 match self.checksum_field() {
596 0b0 => 2,
597 0b1 => 0,
598 _ => unreachable!(),
599 }
600 }
601
602 pub(crate) fn ports_size(&self) -> usize {
604 match self.ports_field() {
605 0b00 => 4, 0b01 => 3, 0b10 => 3, 0b11 => 1, _ => unreachable!(),
610 }
611 }
612}
613
614impl<'a, T: AsRef<[u8]> + ?Sized> UdpNhcPacket<&'a T> {
615 pub fn payload(&self) -> &'a [u8] {
617 let start = 1 + self.ports_size() + self.checksum_size();
618 &self.buffer.as_ref()[start..]
619 }
620}
621
622impl<T: AsRef<[u8]> + AsMut<[u8]>> UdpNhcPacket<T> {
623 pub fn payload_mut(&mut self) -> &mut [u8] {
625 let start = 1 + self.ports_size() + 2; &mut self.buffer.as_mut()[start..]
627 }
628
629 fn set_dispatch_field(&mut self) {
631 let data = self.buffer.as_mut();
632 data[0] = (data[0] & !(0b11111 << 3)) | (DISPATCH_UDP_HEADER << 3);
633 }
634
635 set_field!(set_checksum_field, 0b1, 2);
636 set_field!(set_ports_field, 0b11, 0);
637
638 fn set_ports(&mut self, src_port: u16, dst_port: u16) {
639 let mut idx = 1;
640
641 match (src_port, dst_port) {
642 (0xf0b0..=0xf0bf, 0xf0b0..=0xf0bf) => {
643 self.set_ports_field(0b11);
645 let data = self.buffer.as_mut();
646 data[idx] = (((src_port - 0xf0b0) as u8) << 4) & ((dst_port - 0xf0b0) as u8);
647 }
648 (0xf000..=0xf0ff, _) => {
649 self.set_ports_field(0b10);
651 let data = self.buffer.as_mut();
652 data[idx] = (src_port - 0xf000) as u8;
653 idx += 1;
654
655 NetworkEndian::write_u16(&mut data[idx..idx + 2], dst_port);
656 }
657 (_, 0xf000..=0xf0ff) => {
658 self.set_ports_field(0b01);
660 let data = self.buffer.as_mut();
661 NetworkEndian::write_u16(&mut data[idx..idx + 2], src_port);
662 idx += 2;
663 data[idx] = (dst_port - 0xf000) as u8;
664 }
665 (_, _) => {
666 self.set_ports_field(0b00);
668 let data = self.buffer.as_mut();
669 NetworkEndian::write_u16(&mut data[idx..idx + 2], src_port);
670 idx += 2;
671 NetworkEndian::write_u16(&mut data[idx..idx + 2], dst_port);
672 }
673 };
674 }
675
676 fn set_checksum(&mut self, checksum: u16) {
677 self.set_checksum_field(0b0);
678 let idx = 1 + self.ports_size();
679 let data = self.buffer.as_mut();
680 NetworkEndian::write_u16(&mut data[idx..idx + 2], checksum);
681 }
682}
683
684#[derive(Debug, PartialEq, Eq, Clone, Copy)]
686#[cfg_attr(feature = "defmt", derive(defmt::Format))]
687pub struct UdpNhcRepr(pub UdpRepr);
688
689impl<'a> UdpNhcRepr {
690 pub fn parse<T: AsRef<[u8]> + ?Sized>(
692 packet: &UdpNhcPacket<&'a T>,
693 src_addr: &ipv6::Address,
694 dst_addr: &ipv6::Address,
695 checksum_caps: &ChecksumCapabilities,
696 ) -> Result<Self> {
697 packet.check_len()?;
698
699 if packet.dispatch_field() != DISPATCH_UDP_HEADER {
700 return Err(Error);
701 }
702
703 if checksum_caps.udp.rx() {
704 let payload_len = packet.payload().len();
705 let chk_sum = !checksum::combine(&[
706 checksum::pseudo_header_v6(
707 src_addr,
708 dst_addr,
709 crate::wire::ip::Protocol::Udp,
710 payload_len as u32 + 8,
711 ),
712 packet.src_port(),
713 packet.dst_port(),
714 payload_len as u16 + 8,
715 checksum::data(packet.payload()),
716 ]);
717
718 if let Some(checksum) = packet.checksum() {
719 if chk_sum != checksum {
720 return Err(Error);
721 }
722 }
723 }
724
725 Ok(Self(UdpRepr {
726 src_port: packet.src_port(),
727 dst_port: packet.dst_port(),
728 }))
729 }
730
731 pub fn header_len(&self) -> usize {
733 let mut len = 1; len += 2; match (self.src_port, self.dst_port) {
739 (0xf0b0..=0xf0bf, 0xf0b0..=0xf0bf) => len + 1,
740 (0xf000..=0xf0ff, _) | (_, 0xf000..=0xf0ff) => len + 3,
741 (_, _) => len + 4,
742 }
743 }
744
745 pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(
747 &self,
748 packet: &mut UdpNhcPacket<T>,
749 src_addr: &Address,
750 dst_addr: &Address,
751 payload_len: usize,
752 emit_payload: impl FnOnce(&mut [u8]),
753 checksum_caps: &ChecksumCapabilities,
754 ) {
755 packet.set_dispatch_field();
756 packet.set_ports(self.src_port, self.dst_port);
757 emit_payload(packet.payload_mut());
758
759 if checksum_caps.udp.tx() {
760 let chk_sum = !checksum::combine(&[
761 checksum::pseudo_header_v6(
762 src_addr,
763 dst_addr,
764 crate::wire::ip::Protocol::Udp,
765 payload_len as u32 + 8,
766 ),
767 self.src_port,
768 self.dst_port,
769 payload_len as u16 + 8,
770 checksum::data(packet.payload_mut()),
771 ]);
772
773 packet.set_checksum(chk_sum);
774 }
775 }
776}
777
778impl core::ops::Deref for UdpNhcRepr {
779 type Target = UdpRepr;
780
781 fn deref(&self) -> &Self::Target {
782 &self.0
783 }
784}
785
786impl core::ops::DerefMut for UdpNhcRepr {
787 fn deref_mut(&mut self) -> &mut Self::Target {
788 &mut self.0
789 }
790}
791
792#[cfg(test)]
793mod test {
794 use super::*;
795
796 #[test]
797 fn ext_header_nh_inlined() {
798 let bytes = [0xe2, 0x3a, 0x6, 0x3, 0x0, 0xff, 0x0, 0x0, 0x0];
799
800 let packet = ExtHeaderPacket::new_checked(&bytes[..]).unwrap();
801 assert_eq!(packet.next_header_size(), 1);
802 assert_eq!(packet.length(), 6);
803 assert_eq!(packet.dispatch_field(), DISPATCH_EXT_HEADER);
804 assert_eq!(packet.extension_header_id(), ExtHeaderId::RoutingHeader);
805 assert_eq!(
806 packet.next_header(),
807 NextHeader::Uncompressed(IpProtocol::Icmpv6)
808 );
809
810 assert_eq!(packet.payload(), [0x03, 0x00, 0xff, 0x00, 0x00, 0x00]);
811 }
812
813 #[test]
814 fn ext_header_nh_elided() {
815 let bytes = [0xe3, 0x06, 0x03, 0x00, 0xff, 0x00, 0x00, 0x00];
816
817 let packet = ExtHeaderPacket::new_checked(&bytes[..]).unwrap();
818 assert_eq!(packet.next_header_size(), 0);
819 assert_eq!(packet.length(), 6);
820 assert_eq!(packet.dispatch_field(), DISPATCH_EXT_HEADER);
821 assert_eq!(packet.extension_header_id(), ExtHeaderId::RoutingHeader);
822 assert_eq!(packet.next_header(), NextHeader::Compressed);
823
824 assert_eq!(packet.payload(), [0x03, 0x00, 0xff, 0x00, 0x00, 0x00]);
825 }
826
827 #[test]
828 fn ext_header_emit() {
829 let ext_header = ExtHeaderRepr {
830 ext_header_id: ExtHeaderId::RoutingHeader,
831 next_header: NextHeader::Compressed,
832 length: 6,
833 };
834
835 let len = ext_header.buffer_len();
836 let mut buffer = [0u8; 127];
837 let mut packet = ExtHeaderPacket::new_unchecked(&mut buffer[..len]);
838 ext_header.emit(&mut packet);
839
840 assert_eq!(packet.dispatch_field(), DISPATCH_EXT_HEADER);
841 assert_eq!(packet.next_header(), NextHeader::Compressed);
842 assert_eq!(packet.extension_header_id(), ExtHeaderId::RoutingHeader);
843 }
844
845 #[test]
846 fn udp_nhc_fields() {
847 let bytes = [0xf0, 0x16, 0x2e, 0x22, 0x3d, 0x28, 0xc4];
848
849 let packet = UdpNhcPacket::new_checked(&bytes[..]).unwrap();
850 assert_eq!(packet.dispatch_field(), DISPATCH_UDP_HEADER);
851 assert_eq!(packet.checksum(), Some(0x28c4));
852 assert_eq!(packet.src_port(), 5678);
853 assert_eq!(packet.dst_port(), 8765);
854 }
855
856 #[test]
857 fn udp_emit() {
858 let udp = UdpNhcRepr(UdpRepr {
859 src_port: 0xf0b1,
860 dst_port: 0xf001,
861 });
862
863 let payload = b"Hello World!";
864
865 let src_addr = ipv6::Address::UNSPECIFIED;
866 let dst_addr = ipv6::Address::UNSPECIFIED;
867
868 let len = udp.header_len() + payload.len();
869 let mut buffer = [0u8; 127];
870 let mut packet = UdpNhcPacket::new_unchecked(&mut buffer[..len]);
871 udp.emit(
872 &mut packet,
873 &src_addr,
874 &dst_addr,
875 payload.len(),
876 |buf| buf.copy_from_slice(&payload[..]),
877 &ChecksumCapabilities::default(),
878 );
879
880 assert_eq!(packet.dispatch_field(), DISPATCH_UDP_HEADER);
881 assert_eq!(packet.src_port(), 0xf0b1);
882 assert_eq!(packet.dst_port(), 0xf001);
883 assert_eq!(packet.payload_mut(), b"Hello World!");
884 }
885}