1use crate::net::ieee802154::MacAddress;
6use crate::net::ipv6::ip_utils::{compute_udp_checksum, ip6_nh, IPAddr};
7use crate::net::ipv6::{IP6Header, IP6Packet, TransportHeader};
8use crate::net::udp::UDPHeader;
9use crate::net::util;
10use crate::net::util::{network_slice_to_u16, u16_to_network_slice};
11use core::mem;
14
15mod iphc {
18 pub const DISPATCH: [u8; 2] = [0x60, 0x00];
19
20 pub const TF_TRAFFIC_CLASS: u8 = 0x08;
23 pub const TF_FLOW_LABEL: u8 = 0x10;
24
25 pub const NH: u8 = 0x04;
26
27 pub const HLIM_MASK: u8 = 0x03;
28 pub const HLIM_INLINE: u8 = 0x00;
29 pub const HLIM_1: u8 = 0x01;
30 pub const HLIM_64: u8 = 0x02;
31 pub const HLIM_255: u8 = 0x03;
32
33 pub const CID: u8 = 0x80;
36
37 pub const SAC: u8 = 0x40;
38
39 pub const SAM_MASK: u8 = 0x30;
40 pub const SAM_INLINE: u8 = 0x00;
41 pub const SAM_MODE1: u8 = 0x10;
42 pub const SAM_MODE2: u8 = 0x20;
43 pub const SAM_MODE3: u8 = 0x30;
44
45 pub const MULTICAST: u8 = 0x08;
46
47 pub const DAC: u8 = 0x04;
48 pub const DAM_MASK: u8 = 0x03;
49 pub const DAM_INLINE: u8 = 0x00;
50 pub const DAM_MODE1: u8 = 0x01;
51 pub const DAM_MODE2: u8 = 0x02;
52 pub const DAM_MODE3: u8 = 0x03;
53
54 pub const MAC_BASE: [u8; 8] = [0, 0, 0, 0xff, 0xfe, 0, 0, 0];
56 pub const MAC_UL: u8 = 0x02;
57}
58
59mod nhc {
62 pub const DISPATCH_NHC: u8 = 0xe0;
63 pub const DISPATCH_UDP: u8 = 0xf0;
64 pub const DISPATCH_MASK: u8 = 0xf0;
65
66 pub const EID_MASK: u8 = 0x0e;
67 pub const HOP_OPTS: u8 = 0 << 1;
68 pub const ROUTING: u8 = 1 << 1;
69 pub const FRAGMENT: u8 = 2 << 1;
70 pub const DST_OPTS: u8 = 3 << 1;
71 pub const MOBILITY: u8 = 4 << 1;
72 pub const IP6: u8 = 7 << 1;
73
74 pub const NH: u8 = 0x01;
75
76 pub const UDP_4BIT_PORT: u16 = 0xf0b0;
79 pub const UDP_4BIT_PORT_MASK: u16 = 0xfff0;
80 pub const UDP_8BIT_PORT: u16 = 0xf000;
81 pub const UDP_8BIT_PORT_MASK: u16 = 0xff00;
82
83 pub const UDP_CHECKSUM_FLAG: u8 = 0b100;
84 pub const UDP_SRC_PORT_FLAG: u8 = 0b010;
85 pub const UDP_DST_PORT_FLAG: u8 = 0b001;
86 pub const UDP_PORTS_SIZE: u16 = 4;
87}
88
89#[derive(Copy, Clone, Debug)]
90pub struct Context {
91 pub prefix: [u8; 16],
92 pub prefix_len: u8,
93 pub id: u8,
94 pub compress: bool,
95}
96
97pub trait ContextStore {
103 fn get_context_from_addr(&self, ip_addr: IPAddr) -> Option<Context>;
104 fn get_context_from_id(&self, ctx_id: u8) -> Option<Context>;
105 fn get_context_0(&self) -> Context {
106 match self.get_context_from_id(0) {
107 Some(ctx) => ctx,
108 None => panic!("Context 0 not found"),
109 }
110 }
111 fn get_context_from_prefix(&self, prefix: &[u8], prefix_len: u8) -> Option<Context>;
112}
113
114pub fn compute_iid(mac_addr: &MacAddress) -> [u8; 8] {
117 match *mac_addr {
118 MacAddress::Short(short_addr) => {
119 let mut iid: [u8; 8] = iphc::MAC_BASE;
121 iid[6] = (short_addr >> 1) as u8;
122 iid[7] = (short_addr & 0xff) as u8;
123 iid
124 }
125 MacAddress::Long(long_addr) => {
126 let mut iid: [u8; 8] = long_addr;
128 iid[0] ^= iphc::MAC_UL;
129 iid
130 }
131 }
132}
133
134impl ContextStore for Context {
135 fn get_context_from_addr(&self, ip_addr: IPAddr) -> Option<Context> {
136 if util::matches_prefix(&ip_addr.0, &self.prefix, self.prefix_len) {
137 Some(*self)
138 } else {
139 None
140 }
141 }
142
143 fn get_context_from_id(&self, ctx_id: u8) -> Option<Context> {
144 if ctx_id == 0 {
145 Some(*self)
146 } else {
147 None
148 }
149 }
150
151 fn get_context_from_prefix(&self, prefix: &[u8], prefix_len: u8) -> Option<Context> {
152 if prefix_len == self.prefix_len && util::matches_prefix(prefix, &self.prefix, prefix_len) {
153 Some(*self)
154 } else {
155 None
156 }
157 }
158}
159
160pub fn is_lowpan(packet: &[u8]) -> bool {
161 (packet[0] & iphc::DISPATCH[0]) == iphc::DISPATCH[0]
162}
163
164fn nhc_to_ip6_nh(nhc: u8) -> Result<u8, ()> {
167 match nhc & nhc::DISPATCH_MASK {
168 nhc::DISPATCH_NHC => match nhc & nhc::EID_MASK {
169 nhc::HOP_OPTS => Ok(ip6_nh::HOP_OPTS),
170 nhc::ROUTING => Ok(ip6_nh::ROUTING),
171 nhc::FRAGMENT => Ok(ip6_nh::FRAGMENT),
172 nhc::DST_OPTS => Ok(ip6_nh::DST_OPTS),
173 nhc::MOBILITY => Ok(ip6_nh::MOBILITY),
174 nhc::IP6 => Ok(ip6_nh::IP6),
175 _ => Err(()),
176 },
177 nhc::DISPATCH_UDP => Ok(ip6_nh::UDP),
178 _ => Err(()),
179 }
180}
181
182pub fn compress<'a>(
192 ctx_store: &dyn ContextStore,
193 ip6_packet: &'a IP6Packet<'a>,
194 src_mac_addr: MacAddress,
195 dst_mac_addr: MacAddress,
196 buf: &mut [u8],
197) -> Result<(usize, usize), ()> {
198 let mut consumed = 40; let ip6_header = ip6_packet.header;
202
203 let mut written: usize = 2;
207
208 buf[0..2].copy_from_slice(&iphc::DISPATCH);
210
211 let mut src_ctx: Option<Context> = ctx_store.get_context_from_addr(ip6_header.src_addr);
212 let mut dst_ctx: Option<Context> = if ip6_header.dst_addr.is_multicast() {
213 let prefix_len: u8 = ip6_header.dst_addr.0[3];
214 let prefix: &[u8] = &ip6_header.dst_addr.0[4..12];
215 if util::verify_prefix_len(prefix, prefix_len) {
217 ctx_store.get_context_from_prefix(prefix, prefix_len)
218 } else {
219 None
220 }
221 } else {
222 ctx_store.get_context_from_addr(ip6_header.dst_addr)
223 };
224
225 src_ctx = src_ctx.and_then(|ctx| if ctx.compress { Some(ctx) } else { None });
227 dst_ctx = dst_ctx.and_then(|ctx| if ctx.compress { Some(ctx) } else { None });
228
229 compress_cie(src_ctx.as_ref(), dst_ctx.as_ref(), buf, &mut written);
231
232 compress_tf(&ip6_header, buf, &mut written);
234
235 let is_nhc = ip6_header.next_header == ip6_nh::UDP;
239 compress_nh(&ip6_header, is_nhc, buf, &mut written);
240
241 compress_hl(&ip6_header, buf, &mut written);
243
244 compress_src(
246 &ip6_header.src_addr,
247 &src_mac_addr,
248 src_ctx.as_ref(),
249 buf,
250 &mut written,
251 );
252
253 if ip6_header.dst_addr.is_multicast() {
255 compress_multicast(&ip6_header.dst_addr, dst_ctx.as_ref(), buf, &mut written);
256 } else {
257 compress_dst(
258 &ip6_header.dst_addr,
259 &dst_mac_addr,
260 dst_ctx.as_ref(),
261 buf,
262 &mut written,
263 );
264 }
265
266 if is_nhc {
271 match ip6_packet.payload.header {
272 TransportHeader::UDP(udp_header) => {
273 let mut nhc_header = nhc::DISPATCH_UDP;
274
275 let udp_nh_offset = written;
277 written += 1;
278
279 nhc_header |= compress_udp_ports(&udp_header, buf, &mut written);
281 nhc_header |= compress_udp_checksum(&udp_header, buf, &mut written);
282
283 buf[udp_nh_offset] = nhc_header;
285 consumed += 8;
286 }
287 _ => return Err(()),
290 }
291 }
292 Ok((consumed, written))
293}
294
295fn compress_cie(
296 src_ctx: Option<&Context>,
297 dst_ctx: Option<&Context>,
298 buf: &mut [u8],
299 written: &mut usize,
300) {
301 let mut cie: u8 = 0;
302
303 src_ctx.as_ref().map(|ctx| {
304 if ctx.id != 0 {
305 cie |= ctx.id << 4;
306 }
307 });
308 dst_ctx.as_ref().map(|ctx| {
309 if ctx.id != 0 {
310 cie |= ctx.id;
311 }
312 });
313
314 if cie != 0 {
315 buf[1] |= iphc::CID;
316 buf[*written] = cie;
317 *written += 1;
318 }
319}
320
321fn compress_tf(ip6_header: &IP6Header, buf: &mut [u8], written: &mut usize) {
322 let ecn = ip6_header.get_ecn();
323 let dscp = ip6_header.get_dscp();
324 let flow = ip6_header.get_flow_label();
325
326 let mut tf_encoding = 0;
327 let old_offset = *written;
328
329 if dscp == 0 && (ecn == 0 || flow != 0) {
332 tf_encoding |= iphc::TF_TRAFFIC_CLASS;
333 } else {
334 buf[*written] = dscp;
335 *written += 1;
336 }
337
338 if flow == 0 {
340 tf_encoding |= iphc::TF_FLOW_LABEL;
341 } else {
342 buf[*written] = ((flow >> 16) & 0x0f) as u8;
343 buf[*written + 1] = (flow >> 8) as u8;
344 buf[*written + 2] = flow as u8;
345 *written += 3;
346 }
347
348 if *written != old_offset {
349 buf[old_offset] |= ecn << 6;
350 }
351 buf[0] |= tf_encoding;
352}
353
354fn compress_nh(ip6_header: &IP6Header, is_nhc: bool, buf: &mut [u8], written: &mut usize) {
355 if is_nhc {
356 buf[0] |= iphc::NH;
357 } else {
358 buf[*written] = ip6_header.next_header;
359 *written += 1;
360 }
361}
362
363fn compress_hl(ip6_header: &IP6Header, buf: &mut [u8], written: &mut usize) {
364 let hop_limit_flag = match ip6_header.hop_limit {
365 1 => iphc::HLIM_1,
366 64 => iphc::HLIM_64,
367 255 => iphc::HLIM_255,
368 _ => {
369 buf[*written] = ip6_header.hop_limit;
370 *written += 1;
371 iphc::HLIM_INLINE
372 }
373 };
374 buf[0] |= hop_limit_flag;
375}
376
377fn compress_src(
381 src_ip_addr: &IPAddr,
382 src_mac_addr: &MacAddress,
383 src_ctx: Option<&Context>,
384 buf: &mut [u8],
385 written: &mut usize,
386) {
387 if src_ip_addr.is_unspecified() {
388 buf[1] |= iphc::SAC;
390 } else if src_ip_addr.is_unicast_link_local() {
391 compress_iid(src_ip_addr, src_mac_addr, true, buf, written);
393 } else if src_ctx.is_some() {
394 buf[1] |= iphc::SAC;
396 compress_iid(src_ip_addr, src_mac_addr, true, buf, written);
397 } else {
398 buf[*written..*written + 16].copy_from_slice(&src_ip_addr.0);
400 *written += 16;
401 }
402}
403
404fn compress_iid(
408 ip_addr: &IPAddr,
409 mac_addr: &MacAddress,
410 is_src: bool,
411 buf: &mut [u8],
412 written: &mut usize,
413) {
414 let iid: [u8; 8] = compute_iid(mac_addr);
415 if ip_addr.0[8..16] == iid {
416 buf[1] |= if is_src {
418 iphc::SAM_MODE3
419 } else {
420 iphc::DAM_MODE3
421 };
422 } else if ip_addr.0[8..14] == iphc::MAC_BASE[0..6] {
423 buf[1] |= if is_src {
425 iphc::SAM_MODE2
426 } else {
427 iphc::DAM_MODE2
428 };
429 buf[*written..*written + 2].copy_from_slice(&ip_addr.0[14..16]);
430 *written += 2;
431 } else {
432 buf[1] |= if is_src {
434 iphc::SAM_MODE1
435 } else {
436 iphc::DAM_MODE1
437 };
438 buf[*written..*written + 8].copy_from_slice(&ip_addr.0[8..16]);
439 *written += 8;
440 }
441}
442
443fn compress_dst(
448 dst_ip_addr: &IPAddr,
449 dst_mac_addr: &MacAddress,
450 dst_ctx: Option<&Context>,
451 buf: &mut [u8],
452 written: &mut usize,
453) {
454 if dst_ip_addr.is_unicast_link_local() {
456 compress_iid(dst_ip_addr, dst_mac_addr, false, buf, written);
459 } else if dst_ctx.is_some() {
460 buf[1] |= iphc::DAC;
463 compress_iid(dst_ip_addr, dst_mac_addr, false, buf, written);
464 } else {
465 buf[*written..*written + 16].copy_from_slice(&dst_ip_addr.0);
468 *written += 16;
469 }
470}
471
472fn compress_multicast(
474 dst_ip_addr: &IPAddr,
475 dst_ctx: Option<&Context>,
476 buf: &mut [u8],
477 written: &mut usize,
478) {
479 buf[1] |= iphc::MULTICAST;
481 if dst_ctx.is_some() {
482 buf[1] |= iphc::DAC;
484 buf[*written..*written + 2].copy_from_slice(&dst_ip_addr.0[1..3]);
485 buf[*written + 2..*written + 6].copy_from_slice(&dst_ip_addr.0[12..16]);
486 *written += 6;
487 } else {
488 if dst_ip_addr.0[1] == 0x02 && dst_ip_addr.0[2..15].iter().all(|&b| b == 0) {
490 buf[1] |= iphc::DAM_MODE3;
492 buf[*written] = dst_ip_addr.0[15];
493 *written += 1;
494 } else {
495 if !dst_ip_addr.0[2..11].iter().all(|&b| b == 0) {
496 buf[1] |= iphc::DAM_INLINE;
498 buf[*written..*written + 16].copy_from_slice(&dst_ip_addr.0);
499 *written += 16;
500 } else if !dst_ip_addr.0[11..13].iter().all(|&b| b == 0) {
501 buf[1] |= iphc::DAM_MODE1;
503 buf[*written] = dst_ip_addr.0[1];
504 buf[*written + 1..*written + 6].copy_from_slice(&dst_ip_addr.0[11..16]);
505 *written += 6;
506 } else {
507 buf[1] |= iphc::DAM_MODE2;
509 buf[*written] = dst_ip_addr.0[1];
510 buf[*written + 1..*written + 4].copy_from_slice(&dst_ip_addr.0[13..16]);
511 *written += 4;
512 }
513 }
514 }
515}
516
517fn compress_udp_ports(udp_header: &UDPHeader, buf: &mut [u8], written: &mut usize) -> u8 {
518 let src_port = udp_header.get_src_port().to_be();
520 let dst_port = udp_header.get_dst_port().to_be();
521
522 let mut udp_port_nhc = 0;
523 if (src_port & nhc::UDP_4BIT_PORT_MASK) == nhc::UDP_4BIT_PORT
524 && (dst_port & nhc::UDP_4BIT_PORT_MASK) == nhc::UDP_4BIT_PORT
525 {
526 udp_port_nhc |= nhc::UDP_SRC_PORT_FLAG | nhc::UDP_DST_PORT_FLAG;
528 buf[*written] = (((src_port & !nhc::UDP_4BIT_PORT_MASK) << 4)
531 | (dst_port & !nhc::UDP_4BIT_PORT_MASK)) as u8;
532 *written += 1;
533 } else if (src_port & nhc::UDP_8BIT_PORT_MASK) == nhc::UDP_8BIT_PORT {
534 udp_port_nhc |= nhc::UDP_SRC_PORT_FLAG;
536 buf[*written] = (src_port & !nhc::UDP_8BIT_PORT_MASK) as u8;
537 u16_to_network_slice(dst_port.to_be(), &mut buf[*written + 1..*written + 3]);
538 *written += 3;
539 } else if (dst_port & nhc::UDP_8BIT_PORT_MASK) == nhc::UDP_8BIT_PORT {
540 udp_port_nhc |= nhc::UDP_DST_PORT_FLAG;
541 u16_to_network_slice(src_port.to_be(), &mut buf[*written..*written + 2]);
542 buf[*written + 3] = (dst_port & !nhc::UDP_8BIT_PORT_MASK) as u8;
543 *written += 3;
544 } else {
545 buf[*written] = src_port as u8;
546 buf[*written + 1] = (src_port >> 8) as u8;
547 buf[*written + 2] = dst_port as u8;
548 buf[*written + 3] = (dst_port >> 8) as u8;
549 *written += 4;
551 }
552 udp_port_nhc
553}
554
555fn compress_udp_checksum(udp_header: &UDPHeader, buf: &mut [u8], written: &mut usize) -> u8 {
558 let cksum = udp_header.get_cksum().to_be();
560 buf[*written] = cksum as u8;
561 buf[*written + 1] = (cksum >> 8) as u8;
562 *written += 2;
563 0
565}
566
567pub fn decompress(
608 ctx_store: &dyn ContextStore,
609 buf: &[u8],
610 src_mac_addr: MacAddress,
611 dst_mac_addr: MacAddress,
612 out_buf: &mut [u8],
613 dgram_size: u16,
614 is_fragment: bool,
615) -> Result<(usize, usize), ()> {
616 let iphc_header_1: u8 = buf[0];
618 let iphc_header_2: u8 = buf[1];
619 let mut consumed: usize = 2;
620
621 let mut ip6_header = IP6Header::new();
622 let mut written: usize = mem::size_of::<IP6Header>();
623
624 let (src_ctx, dst_ctx) = decompress_cie(ctx_store, iphc_header_1, buf, &mut consumed)?;
626
627 decompress_tf(&mut ip6_header, iphc_header_1, buf, &mut consumed);
629
630 let (mut is_nhc, mut next_header) = decompress_nh(iphc_header_1, buf, &mut consumed);
632
633 decompress_hl(&mut ip6_header, iphc_header_1, buf, &mut consumed)?;
635
636 decompress_src(
638 &mut ip6_header,
639 iphc_header_2,
640 &src_mac_addr,
641 &src_ctx,
642 buf,
643 &mut consumed,
644 )?;
645
646 if (iphc_header_2 & iphc::MULTICAST) != 0 {
648 decompress_multicast(&mut ip6_header, iphc_header_2, &dst_ctx, buf, &mut consumed)?;
649 } else {
650 decompress_dst(
651 &mut ip6_header,
652 iphc_header_2,
653 &dst_mac_addr,
654 &dst_ctx,
655 buf,
656 &mut consumed,
657 )?;
658 }
659
660 if is_nhc {
663 next_header = nhc_to_ip6_nh(buf[consumed])?;
664 }
665 ip6_header.set_next_header(next_header);
666
667 while is_nhc {
671 let nhc_header = buf[consumed];
673 consumed += 1;
674
675 let next_headers: &mut [u8] = &mut out_buf[written..];
677
678 match next_header {
679 ip6_nh::IP6 => {
680 let (encap_consumed, encap_written) = decompress(
681 ctx_store,
682 &buf[consumed..],
683 src_mac_addr,
684 dst_mac_addr,
685 next_headers,
686 dgram_size,
687 is_fragment,
688 )?;
689 consumed += encap_consumed;
690 written += encap_written;
691 break;
692 }
693 ip6_nh::UDP => {
694 let mut udp_length = if is_fragment {
697 dgram_size - written as u16
698 } else {
699 buf.len() as u16 - consumed as u16
700 };
701
702 let consumed_before_port_decompress = consumed;
704 let (src_port, dst_port) = decompress_udp_ports(nhc_header, buf, &mut consumed);
705
706 if !is_fragment {
709 udp_length +=
711 nhc::UDP_PORTS_SIZE - ((consumed - consumed_before_port_decompress) as u16);
712 udp_length += 2;
715 if (nhc_header & nhc::UDP_CHECKSUM_FLAG) != 0 {
717 udp_length += 2;
718 }
719 }
720
721 u16_to_network_slice(src_port.to_be(), &mut next_headers[0..2]);
727 u16_to_network_slice(dst_port.to_be(), &mut next_headers[2..4]);
728 u16_to_network_slice(udp_length, &mut next_headers[4..6]);
729 let udp_checksum = decompress_udp_checksum(
731 nhc_header,
732 &next_headers[0..8],
733 udp_length,
734 &ip6_header,
735 buf,
736 &mut consumed,
737 is_fragment,
738 );
739 u16_to_network_slice(udp_checksum.to_be(), &mut next_headers[6..8]);
740
741 written += 8;
742 break;
743 }
744 ip6_nh::FRAGMENT
745 | ip6_nh::HOP_OPTS
746 | ip6_nh::ROUTING
747 | ip6_nh::DST_OPTS
748 | ip6_nh::MOBILITY => {
749 is_nhc = (nhc_header & nhc::NH) != 0;
751
752 let len = buf[consumed] as usize;
754 consumed += 1;
755
756 if consumed + len >= buf.len() {
760 return Err(());
761 }
762
763 let mut hdr_len_field = (len - 6) / 8;
766 if (len - 6) % 8 != 0 {
767 hdr_len_field += 1;
768 }
769
770 next_header = if is_nhc {
774 nhc_to_ip6_nh(buf[consumed + len])?
776 } else {
777 buf[consumed + len]
779 };
780
781 next_headers[0] = next_header;
783 next_headers[1] = hdr_len_field as u8;
784 next_headers[2..2 + len].copy_from_slice(&buf[consumed..consumed + len]);
786
787 let pad_bytes = hdr_len_field * 8 - len + 6;
789 if pad_bytes == 1 {
790 next_headers[2 + len] = 0;
792 } else {
793 next_headers[2 + len] = 1;
795 next_headers[2 + len + 1] = pad_bytes as u8 - 2;
796 for i in 2..pad_bytes {
797 next_headers[2 + len + i] = 0;
798 }
799 }
800
801 written += 8 + hdr_len_field * 8;
802 consumed += len;
803 }
804 _ => panic!("Unreachable case"),
805 }
806 }
807
808 let payload_len = if is_fragment {
812 (dgram_size as usize) - mem::size_of::<IP6Header>()
813 } else {
814 written + (buf.len() - consumed) - mem::size_of::<IP6Header>()
815 };
816 ip6_header.payload_len = (payload_len as u16).to_be();
817 IP6Header::encode(&ip6_header, out_buf).done().ok_or(())?;
818 Ok((consumed, written))
819}
820
821fn decompress_cie(
822 ctx_store: &dyn ContextStore,
823 iphc_header: u8,
824 buf: &[u8],
825 consumed: &mut usize,
826) -> Result<(Context, Context), ()> {
827 let ctx_0 = ctx_store.get_context_0();
828 let (mut src_ctx, mut dst_ctx) = (ctx_0, ctx_0);
829 if iphc_header & iphc::CID != 0 {
830 let sci = buf[*consumed] >> 4;
831 let dci = buf[*consumed] & 0xf;
832 *consumed += 1;
833
834 if sci != 0 {
835 src_ctx = ctx_store.get_context_from_id(sci).ok_or(())?;
836 }
837 if dci != 0 {
838 dst_ctx = ctx_store.get_context_from_id(dci).ok_or(())?;
839 }
840 }
841 Ok((src_ctx, dst_ctx))
842}
843
844fn decompress_tf(ip6_header: &mut IP6Header, iphc_header: u8, buf: &[u8], consumed: &mut usize) {
845 let fl_compressed = (iphc_header & iphc::TF_FLOW_LABEL) != 0;
846 let tc_compressed = (iphc_header & iphc::TF_TRAFFIC_CLASS) != 0;
847
848 if !fl_compressed || !tc_compressed {
851 let ecn = buf[*consumed] >> 6;
852 ip6_header.set_ecn(ecn);
853 }
854 if !tc_compressed {
855 let dscp = buf[*consumed] & 0b111111;
856 ip6_header.set_dscp(dscp);
857 *consumed += 1;
858 }
859
860 if fl_compressed {
863 ip6_header.set_flow_label(0);
864 } else {
865 let flow = (((buf[*consumed] & 0x0f) as u32) << 16)
866 | ((buf[*consumed + 1] as u32) << 8)
867 | (buf[*consumed + 2] as u32);
868 *consumed += 3;
869 ip6_header.set_flow_label(flow);
870 }
871}
872
873fn decompress_nh(iphc_header: u8, buf: &[u8], consumed: &mut usize) -> (bool, u8) {
874 let is_nhc = (iphc_header & iphc::NH) != 0;
875 let mut next_header: u8 = 0;
876 if !is_nhc {
877 next_header = buf[*consumed];
878 *consumed += 1;
879 }
880 (is_nhc, next_header)
881}
882
883fn decompress_hl(
884 ip6_header: &mut IP6Header,
885 iphc_header: u8,
886 buf: &[u8],
887 consumed: &mut usize,
888) -> Result<(), ()> {
889 let hop_limit = match iphc_header & iphc::HLIM_MASK {
890 iphc::HLIM_1 => 1,
891 iphc::HLIM_64 => 64,
892 iphc::HLIM_255 => 255,
893 iphc::HLIM_INLINE => {
894 let hl = buf[*consumed];
895 *consumed += 1;
896 hl
897 }
898 _ => panic!("Unreachable case"),
899 };
900 ip6_header.set_hop_limit(hop_limit);
901 Ok(())
902}
903
904fn decompress_src(
905 ip6_header: &mut IP6Header,
906 iphc_header: u8,
907 mac_addr: &MacAddress,
908 ctx: &Context,
909 buf: &[u8],
910 consumed: &mut usize,
911) -> Result<(), ()> {
912 let uses_context = (iphc_header & iphc::SAC) != 0;
913 let sam_mode = iphc_header & iphc::SAM_MASK;
914 if uses_context && sam_mode == iphc::SAM_INLINE {
915 } else if uses_context {
917 decompress_iid_context(
919 sam_mode,
920 &mut ip6_header.src_addr,
921 mac_addr,
922 ctx,
923 buf,
924 consumed,
925 )?;
926 } else {
927 decompress_iid_link_local(sam_mode, &mut ip6_header.src_addr, mac_addr, buf, consumed)?;
929 }
930 Ok(())
931}
932
933fn decompress_dst(
934 ip6_header: &mut IP6Header,
935 iphc_header: u8,
936 mac_addr: &MacAddress,
937 ctx: &Context,
938 buf: &[u8],
939 consumed: &mut usize,
940) -> Result<(), ()> {
941 let uses_context = (iphc_header & iphc::DAC) != 0;
942 let dam_mode = iphc_header & iphc::DAM_MASK;
943 if uses_context && dam_mode == iphc::DAM_INLINE {
944 return Err(());
946 } else if uses_context {
947 decompress_iid_context(
949 dam_mode,
950 &mut ip6_header.dst_addr,
951 mac_addr,
952 ctx,
953 buf,
954 consumed,
955 )?;
956 } else {
957 decompress_iid_link_local(dam_mode, &mut ip6_header.dst_addr, mac_addr, buf, consumed)?;
959 }
960 Ok(())
961}
962
963fn decompress_multicast(
964 ip6_header: &mut IP6Header,
965 iphc_header: u8,
966 ctx: &Context,
967 buf: &[u8],
968 consumed: &mut usize,
969) -> Result<(), ()> {
970 let uses_context = (iphc_header & iphc::DAC) != 0;
971 let dam_mode = iphc_header & iphc::DAM_MASK;
972 let ip_addr: &mut IPAddr = &mut ip6_header.dst_addr;
973 if uses_context {
974 match dam_mode {
975 iphc::DAM_INLINE => {
976 let prefix_bytes = ctx.prefix_len.div_ceil(8) as usize;
979 if prefix_bytes > 8 {
980 return Err(());
984 }
985 ip_addr.0[0] = 0xff;
986 ip_addr.0[1] = buf[*consumed];
987 ip_addr.0[2] = buf[*consumed + 1];
988 ip_addr.0[3] = ctx.prefix_len;
989 ip_addr.0[4..4 + prefix_bytes].copy_from_slice(&ctx.prefix[0..prefix_bytes]);
990 ip_addr.0[12..16].copy_from_slice(&buf[*consumed + 2..*consumed + 6]);
991 *consumed += 6;
992 }
993 _ => {
994 return Err(());
996 }
997 }
998 } else {
999 match dam_mode {
1000 iphc::DAM_INLINE => {
1002 ip_addr.0.copy_from_slice(&buf[*consumed..*consumed + 16]);
1003 *consumed += 16;
1004 }
1005 iphc::DAM_MODE1 => {
1008 ip_addr.0[0] = 0xff;
1009 ip_addr.0[1] = buf[*consumed];
1010 *consumed += 1;
1011 ip_addr.0[11..16].copy_from_slice(&buf[*consumed..*consumed + 5]);
1012 *consumed += 5;
1013 }
1014 iphc::DAM_MODE2 => {
1017 ip_addr.0[0] = 0xff;
1018 ip_addr.0[1] = buf[*consumed];
1019 *consumed += 1;
1020 ip_addr.0[13..16].copy_from_slice(&buf[*consumed..*consumed + 3]);
1021 *consumed += 3;
1022 }
1023 iphc::DAM_MODE3 => {
1026 ip_addr.0[0] = 0xff;
1027 ip_addr.0[1] = 0x02;
1028 ip_addr.0[15] = buf[*consumed];
1029 *consumed += 1;
1030 }
1031 _ => panic!("Unreachable case"),
1032 }
1033 }
1034 Ok(())
1035}
1036
1037fn decompress_iid_link_local(
1038 addr_mode: u8,
1039 ip_addr: &mut IPAddr,
1040 mac_addr: &MacAddress,
1041 buf: &[u8],
1042 consumed: &mut usize,
1043) -> Result<(), ()> {
1044 let mode = addr_mode & (iphc::SAM_MASK | iphc::DAM_MASK);
1045 match mode {
1046 iphc::SAM_INLINE => {
1048 ip_addr.0.copy_from_slice(&buf[*consumed..*consumed + 16]);
1050 *consumed += 16;
1051 }
1052 iphc::SAM_MODE1 | iphc::DAM_MODE1 => {
1055 ip_addr.set_unicast_link_local();
1056 ip_addr.0[8..16].copy_from_slice(&buf[*consumed..*consumed + 8]);
1057 *consumed += 8;
1058 }
1059 iphc::SAM_MODE2 | iphc::DAM_MODE2 => {
1062 ip_addr.set_unicast_link_local();
1063 ip_addr.0[11..13].copy_from_slice(&iphc::MAC_BASE[3..5]);
1064 ip_addr.0[14..16].copy_from_slice(&buf[*consumed..*consumed + 2]);
1065 *consumed += 2;
1066 }
1067 iphc::SAM_MODE3 | iphc::DAM_MODE3 => {
1070 ip_addr.set_unicast_link_local();
1071 ip_addr.0[8..16].copy_from_slice(&compute_iid(mac_addr));
1072 }
1073 _ => panic!("Unreachable case"),
1074 }
1075 Ok(())
1076}
1077
1078fn decompress_iid_context(
1079 addr_mode: u8,
1080 ip_addr: &mut IPAddr,
1081 mac_addr: &MacAddress,
1082 ctx: &Context,
1083 buf: &[u8],
1084 consumed: &mut usize,
1085) -> Result<(), ()> {
1086 let mode = addr_mode & (iphc::SAM_MASK | iphc::DAM_MASK);
1087 match mode {
1088 iphc::DAM_INLINE => {
1091 return Err(());
1092 }
1093 iphc::SAM_MODE1 | iphc::DAM_MODE1 => {
1096 ip_addr.0[8..16].copy_from_slice(&buf[*consumed..*consumed + 8]);
1097 *consumed += 8;
1098 }
1099 iphc::SAM_MODE2 | iphc::DAM_MODE2 => {
1102 ip_addr.0[8..16].copy_from_slice(&iphc::MAC_BASE);
1103 ip_addr.0[14..16].copy_from_slice(&buf[*consumed..*consumed + 2]);
1104 *consumed += 2;
1105 }
1106 iphc::SAM_MODE3 | iphc::DAM_MODE3 => {
1109 let iid = compute_iid(mac_addr);
1110 ip_addr.0[8..16].copy_from_slice(&iid[0..8]);
1111 }
1112 _ => panic!("Unreachable case"),
1113 }
1114 ip_addr.set_prefix(&ctx.prefix, ctx.prefix_len);
1117 Ok(())
1118}
1119
1120fn decompress_udp_ports(udp_nhc: u8, buf: &[u8], consumed: &mut usize) -> (u16, u16) {
1122 let src_compressed = (udp_nhc & nhc::UDP_SRC_PORT_FLAG) != 0;
1123 let dst_compressed = (udp_nhc & nhc::UDP_DST_PORT_FLAG) != 0;
1124
1125 let src_port;
1126 let dst_port;
1127 if src_compressed && dst_compressed {
1128 let src_short = ((buf[*consumed] >> 4) & 0xf) as u16;
1130 let dst_short = (buf[*consumed] & 0xf) as u16;
1131 src_port = nhc::UDP_4BIT_PORT | src_short;
1132 dst_port = nhc::UDP_4BIT_PORT | dst_short;
1133 *consumed += 1;
1134 } else if src_compressed {
1135 src_port = nhc::UDP_8BIT_PORT | (buf[*consumed] as u16);
1137 dst_port = u16::from_be(network_slice_to_u16(&buf[*consumed + 1..*consumed + 3]));
1139 *consumed += 3;
1140 } else if dst_compressed {
1141 src_port = u16::from_be(network_slice_to_u16(&buf[*consumed..*consumed + 2]));
1143 dst_port = nhc::UDP_8BIT_PORT | (buf[*consumed + 2] as u16);
1145 *consumed += 3;
1146 } else {
1147 src_port = u16::from_be(network_slice_to_u16(&buf[*consumed..*consumed + 2]));
1149 dst_port = u16::from_be(network_slice_to_u16(&buf[*consumed + 2..*consumed + 4]));
1150 *consumed += 4;
1151 }
1152 (src_port, dst_port)
1153}
1154
1155fn decompress_udp_checksum(
1157 udp_nhc: u8,
1158 udp_header: &[u8],
1159 udp_length: u16,
1160 ip6_header: &IP6Header,
1161 buf: &[u8],
1162 consumed: &mut usize,
1163 is_fragment: bool,
1164) -> u16 {
1165 if (udp_nhc & nhc::UDP_CHECKSUM_FLAG) != 0 && !is_fragment {
1171 let mut udp_header_copy: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0];
1172 udp_header_copy.copy_from_slice(udp_header);
1173 match UDPHeader::decode(&udp_header_copy).done() {
1174 Some((_offset, hdr)) => u16::from_be(compute_udp_checksum(
1175 ip6_header,
1176 &hdr,
1177 udp_length,
1178 &buf[*consumed..],
1179 )),
1180 None => 0, }
1182 } else {
1183 let checksum = u16::from_be(network_slice_to_u16(&buf[*consumed..*consumed + 2]));
1184 *consumed += 2;
1185 checksum
1186 }
1187}