smoltcp/wire/sixlowpan/
iphc.rs

1//! Implementation of IP Header Compression from [RFC 6282 § 3.1].
2//! It defines the compression of IPv6 headers.
3//!
4//! [RFC 6282 § 3.1]: https://datatracker.ietf.org/doc/html/rfc6282#section-3.1
5
6use super::{
7    AddressContext, AddressMode, Error, NextHeader, Result, UnresolvedAddress, DISPATCH_IPHC_HEADER,
8};
9use crate::wire::{ieee802154::Address as LlAddress, ipv6, ipv6::AddressExt, IpProtocol};
10use byteorder::{ByteOrder, NetworkEndian};
11
12mod field {
13    use crate::wire::field::*;
14
15    pub const IPHC_FIELD: Field = 0..2;
16}
17
18macro_rules! get_field {
19    ($name:ident, $mask:expr, $shift:expr) => {
20        fn $name(&self) -> u8 {
21            let data = self.buffer.as_ref();
22            let raw = NetworkEndian::read_u16(&data[field::IPHC_FIELD]);
23            ((raw >> $shift) & $mask) as u8
24        }
25    };
26}
27
28macro_rules! set_field {
29    ($name:ident, $mask:expr, $shift:expr) => {
30        fn $name(&mut self, val: u8) {
31            let data = &mut self.buffer.as_mut()[field::IPHC_FIELD];
32            let mut raw = NetworkEndian::read_u16(data);
33
34            raw = (raw & !($mask << $shift)) | ((val as u16) << $shift);
35            NetworkEndian::write_u16(data, raw);
36        }
37    };
38}
39
40/// A read/write wrapper around a 6LoWPAN IPHC header.
41/// [RFC 6282 § 3.1] specifies the format of the header.
42///
43/// The header always start with the following base format (from [RFC 6282 § 3.1.1]):
44/// ```txt
45///    0                                       1
46///    0   1   2   3   4   5   6   7   8   9   0   1   2   3   4   5
47///  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
48///  | 0 | 1 | 1 |  TF   |NH | HLIM  |CID|SAC|  SAM  | M |DAC|  DAM  |
49///  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
50/// ```
51/// With:
52/// - TF: Traffic Class and Flow Label
53/// - NH: Next Header
54/// - HLIM: Hop Limit
55/// - CID: Context Identifier Extension
56/// - SAC: Source Address Compression
57/// - SAM: Source Address Mode
58/// - M: Multicast Compression
59/// - DAC: Destination Address Compression
60/// - DAM: Destination Address Mode
61///
62/// Depending on the flags in the base format, the following fields are added to the header:
63/// - Traffic Class and Flow Label
64/// - Next Header
65/// - Hop Limit
66/// - IPv6 source address
67/// - IPv6 destination address
68///
69/// [RFC 6282 § 3.1]: https://datatracker.ietf.org/doc/html/rfc6282#section-3.1
70/// [RFC 6282 § 3.1.1]: https://datatracker.ietf.org/doc/html/rfc6282#section-3.1.1
71#[derive(Debug, Clone)]
72#[cfg_attr(feature = "defmt", derive(defmt::Format))]
73pub struct Packet<T: AsRef<[u8]>> {
74    buffer: T,
75}
76
77impl<T: AsRef<[u8]>> Packet<T> {
78    /// Input a raw octet buffer with a 6LoWPAN IPHC header structure.
79    pub const fn new_unchecked(buffer: T) -> Self {
80        Packet { buffer }
81    }
82
83    /// Shorthand for a combination of [new_unchecked] and [check_len].
84    ///
85    /// [new_unchecked]: #method.new_unchecked
86    /// [check_len]: #method.check_len
87    pub fn new_checked(buffer: T) -> Result<Self> {
88        let packet = Self::new_unchecked(buffer);
89        packet.check_len()?;
90        Ok(packet)
91    }
92
93    /// Ensure that no accessor method will panic if called.
94    /// Returns `Err(Error)` if the buffer is too short.
95    pub fn check_len(&self) -> Result<()> {
96        let buffer = self.buffer.as_ref();
97        if buffer.len() < 2 {
98            return Err(Error);
99        }
100
101        let mut offset = self.ip_fields_start()
102            + self.traffic_class_size()
103            + self.next_header_size()
104            + self.hop_limit_size();
105        offset += self.src_address_size();
106        offset += self.dst_address_size();
107
108        if offset as usize > buffer.len() {
109            return Err(Error);
110        }
111
112        Ok(())
113    }
114
115    /// Consumes the frame, returning the underlying buffer.
116    pub fn into_inner(self) -> T {
117        self.buffer
118    }
119
120    /// Return the Next Header field.
121    pub fn next_header(&self) -> NextHeader {
122        let nh = self.nh_field();
123
124        if nh == 1 {
125            // The next header field is compressed.
126            // It is also encoded using LOWPAN_NHC.
127            NextHeader::Compressed
128        } else {
129            // The full 8 bits for Next Header are carried in-line.
130            let start = (self.ip_fields_start() + self.traffic_class_size()) as usize;
131
132            let data = self.buffer.as_ref();
133            let nh = data[start..start + 1][0];
134            NextHeader::Uncompressed(IpProtocol::from(nh))
135        }
136    }
137
138    /// Return the Hop Limit.
139    pub fn hop_limit(&self) -> u8 {
140        match self.hlim_field() {
141            0b00 => {
142                let start = (self.ip_fields_start()
143                    + self.traffic_class_size()
144                    + self.next_header_size()) as usize;
145
146                let data = self.buffer.as_ref();
147                data[start..start + 1][0]
148            }
149            0b01 => 1,
150            0b10 => 64,
151            0b11 => 255,
152            _ => unreachable!(),
153        }
154    }
155
156    /// Return the Source Context Identifier.
157    pub fn src_context_id(&self) -> Option<u8> {
158        if self.cid_field() == 1 {
159            let data = self.buffer.as_ref();
160            Some(data[2] >> 4)
161        } else {
162            None
163        }
164    }
165
166    /// Return the Destination Context Identifier.
167    pub fn dst_context_id(&self) -> Option<u8> {
168        if self.cid_field() == 1 {
169            let data = self.buffer.as_ref();
170            Some(data[2] & 0x0f)
171        } else {
172            None
173        }
174    }
175
176    /// Return the ECN field (when it is inlined).
177    pub fn ecn_field(&self) -> Option<u8> {
178        match self.tf_field() {
179            0b00..=0b10 => {
180                let start = self.ip_fields_start() as usize;
181                Some(self.buffer.as_ref()[start..][0] & 0b1100_0000)
182            }
183            0b11 => None,
184            _ => unreachable!(),
185        }
186    }
187
188    /// Return the DSCP field (when it is inlined).
189    pub fn dscp_field(&self) -> Option<u8> {
190        match self.tf_field() {
191            0b00 | 0b10 => {
192                let start = self.ip_fields_start() as usize;
193                Some(self.buffer.as_ref()[start..][0] & 0b111111)
194            }
195            0b01 | 0b11 => None,
196            _ => unreachable!(),
197        }
198    }
199
200    /// Return the flow label field (when it is inlined).
201    pub fn flow_label_field(&self) -> Option<u16> {
202        match self.tf_field() {
203            0b00 => {
204                let start = self.ip_fields_start() as usize;
205                Some(NetworkEndian::read_u16(
206                    &self.buffer.as_ref()[start..][2..4],
207                ))
208            }
209            0b01 => {
210                let start = self.ip_fields_start() as usize;
211                Some(NetworkEndian::read_u16(
212                    &self.buffer.as_ref()[start..][1..3],
213                ))
214            }
215            0b10 | 0b11 => None,
216            _ => unreachable!(),
217        }
218    }
219
220    /// Return the Source Address.
221    pub fn src_addr(&self) -> Result<UnresolvedAddress> {
222        let start = (self.ip_fields_start()
223            + self.traffic_class_size()
224            + self.next_header_size()
225            + self.hop_limit_size()) as usize;
226
227        let data = self.buffer.as_ref();
228        match (self.sac_field(), self.sam_field()) {
229            (0, 0b00) => Ok(UnresolvedAddress::WithoutContext(AddressMode::FullInline(
230                &data[start..][..16],
231            ))),
232            (0, 0b01) => Ok(UnresolvedAddress::WithoutContext(
233                AddressMode::InLine64bits(&data[start..][..8]),
234            )),
235            (0, 0b10) => Ok(UnresolvedAddress::WithoutContext(
236                AddressMode::InLine16bits(&data[start..][..2]),
237            )),
238            (0, 0b11) => Ok(UnresolvedAddress::WithoutContext(AddressMode::FullyElided)),
239            (1, 0b00) => Ok(UnresolvedAddress::WithContext((
240                0,
241                AddressMode::Unspecified,
242            ))),
243            (1, 0b01) => {
244                if let Some(id) = self.src_context_id() {
245                    Ok(UnresolvedAddress::WithContext((
246                        id as usize,
247                        AddressMode::InLine64bits(&data[start..][..8]),
248                    )))
249                } else {
250                    Err(Error)
251                }
252            }
253            (1, 0b10) => {
254                if let Some(id) = self.src_context_id() {
255                    Ok(UnresolvedAddress::WithContext((
256                        id as usize,
257                        AddressMode::InLine16bits(&data[start..][..2]),
258                    )))
259                } else {
260                    Err(Error)
261                }
262            }
263            (1, 0b11) => {
264                if let Some(id) = self.src_context_id() {
265                    Ok(UnresolvedAddress::WithContext((
266                        id as usize,
267                        AddressMode::FullyElided,
268                    )))
269                } else {
270                    Err(Error)
271                }
272            }
273            _ => Err(Error),
274        }
275    }
276
277    /// Return the Destination Address.
278    pub fn dst_addr(&self) -> Result<UnresolvedAddress> {
279        let start = (self.ip_fields_start()
280            + self.traffic_class_size()
281            + self.next_header_size()
282            + self.hop_limit_size()
283            + self.src_address_size()) as usize;
284
285        let data = self.buffer.as_ref();
286        match (self.m_field(), self.dac_field(), self.dam_field()) {
287            (0, 0, 0b00) => Ok(UnresolvedAddress::WithoutContext(AddressMode::FullInline(
288                &data[start..][..16],
289            ))),
290            (0, 0, 0b01) => Ok(UnresolvedAddress::WithoutContext(
291                AddressMode::InLine64bits(&data[start..][..8]),
292            )),
293            (0, 0, 0b10) => Ok(UnresolvedAddress::WithoutContext(
294                AddressMode::InLine16bits(&data[start..][..2]),
295            )),
296            (0, 0, 0b11) => Ok(UnresolvedAddress::WithoutContext(AddressMode::FullyElided)),
297            (0, 1, 0b00) => Ok(UnresolvedAddress::Reserved),
298            (0, 1, 0b01) => {
299                if let Some(id) = self.dst_context_id() {
300                    Ok(UnresolvedAddress::WithContext((
301                        id as usize,
302                        AddressMode::InLine64bits(&data[start..][..8]),
303                    )))
304                } else {
305                    Err(Error)
306                }
307            }
308            (0, 1, 0b10) => {
309                if let Some(id) = self.dst_context_id() {
310                    Ok(UnresolvedAddress::WithContext((
311                        id as usize,
312                        AddressMode::InLine16bits(&data[start..][..2]),
313                    )))
314                } else {
315                    Err(Error)
316                }
317            }
318            (0, 1, 0b11) => {
319                if let Some(id) = self.dst_context_id() {
320                    Ok(UnresolvedAddress::WithContext((
321                        id as usize,
322                        AddressMode::FullyElided,
323                    )))
324                } else {
325                    Err(Error)
326                }
327            }
328            (1, 0, 0b00) => Ok(UnresolvedAddress::WithoutContext(AddressMode::FullInline(
329                &data[start..][..16],
330            ))),
331            (1, 0, 0b01) => Ok(UnresolvedAddress::WithoutContext(
332                AddressMode::Multicast48bits(&data[start..][..6]),
333            )),
334            (1, 0, 0b10) => Ok(UnresolvedAddress::WithoutContext(
335                AddressMode::Multicast32bits(&data[start..][..4]),
336            )),
337            (1, 0, 0b11) => Ok(UnresolvedAddress::WithoutContext(
338                AddressMode::Multicast8bits(&data[start..][..1]),
339            )),
340            (1, 1, 0b00) => Ok(UnresolvedAddress::WithContext((
341                0,
342                AddressMode::NotSupported,
343            ))),
344            (1, 1, 0b01..=0b11) => Ok(UnresolvedAddress::Reserved),
345            _ => Err(Error),
346        }
347    }
348
349    get_field!(dispatch_field, 0b111, 13);
350    get_field!(tf_field, 0b11, 11);
351    get_field!(nh_field, 0b1, 10);
352    get_field!(hlim_field, 0b11, 8);
353    get_field!(cid_field, 0b1, 7);
354    get_field!(sac_field, 0b1, 6);
355    get_field!(sam_field, 0b11, 4);
356    get_field!(m_field, 0b1, 3);
357    get_field!(dac_field, 0b1, 2);
358    get_field!(dam_field, 0b11, 0);
359
360    /// Return the start for the IP fields.
361    fn ip_fields_start(&self) -> u8 {
362        2 + self.cid_size()
363    }
364
365    /// Get the size in octets of the traffic class field.
366    fn traffic_class_size(&self) -> u8 {
367        match self.tf_field() {
368            0b00 => 4,
369            0b01 => 3,
370            0b10 => 1,
371            0b11 => 0,
372            _ => unreachable!(),
373        }
374    }
375
376    /// Get the size in octets of the next header field.
377    fn next_header_size(&self) -> u8 {
378        (self.nh_field() != 1) as u8
379    }
380
381    /// Get the size in octets of the hop limit field.
382    fn hop_limit_size(&self) -> u8 {
383        (self.hlim_field() == 0b00) as u8
384    }
385
386    /// Get the size in octets of the CID field.
387    fn cid_size(&self) -> u8 {
388        (self.cid_field() == 1) as u8
389    }
390
391    /// Get the size in octets of the source address.
392    fn src_address_size(&self) -> u8 {
393        match (self.sac_field(), self.sam_field()) {
394            (0, 0b00) => 16, // The full address is carried in-line.
395            (0, 0b01) => 8,  // The first 64 bits are elided.
396            (0, 0b10) => 2,  // The first 112 bits are elided.
397            (0, 0b11) => 0,  // The address is fully elided.
398            (1, 0b00) => 0,  // The UNSPECIFIED address.
399            (1, 0b01) => 8,  // Address derived using context information.
400            (1, 0b10) => 2,  // Address derived using context information.
401            (1, 0b11) => 0,  // Address derived using context information.
402            _ => unreachable!(),
403        }
404    }
405
406    /// Get the size in octets of the address address.
407    fn dst_address_size(&self) -> u8 {
408        match (self.m_field(), self.dac_field(), self.dam_field()) {
409            (0, 0, 0b00) => 16, // The full address is carried in-line.
410            (0, 0, 0b01) => 8,  // The first 64 bits are elided.
411            (0, 0, 0b10) => 2,  // The first 112 bits are elided.
412            (0, 0, 0b11) => 0,  // The address is fully elided.
413            (0, 1, 0b00) => 0,  // Reserved.
414            (0, 1, 0b01) => 8,  // Address derived using context information.
415            (0, 1, 0b10) => 2,  // Address derived using context information.
416            (0, 1, 0b11) => 0,  // Address derived using context information.
417            (1, 0, 0b00) => 16, // The full address is carried in-line.
418            (1, 0, 0b01) => 6,  // The address takes the form ffXX::00XX:XXXX:XXXX.
419            (1, 0, 0b10) => 4,  // The address takes the form ffXX::00XX:XXXX.
420            (1, 0, 0b11) => 1,  // The address takes the form ff02::00XX.
421            (1, 1, 0b00) => 6,  // Match Unicast-Prefix-based IPv6.
422            (1, 1, 0b01) => 0,  // Reserved.
423            (1, 1, 0b10) => 0,  // Reserved.
424            (1, 1, 0b11) => 0,  // Reserved.
425            _ => unreachable!(),
426        }
427    }
428
429    /// Return the length of the header.
430    pub fn header_len(&self) -> usize {
431        let mut len = self.ip_fields_start();
432        len += self.traffic_class_size();
433        len += self.next_header_size();
434        len += self.hop_limit_size();
435        len += self.src_address_size();
436        len += self.dst_address_size();
437
438        len as usize
439    }
440}
441
442impl<'a, T: AsRef<[u8]> + ?Sized> Packet<&'a T> {
443    /// Return a pointer to the payload.
444    pub fn payload(&self) -> &'a [u8] {
445        let mut len = self.ip_fields_start();
446        len += self.traffic_class_size();
447        len += self.next_header_size();
448        len += self.hop_limit_size();
449        len += self.src_address_size();
450        len += self.dst_address_size();
451
452        let len = len as usize;
453
454        let data = self.buffer.as_ref();
455        &data[len..]
456    }
457}
458
459impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
460    /// Set the dispatch field to `0b011`.
461    fn set_dispatch_field(&mut self) {
462        let data = &mut self.buffer.as_mut()[field::IPHC_FIELD];
463        let mut raw = NetworkEndian::read_u16(data);
464
465        raw = (raw & !(0b111 << 13)) | (0b11 << 13);
466        NetworkEndian::write_u16(data, raw);
467    }
468
469    set_field!(set_tf_field, 0b11, 11);
470    set_field!(set_nh_field, 0b1, 10);
471    set_field!(set_hlim_field, 0b11, 8);
472    set_field!(set_cid_field, 0b1, 7);
473    set_field!(set_sac_field, 0b1, 6);
474    set_field!(set_sam_field, 0b11, 4);
475    set_field!(set_m_field, 0b1, 3);
476    set_field!(set_dac_field, 0b1, 2);
477    set_field!(set_dam_field, 0b11, 0);
478
479    fn set_field(&mut self, idx: usize, value: &[u8]) {
480        let raw = self.buffer.as_mut();
481        raw[idx..idx + value.len()].copy_from_slice(value);
482    }
483
484    /// Set the Next Header.
485    ///
486    /// **NOTE**: `idx` is the offset at which the Next Header needs to be written to.
487    fn set_next_header(&mut self, nh: NextHeader, mut idx: usize) -> usize {
488        match nh {
489            NextHeader::Uncompressed(nh) => {
490                self.set_nh_field(0);
491                self.set_field(idx, &[nh.into()]);
492                idx += 1;
493            }
494            NextHeader::Compressed => self.set_nh_field(1),
495        }
496
497        idx
498    }
499
500    /// Set the Hop Limit.
501    ///
502    /// **NOTE**: `idx` is the offset at which the Next Header needs to be written to.
503    fn set_hop_limit(&mut self, hl: u8, mut idx: usize) -> usize {
504        match hl {
505            255 => self.set_hlim_field(0b11),
506            64 => self.set_hlim_field(0b10),
507            1 => self.set_hlim_field(0b01),
508            _ => {
509                self.set_hlim_field(0b00);
510                self.set_field(idx, &[hl]);
511                idx += 1;
512            }
513        }
514
515        idx
516    }
517
518    /// Set the Source Address based on the IPv6 address and the Link-Local address.
519    ///
520    /// **NOTE**: `idx` is the offset at which the Next Header needs to be written to.
521    fn set_src_address(
522        &mut self,
523        src_addr: ipv6::Address,
524        ll_src_addr: Option<LlAddress>,
525        mut idx: usize,
526    ) -> usize {
527        self.set_cid_field(0);
528        self.set_sac_field(0);
529        let src = src_addr.octets();
530        if src_addr == ipv6::Address::UNSPECIFIED {
531            self.set_sac_field(1);
532            self.set_sam_field(0b00);
533        } else if src_addr.is_link_local() {
534            // We have a link local address.
535            // The remainder of the address can be elided when the context contains
536            // a 802.15.4 short address or a 802.15.4 extended address which can be
537            // converted to a eui64 address.
538            let is_eui_64 = ll_src_addr
539                .map(|addr| {
540                    addr.as_eui_64()
541                        .map(|addr| addr[..] == src[8..])
542                        .unwrap_or(false)
543                })
544                .unwrap_or(false);
545
546            if src[8..14] == [0, 0, 0, 0xff, 0xfe, 0] {
547                let ll = [src[14], src[15]];
548
549                if ll_src_addr == Some(LlAddress::Short(ll)) {
550                    // We have the context from the 802.15.4 frame.
551                    // The context contains the short address.
552                    // We can elide the source address.
553                    self.set_sam_field(0b11);
554                } else {
555                    // We don't have the context from the 802.15.4 frame.
556                    // We cannot elide the source address, however we can elide 112 bits.
557                    self.set_sam_field(0b10);
558
559                    self.set_field(idx, &src[14..]);
560                    idx += 2;
561                }
562            } else if is_eui_64 {
563                // We have the context from the 802.15.4 frame.
564                // The context contains the extended address.
565                // We can elide the source address.
566                self.set_sam_field(0b11);
567            } else {
568                // We cannot elide the source address, however we can elide 64 bits.
569                self.set_sam_field(0b01);
570
571                self.set_field(idx, &src[8..]);
572                idx += 8;
573            }
574        } else {
575            // We cannot elide anything.
576            self.set_sam_field(0b00);
577            self.set_field(idx, &src);
578            idx += 16;
579        }
580
581        idx
582    }
583
584    /// Set the Destination Address based on the IPv6 address and the Link-Local address.
585    ///
586    /// **NOTE**: `idx` is the offset at which the Next Header needs to be written to.
587    fn set_dst_address(
588        &mut self,
589        dst_addr: ipv6::Address,
590        ll_dst_addr: Option<LlAddress>,
591        mut idx: usize,
592    ) -> usize {
593        self.set_dac_field(0);
594        self.set_dam_field(0);
595        self.set_m_field(0);
596        let dst = dst_addr.octets();
597        if dst_addr.is_multicast() {
598            self.set_m_field(1);
599
600            if dst[1] == 0x02 && dst[2..15] == [0; 13] {
601                self.set_dam_field(0b11);
602
603                self.set_field(idx, &[dst[15]]);
604                idx += 1;
605            } else if dst[2..13] == [0; 11] {
606                self.set_dam_field(0b10);
607
608                self.set_field(idx, &[dst[1]]);
609                idx += 1;
610                self.set_field(idx, &dst[13..]);
611                idx += 3;
612            } else if dst[2..11] == [0; 9] {
613                self.set_dam_field(0b01);
614
615                self.set_field(idx, &[dst[1]]);
616                idx += 1;
617                self.set_field(idx, &dst[11..]);
618                idx += 5;
619            } else {
620                self.set_dam_field(0b11);
621
622                self.set_field(idx, &dst);
623                idx += 16;
624            }
625        } else if dst_addr.is_link_local() {
626            let is_eui_64 = ll_dst_addr
627                .map(|addr| {
628                    addr.as_eui_64()
629                        .map(|addr| addr[..] == dst[8..])
630                        .unwrap_or(false)
631                })
632                .unwrap_or(false);
633
634            if dst[8..14] == [0, 0, 0, 0xff, 0xfe, 0] {
635                let ll = [dst[14], dst[15]];
636
637                if ll_dst_addr == Some(LlAddress::Short(ll)) {
638                    self.set_dam_field(0b11);
639                } else {
640                    self.set_dam_field(0b10);
641
642                    self.set_field(idx, &dst[14..]);
643                    idx += 2;
644                }
645            } else if is_eui_64 {
646                self.set_dam_field(0b11);
647            } else {
648                self.set_dam_field(0b01);
649
650                self.set_field(idx, &dst[8..]);
651                idx += 8;
652            }
653        } else {
654            self.set_dam_field(0b00);
655
656            self.set_field(idx, &dst);
657            idx += 16;
658        }
659
660        idx
661    }
662
663    /// Return a mutable pointer to the payload.
664    pub fn payload_mut(&mut self) -> &mut [u8] {
665        let mut len = self.ip_fields_start();
666
667        len += self.traffic_class_size();
668        len += self.next_header_size();
669        len += self.hop_limit_size();
670        len += self.src_address_size();
671        len += self.dst_address_size();
672
673        let len = len as usize;
674
675        let data = self.buffer.as_mut();
676        &mut data[len..]
677    }
678}
679
680/// A high-level representation of a 6LoWPAN IPHC header.
681#[derive(Debug, PartialEq, Eq, Clone, Copy)]
682pub struct Repr {
683    pub src_addr: ipv6::Address,
684    pub ll_src_addr: Option<LlAddress>,
685    pub dst_addr: ipv6::Address,
686    pub ll_dst_addr: Option<LlAddress>,
687    pub next_header: NextHeader,
688    pub hop_limit: u8,
689    // TODO(thvdveld): refactor the following fields into something else
690    pub ecn: Option<u8>,
691    pub dscp: Option<u8>,
692    pub flow_label: Option<u16>,
693}
694
695impl core::fmt::Display for Repr {
696    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
697        write!(
698            f,
699            "IPHC src={} dst={} nxt-hdr={} hop-limit={}",
700            self.src_addr, self.dst_addr, self.next_header, self.hop_limit
701        )
702    }
703}
704
705#[cfg(feature = "defmt")]
706impl defmt::Format for Repr {
707    fn format(&self, fmt: defmt::Formatter) {
708        defmt::write!(
709            fmt,
710            "IPHC src={} dst={} nxt-hdr={} hop-limit={}",
711            self.src_addr,
712            self.dst_addr,
713            self.next_header,
714            self.hop_limit
715        );
716    }
717}
718
719impl Repr {
720    /// Parse a 6LoWPAN IPHC header and return a high-level representation.
721    ///
722    /// The `ll_src_addr` and `ll_dst_addr` are the link-local addresses used for resolving the
723    /// IPv6 packets.
724    pub fn parse<T: AsRef<[u8]> + ?Sized>(
725        packet: &Packet<&T>,
726        ll_src_addr: Option<LlAddress>,
727        ll_dst_addr: Option<LlAddress>,
728        addr_context: &[AddressContext],
729    ) -> Result<Self> {
730        // Ensure basic accessors will work.
731        packet.check_len()?;
732
733        if packet.dispatch_field() != DISPATCH_IPHC_HEADER {
734            // This is not an LOWPAN_IPHC packet.
735            return Err(Error);
736        }
737
738        let src_addr = packet.src_addr()?.resolve(ll_src_addr, addr_context)?;
739        let dst_addr = packet.dst_addr()?.resolve(ll_dst_addr, addr_context)?;
740
741        Ok(Self {
742            src_addr,
743            ll_src_addr,
744            dst_addr,
745            ll_dst_addr,
746            next_header: packet.next_header(),
747            hop_limit: packet.hop_limit(),
748            ecn: packet.ecn_field(),
749            dscp: packet.dscp_field(),
750            flow_label: packet.flow_label_field(),
751        })
752    }
753
754    /// Return the length of a header that will be emitted from this high-level representation.
755    pub fn buffer_len(&self) -> usize {
756        let mut len = 0;
757        len += 2; // The minimal header length
758
759        len += match self.next_header {
760            NextHeader::Compressed => 0, // The next header is compressed (we don't need to inline what the next header is)
761            NextHeader::Uncompressed(_) => 1, // The next header field is inlined
762        };
763
764        // Hop Limit size
765        len += match self.hop_limit {
766            255 | 64 | 1 => 0, // We can inline the hop limit
767            _ => 1,
768        };
769
770        // Add the length of the source address
771        len += if self.src_addr == ipv6::Address::UNSPECIFIED {
772            0
773        } else if self.src_addr.is_link_local() {
774            let src = self.src_addr.octets();
775            let ll = [src[14], src[15]];
776
777            let is_eui_64 = self
778                .ll_src_addr
779                .map(|addr| {
780                    addr.as_eui_64()
781                        .map(|addr| addr[..] == src[8..])
782                        .unwrap_or(false)
783                })
784                .unwrap_or(false);
785
786            if src[8..14] == [0, 0, 0, 0xff, 0xfe, 0] {
787                if self.ll_src_addr == Some(LlAddress::Short(ll)) {
788                    0
789                } else {
790                    2
791                }
792            } else if is_eui_64 {
793                0
794            } else {
795                8
796            }
797        } else {
798            16
799        };
800
801        // Add the size of the destination header
802        let dst = self.dst_addr.octets();
803        len += if self.dst_addr.is_multicast() {
804            if dst[1] == 0x02 && dst[2..15] == [0; 13] {
805                1
806            } else if dst[2..13] == [0; 11] {
807                4
808            } else if dst[2..11] == [0; 9] {
809                6
810            } else {
811                16
812            }
813        } else if self.dst_addr.is_link_local() {
814            let is_eui_64 = self
815                .ll_dst_addr
816                .map(|addr| {
817                    addr.as_eui_64()
818                        .map(|addr| addr[..] == dst[8..])
819                        .unwrap_or(false)
820                })
821                .unwrap_or(false);
822
823            if dst[8..14] == [0, 0, 0, 0xff, 0xfe, 0] {
824                let ll = [dst[14], dst[15]];
825
826                if self.ll_dst_addr == Some(LlAddress::Short(ll)) {
827                    0
828                } else {
829                    2
830                }
831            } else if is_eui_64 {
832                0
833            } else {
834                8
835            }
836        } else {
837            16
838        };
839
840        len += match (self.ecn, self.dscp, self.flow_label) {
841            (Some(_), Some(_), Some(_)) => 4,
842            (Some(_), None, Some(_)) => 3,
843            (Some(_), Some(_), None) => 1,
844            (None, None, None) => 0,
845            _ => unreachable!(),
846        };
847
848        len
849    }
850
851    /// Emit a high-level representation into a 6LoWPAN IPHC header.
852    pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(&self, packet: &mut Packet<T>) {
853        let idx = 2;
854
855        packet.set_dispatch_field();
856
857        // FIXME(thvdveld): we don't set anything from the traffic flow.
858        packet.set_tf_field(0b11);
859
860        let idx = packet.set_next_header(self.next_header, idx);
861        let idx = packet.set_hop_limit(self.hop_limit, idx);
862        let idx = packet.set_src_address(self.src_addr, self.ll_src_addr, idx);
863        packet.set_dst_address(self.dst_addr, self.ll_dst_addr, idx);
864    }
865}
866
867#[cfg(test)]
868mod test {
869    use super::*;
870
871    #[test]
872    fn iphc_fields() {
873        let bytes = [
874            0x7a, 0x33, // IPHC
875            0x3a, // Next header
876        ];
877
878        let packet = Packet::new_unchecked(bytes);
879
880        assert_eq!(packet.dispatch_field(), 0b011);
881        assert_eq!(packet.tf_field(), 0b11);
882        assert_eq!(packet.nh_field(), 0b0);
883        assert_eq!(packet.hlim_field(), 0b10);
884        assert_eq!(packet.cid_field(), 0b0);
885        assert_eq!(packet.sac_field(), 0b0);
886        assert_eq!(packet.sam_field(), 0b11);
887        assert_eq!(packet.m_field(), 0b0);
888        assert_eq!(packet.dac_field(), 0b0);
889        assert_eq!(packet.dam_field(), 0b11);
890
891        assert_eq!(
892            packet.next_header(),
893            NextHeader::Uncompressed(IpProtocol::Icmpv6)
894        );
895
896        assert_eq!(packet.src_address_size(), 0);
897        assert_eq!(packet.dst_address_size(), 0);
898        assert_eq!(packet.hop_limit(), 64);
899
900        assert_eq!(
901            packet.src_addr(),
902            Ok(UnresolvedAddress::WithoutContext(AddressMode::FullyElided))
903        );
904        assert_eq!(
905            packet.dst_addr(),
906            Ok(UnresolvedAddress::WithoutContext(AddressMode::FullyElided))
907        );
908
909        let bytes = [
910            0x7e, 0xf7, // IPHC,
911            0x00, // CID
912        ];
913
914        let packet = Packet::new_unchecked(bytes);
915
916        assert_eq!(packet.dispatch_field(), 0b011);
917        assert_eq!(packet.tf_field(), 0b11);
918        assert_eq!(packet.nh_field(), 0b1);
919        assert_eq!(packet.hlim_field(), 0b10);
920        assert_eq!(packet.cid_field(), 0b1);
921        assert_eq!(packet.sac_field(), 0b1);
922        assert_eq!(packet.sam_field(), 0b11);
923        assert_eq!(packet.m_field(), 0b0);
924        assert_eq!(packet.dac_field(), 0b1);
925        assert_eq!(packet.dam_field(), 0b11);
926
927        assert_eq!(packet.next_header(), NextHeader::Compressed);
928
929        assert_eq!(packet.src_address_size(), 0);
930        assert_eq!(packet.dst_address_size(), 0);
931        assert_eq!(packet.hop_limit(), 64);
932
933        assert_eq!(
934            packet.src_addr(),
935            Ok(UnresolvedAddress::WithContext((
936                0,
937                AddressMode::FullyElided
938            )))
939        );
940        assert_eq!(
941            packet.dst_addr(),
942            Ok(UnresolvedAddress::WithContext((
943                0,
944                AddressMode::FullyElided
945            )))
946        );
947    }
948}