Skip to main content

smoltcp/iface/interface/
sixlowpan.rs

1use super::*;
2use crate::wire::Result;
3
4// Max len of non-fragmented packets after decompression (including ipv6 header and payload)
5// TODO: lower. Should be (6lowpan mtu) - (min 6lowpan header size) + (max ipv6 header size)
6pub(crate) const MAX_DECOMPRESSED_LEN: usize = 1500;
7
8impl Interface {
9    /// Process fragments that still need to be sent for 6LoWPAN packets.
10    #[cfg(feature = "proto-sixlowpan-fragmentation")]
11    pub(super) fn sixlowpan_egress(&mut self, device: &mut (impl Device + ?Sized)) {
12        // Reset the buffer when we transmitted everything.
13        if self.fragmenter.finished() {
14            self.fragmenter.reset();
15        }
16
17        if self.fragmenter.is_empty() {
18            return;
19        }
20
21        let pkt = &self.fragmenter;
22        if pkt.packet_len > pkt.sent_bytes
23            && let Some(tx_token) = device.transmit(self.inner.now)
24        {
25            self.inner
26                .dispatch_ieee802154_frag(tx_token, &mut self.fragmenter);
27        }
28    }
29
30    /// Get the 6LoWPAN address contexts.
31    pub fn sixlowpan_address_context(&self) -> &[SixlowpanAddressContext] {
32        &self.inner.sixlowpan_address_context[..]
33    }
34
35    /// Get a mutable reference to the 6LoWPAN address contexts.
36    pub fn sixlowpan_address_context_mut(
37        &mut self,
38    ) -> &mut Vec<SixlowpanAddressContext, IFACE_MAX_SIXLOWPAN_ADDRESS_CONTEXT_COUNT> {
39        &mut self.inner.sixlowpan_address_context
40    }
41}
42
43impl InterfaceInner {
44    /// Get the next tag for a 6LoWPAN fragment.
45    #[cfg(feature = "proto-sixlowpan-fragmentation")]
46    fn get_sixlowpan_fragment_tag(&mut self) -> u16 {
47        let tag = self.tag;
48        self.tag = self.tag.wrapping_add(1);
49        tag
50    }
51
52    pub(super) fn process_sixlowpan<'output, 'payload: 'output>(
53        &mut self,
54        sockets: &mut SocketSet,
55        meta: PacketMeta,
56        ieee802154_repr: &Ieee802154Repr,
57        payload: &'payload [u8],
58        f: &'output mut FragmentsBuffer,
59    ) -> Option<Packet<'output>> {
60        let payload = match check!(SixlowpanPacket::dispatch(payload)) {
61            #[cfg(not(feature = "proto-sixlowpan-fragmentation"))]
62            SixlowpanPacket::FragmentHeader => {
63                net_debug!(
64                    "Fragmentation is not supported, \
65                    use the `proto-sixlowpan-fragmentation` feature to add support."
66                );
67                return None;
68            }
69            #[cfg(feature = "proto-sixlowpan-fragmentation")]
70            SixlowpanPacket::FragmentHeader => {
71                self.process_sixlowpan_fragment(ieee802154_repr, payload, f)?
72            }
73            SixlowpanPacket::IphcHeader => {
74                match Self::sixlowpan_to_ipv6(
75                    &self.sixlowpan_address_context,
76                    ieee802154_repr,
77                    payload,
78                    None,
79                    &mut f.decompress_buf,
80                ) {
81                    Ok(len) => &f.decompress_buf[..len],
82                    Err(e) => {
83                        net_debug!("sixlowpan decompress failed: {:?}", e);
84                        return None;
85                    }
86                }
87            }
88        };
89
90        self.process_ipv6(
91            sockets,
92            meta,
93            match ieee802154_repr.src_addr {
94                Some(s) => HardwareAddress::Ieee802154(s),
95                None => HardwareAddress::Ieee802154(Ieee802154Address::Absent),
96            },
97            &check!(Ipv6Packet::new_checked(payload)),
98        )
99    }
100
101    #[cfg(feature = "proto-sixlowpan-fragmentation")]
102    fn process_sixlowpan_fragment<'output, 'payload: 'output>(
103        &mut self,
104        ieee802154_repr: &Ieee802154Repr,
105        payload: &'payload [u8],
106        f: &'output mut FragmentsBuffer,
107    ) -> Option<&'output [u8]> {
108        use crate::iface::fragmentation::{AssemblerError, AssemblerFullError};
109
110        // We have a fragment header, which means we cannot process the 6LoWPAN packet,
111        // unless we have a complete one after processing this fragment.
112        let frag = check!(SixlowpanFragPacket::new_checked(payload));
113
114        // From RFC 4944 § 5.3: "The value of datagram_size SHALL be 40 octets more than the value
115        // of Payload Length in the IPv6 header of the packet."
116        // We should check that this is true, otherwise `buffer.split_at_mut(40)` will panic, since
117        // we assume that the decompressed packet is at least 40 bytes.
118        if frag.datagram_size() < 40 {
119            net_debug!("6LoWPAN: fragment size too small");
120            return None;
121        }
122
123        // The key specifies to which 6LoWPAN fragment it belongs too.
124        // It is based on the link layer addresses, the tag and the size.
125        let key = FragKey::Sixlowpan(frag.get_key(ieee802154_repr));
126
127        // The offset of this fragment in increments of 8 octets.
128        let offset = frag.datagram_offset() as usize * 8;
129
130        // We reserve a spot in the packet assembler set and add the required
131        // information to the packet assembler.
132        // This information is the total size of the packet when it is fully assmbled.
133        // We also pass the header size, since this is needed when other fragments
134        // (other than the first one) are added.
135        let frag_slot = match f.assembler.get(&key, self.now + f.reassembly_timeout) {
136            Ok(frag) => frag,
137            Err(AssemblerFullError) => {
138                net_debug!("No available packet assembler for fragmented packet");
139                return None;
140            }
141        };
142
143        if frag.is_first_fragment() {
144            // The first fragment contains the total size of the IPv6 packet.
145            // However, we received a packet that is compressed following the 6LoWPAN
146            // standard. This means we need to convert the IPv6 packet size to a 6LoWPAN
147            // packet size. The packet size can be different because of first the
148            // compression of the IP header and when UDP is used (because the UDP header
149            // can also be compressed). Other headers are not compressed by 6LoWPAN.
150
151            // First segment tells us the total size.
152            let total_size = frag.datagram_size() as usize;
153            if frag_slot.set_total_size(total_size).is_err() {
154                net_debug!("No available packet assembler for fragmented packet");
155                return None;
156            }
157
158            // Decompress headers+payload into the assembler.
159            if let Err(e) = frag_slot.add_with(0, |buffer| {
160                Self::sixlowpan_to_ipv6(
161                    &self.sixlowpan_address_context,
162                    ieee802154_repr,
163                    frag.payload(),
164                    Some(total_size),
165                    buffer,
166                )
167                .map_err(|_| AssemblerError)
168            }) {
169                net_debug!("fragmentation error: {:?}", e);
170                return None;
171            }
172        } else {
173            // Add the fragment to the packet assembler.
174            if let Err(e) = frag_slot.add(frag.payload(), offset) {
175                net_debug!("fragmentation error: {:?}", e);
176                return None;
177            }
178        }
179
180        match frag_slot.assemble() {
181            Some(payload) => {
182                net_trace!("6LoWPAN: fragmented packet now complete");
183                Some(payload)
184            }
185            None => None,
186        }
187    }
188
189    /// Decompress a 6LoWPAN packet into an IPv6 packet.
190    ///
191    /// The return value is the length of the decompressed packet, but not including the total
192    /// length of the payload of the UDP packet. This value is then used by the assembler to know
193    /// how far in the assembler buffer the packet is.
194    ///
195    /// **NOTE**: when decompressing a fragmented packet, the `total_len` parameter should be
196    /// passed. This is the total length of the IPv6 packet, including the IPv6 header. It is used
197    /// for calculating the length field in the UDP header.
198    fn sixlowpan_to_ipv6(
199        address_context: &[SixlowpanAddressContext],
200        ieee802154_repr: &Ieee802154Repr,
201        iphc_payload: &[u8],
202        total_len: Option<usize>,
203        buffer: &mut [u8],
204    ) -> Result<usize> {
205        let iphc = SixlowpanIphcPacket::new_checked(iphc_payload)?;
206        let iphc_repr = SixlowpanIphcRepr::parse(
207            &iphc,
208            ieee802154_repr.src_addr,
209            ieee802154_repr.dst_addr,
210            address_context,
211        )?;
212
213        // The first thing we have to decompress is the IPv6 header. However, at this point we
214        // don't know the total size of the packet, neither the next header, since that can be a
215        // compressed header. However, we know that the IPv6 header is 40 bytes, so we can reserve
216        // this space in the buffer such that we can decompress the IPv6 header into it at a later
217        // point.
218        let (ipv6_buffer, mut buffer) = buffer.split_at_mut(40);
219        let mut ipv6_header = Ipv6Packet::new_unchecked(ipv6_buffer);
220
221        // If the total length is given, we are dealing with a fragmented packet. The total
222        // length is then used to calculate the length field for the UDP header. If the total
223        // length is not given, we are not working with a fragmented packet, and we need to
224        // calculate the length of the payload ourselves.
225        let mut payload_len = 40;
226        let mut decompressed_len = 40;
227
228        let mut next_header = Some(iphc_repr.next_header);
229        let mut data = iphc.payload();
230
231        while let Some(nh) = next_header {
232            match nh {
233                SixlowpanNextHeader::Compressed => match SixlowpanNhcPacket::dispatch(data)? {
234                    SixlowpanNhcPacket::ExtHeader => {
235                        (buffer, data) = decompress_ext_hdr(
236                            data,
237                            &mut next_header,
238                            buffer,
239                            &mut payload_len,
240                            &mut decompressed_len,
241                        )?;
242                    }
243                    SixlowpanNhcPacket::UdpHeader => {
244                        decompress_udp(
245                            data,
246                            &iphc_repr,
247                            buffer,
248                            total_len,
249                            &mut payload_len,
250                            &mut decompressed_len,
251                        )?;
252
253                        break;
254                    }
255                },
256                SixlowpanNextHeader::Uncompressed(proto) => {
257                    // We have a 6LoWPAN uncompressed header.
258                    match proto {
259                        IpProtocol::Tcp | IpProtocol::Udp | IpProtocol::Icmpv6 => {
260                            // There can be no protocol after this one, so we can just copy the
261                            // rest of the data buffer. There is also no length field in the UDP
262                            // header that we need to correct as this header was not changed by the
263                            // 6LoWPAN compressor.
264                            if data.len() > buffer.len() {
265                                return Err(Error);
266                            }
267                            buffer[..data.len()].copy_from_slice(data);
268                            payload_len += data.len();
269                            decompressed_len += data.len();
270                            break;
271                        }
272                        proto => {
273                            net_debug!("Unsupported uncompressed next header: {:?}", proto);
274                            return Err(Error);
275                        }
276                    }
277                }
278            }
279        }
280
281        let ipv6_repr = Ipv6Repr {
282            src_addr: iphc_repr.src_addr,
283            dst_addr: iphc_repr.dst_addr,
284            next_header: decompress_next_header(iphc_repr.next_header, iphc.payload())?,
285            payload_len: total_len.unwrap_or(payload_len) - 40,
286            hop_limit: iphc_repr.hop_limit,
287        };
288        ipv6_repr.emit(&mut ipv6_header);
289
290        Ok(decompressed_len)
291    }
292
293    pub(super) fn dispatch_sixlowpan<Tx: TxToken>(
294        &mut self,
295        mut tx_token: Tx,
296        meta: PacketMeta,
297        packet: Packet,
298        ieee_repr: Ieee802154Repr,
299        frag: &mut Fragmenter,
300    ) {
301        let packet = match packet {
302            #[cfg(feature = "proto-ipv4")]
303            Packet::Ipv4(_) => unreachable!(),
304            Packet::Ipv6(packet) => packet,
305        };
306
307        // First we calculate the size we are going to need. If the size is bigger than the MTU,
308        // then we use fragmentation.
309        let (total_size, compressed_size, uncompressed_size) =
310            Self::compressed_packet_size(&packet, &ieee_repr);
311
312        let ieee_len = ieee_repr.buffer_len();
313
314        // TODO(thvdveld): use the MTU of the device.
315        if total_size + ieee_len > 125 {
316            #[cfg(feature = "proto-sixlowpan-fragmentation")]
317            {
318                // The packet does not fit in one Ieee802154 frame, so we need fragmentation.
319                // We do this by emitting everything in the `frag.buffer` from the interface.
320                // After emitting everything into that buffer, we send the first fragment heere.
321                // When `poll` is called again, we check if frag was fully sent, otherwise we
322                // call `dispatch_ieee802154_frag`, which will transmit the other fragments.
323
324                // `dispatch_ieee802154_frag` requires some information about the total packet size,
325                // the link local source and destination address...
326
327                let pkt = frag;
328                if pkt.buffer.len() < total_size {
329                    net_debug!(
330                        "dispatch_ieee802154: dropping, \
331                        fragmentation buffer is too small, at least {} needed",
332                        total_size
333                    );
334                    return;
335                }
336
337                let payload_length = packet.header.payload_len;
338
339                Self::ipv6_to_sixlowpan(
340                    &self.checksum_caps(),
341                    packet,
342                    &ieee_repr,
343                    &mut pkt.buffer[..],
344                );
345
346                pkt.sixlowpan.ll_dst_addr = ieee_repr.dst_addr.unwrap();
347                pkt.sixlowpan.ll_src_addr = ieee_repr.src_addr.unwrap();
348                pkt.packet_len = total_size;
349
350                // The datagram size that we need to set in the first fragment header is equal to the
351                // IPv6 payload length + 40.
352                pkt.sixlowpan.datagram_size = (payload_length + 40) as u16;
353
354                let tag = self.get_sixlowpan_fragment_tag();
355                // We save the tag for the other fragments that will be created when calling `poll`
356                // multiple times.
357                pkt.sixlowpan.datagram_tag = tag;
358
359                let frag1 = SixlowpanFragRepr::FirstFragment {
360                    size: pkt.sixlowpan.datagram_size,
361                    tag,
362                };
363                let fragn = SixlowpanFragRepr::Fragment {
364                    size: pkt.sixlowpan.datagram_size,
365                    tag,
366                    offset: 0,
367                };
368
369                // We calculate how much data we can send in the first fragment and the other
370                // fragments. The eventual IPv6 sizes of these fragments need to be a multiple of eight
371                // (except for the last fragment) since the offset field in the fragment is an offset
372                // in multiples of 8 octets. This is explained in [RFC 4944 § 5.3].
373                //
374                // [RFC 4944 § 5.3]: https://datatracker.ietf.org/doc/html/rfc4944#section-5.3
375
376                let header_diff = uncompressed_size - compressed_size;
377                let frag1_size =
378                    (125 - ieee_len - frag1.buffer_len() + header_diff) / 8 * 8 - header_diff;
379
380                pkt.sixlowpan.fragn_size = (125 - ieee_len - fragn.buffer_len()) / 8 * 8;
381                pkt.sent_bytes = frag1_size;
382                pkt.sixlowpan.datagram_offset = frag1_size + header_diff;
383
384                tx_token.set_meta(meta);
385                tx_token.consume(ieee_len + frag1.buffer_len() + frag1_size, |mut tx_buf| {
386                    // Add the IEEE header.
387                    let mut ieee_packet = Ieee802154Frame::new_unchecked(&mut tx_buf[..ieee_len]);
388                    ieee_repr.emit(&mut ieee_packet);
389                    tx_buf = &mut tx_buf[ieee_len..];
390
391                    // Add the first fragment header
392                    let mut frag1_packet = SixlowpanFragPacket::new_unchecked(&mut tx_buf);
393                    frag1.emit(&mut frag1_packet);
394                    tx_buf = &mut tx_buf[frag1.buffer_len()..];
395
396                    // Add the buffer part.
397                    tx_buf[..frag1_size].copy_from_slice(&pkt.buffer[..frag1_size]);
398                });
399            }
400
401            #[cfg(not(feature = "proto-sixlowpan-fragmentation"))]
402            {
403                net_debug!(
404                    "Enable the `proto-sixlowpan-fragmentation` feature for fragmentation support."
405                );
406                return;
407            }
408        } else {
409            tx_token.set_meta(meta);
410
411            // We don't need fragmentation, so we emit everything to the TX token.
412            tx_token.consume(total_size + ieee_len, |mut tx_buf| {
413                let mut ieee_packet = Ieee802154Frame::new_unchecked(&mut tx_buf[..ieee_len]);
414                ieee_repr.emit(&mut ieee_packet);
415                tx_buf = &mut tx_buf[ieee_len..];
416
417                Self::ipv6_to_sixlowpan(&self.checksum_caps(), packet, &ieee_repr, tx_buf);
418            });
419        }
420    }
421
422    fn ipv6_to_sixlowpan(
423        checksum_caps: &ChecksumCapabilities,
424        mut packet: PacketV6,
425        ieee_repr: &Ieee802154Repr,
426        mut buffer: &mut [u8],
427    ) {
428        let last_header = packet.payload.as_sixlowpan_next_header();
429        let next_header = last_header;
430
431        #[cfg(feature = "proto-ipv6-hbh")]
432        let next_header = if packet.hop_by_hop.is_some() {
433            SixlowpanNextHeader::Compressed
434        } else {
435            next_header
436        };
437
438        #[cfg(feature = "proto-ipv6-routing")]
439        let next_header = if packet.routing.is_some() {
440            SixlowpanNextHeader::Compressed
441        } else {
442            next_header
443        };
444
445        let iphc_repr = SixlowpanIphcRepr {
446            src_addr: packet.header.src_addr,
447            ll_src_addr: ieee_repr.src_addr,
448            dst_addr: packet.header.dst_addr,
449            ll_dst_addr: ieee_repr.dst_addr,
450            next_header,
451            hop_limit: packet.header.hop_limit,
452            ecn: None,
453            dscp: None,
454            flow_label: None,
455        };
456
457        iphc_repr.emit(&mut SixlowpanIphcPacket::new_unchecked(
458            &mut buffer[..iphc_repr.buffer_len()],
459        ));
460        buffer = &mut buffer[iphc_repr.buffer_len()..];
461
462        // Emit the Hop-by-Hop header
463        #[cfg(feature = "proto-ipv6-hbh")]
464        if let Some(hbh) = packet.hop_by_hop {
465            #[allow(unused)]
466            let next_header = last_header;
467
468            #[cfg(feature = "proto-ipv6-routing")]
469            let next_header = if packet.routing.is_some() {
470                SixlowpanNextHeader::Compressed
471            } else {
472                last_header
473            };
474
475            let ext_hdr = SixlowpanExtHeaderRepr {
476                ext_header_id: SixlowpanExtHeaderId::HopByHopHeader,
477                next_header,
478                length: hbh.options.iter().map(|o| o.buffer_len()).sum::<usize>() as u8,
479            };
480            ext_hdr.emit(&mut SixlowpanExtHeaderPacket::new_unchecked(
481                &mut buffer[..ext_hdr.buffer_len()],
482            ));
483            buffer = &mut buffer[ext_hdr.buffer_len()..];
484
485            for opt in &hbh.options {
486                opt.emit(&mut Ipv6Option::new_unchecked(
487                    &mut buffer[..opt.buffer_len()],
488                ));
489
490                buffer = &mut buffer[opt.buffer_len()..];
491            }
492        }
493
494        // Emit the Routing header
495        #[cfg(feature = "proto-ipv6-routing")]
496        if let Some(routing) = &packet.routing {
497            let ext_hdr = SixlowpanExtHeaderRepr {
498                ext_header_id: SixlowpanExtHeaderId::RoutingHeader,
499                next_header,
500                length: routing.buffer_len() as u8,
501            };
502            ext_hdr.emit(&mut SixlowpanExtHeaderPacket::new_unchecked(
503                &mut buffer[..ext_hdr.buffer_len()],
504            ));
505            buffer = &mut buffer[ext_hdr.buffer_len()..];
506
507            routing.emit(&mut Ipv6RoutingHeader::new_unchecked(
508                &mut buffer[..routing.buffer_len()],
509            ));
510            buffer = &mut buffer[routing.buffer_len()..];
511        }
512
513        match &mut packet.payload {
514            IpPayload::Icmpv6(icmp_repr) => {
515                icmp_repr.emit(
516                    &packet.header.src_addr,
517                    &packet.header.dst_addr,
518                    &mut Icmpv6Packet::new_unchecked(&mut buffer[..icmp_repr.buffer_len()]),
519                    checksum_caps,
520                );
521            }
522            #[cfg(any(feature = "socket-udp", feature = "socket-dns"))]
523            IpPayload::Udp(udp_repr, payload) => {
524                let udp_repr = SixlowpanUdpNhcRepr(*udp_repr);
525                udp_repr.emit(
526                    &mut SixlowpanUdpNhcPacket::new_unchecked(
527                        &mut buffer[..udp_repr.header_len() + payload.len()],
528                    ),
529                    &iphc_repr.src_addr,
530                    &iphc_repr.dst_addr,
531                    payload.len(),
532                    |buf| buf.copy_from_slice(payload),
533                    checksum_caps,
534                );
535            }
536            #[cfg(feature = "socket-tcp")]
537            IpPayload::Tcp(tcp_repr) => {
538                tcp_repr.emit(
539                    &mut TcpPacket::new_unchecked(&mut buffer[..tcp_repr.buffer_len()]),
540                    &packet.header.src_addr.into(),
541                    &packet.header.dst_addr.into(),
542                    checksum_caps,
543                );
544            }
545            #[cfg(feature = "socket-raw")]
546            IpPayload::Raw(_raw) => todo!(),
547
548            #[allow(unreachable_patterns)]
549            _ => unreachable!(),
550        }
551    }
552
553    /// Calculates three sizes:
554    ///  - total size: the size of a compressed IPv6 packet
555    ///  - compressed header size: the size of the compressed headers
556    ///  - uncompressed header size: the size of the headers that are not compressed
557    ///
558    /// They are returned as a tuple in the same order.
559    fn compressed_packet_size(
560        packet: &PacketV6,
561        ieee_repr: &Ieee802154Repr,
562    ) -> (usize, usize, usize) {
563        let last_header = packet.payload.as_sixlowpan_next_header();
564        let next_header = last_header;
565
566        #[cfg(feature = "proto-ipv6-hbh")]
567        let next_header = if packet.hop_by_hop.is_some() {
568            SixlowpanNextHeader::Compressed
569        } else {
570            next_header
571        };
572
573        #[cfg(feature = "proto-ipv6-routing")]
574        let next_header = if packet.routing.is_some() {
575            SixlowpanNextHeader::Compressed
576        } else {
577            next_header
578        };
579
580        let iphc = SixlowpanIphcRepr {
581            src_addr: packet.header.src_addr,
582            ll_src_addr: ieee_repr.src_addr,
583            dst_addr: packet.header.dst_addr,
584            ll_dst_addr: ieee_repr.dst_addr,
585            next_header,
586            hop_limit: packet.header.hop_limit,
587            ecn: None,
588            dscp: None,
589            flow_label: None,
590        };
591
592        let mut total_size = iphc.buffer_len();
593        let mut compressed_hdr_size = iphc.buffer_len();
594        let mut uncompressed_hdr_size = packet.header.buffer_len();
595
596        // Add the hop-by-hop to the sizes.
597        #[cfg(feature = "proto-ipv6-hbh")]
598        if let Some(hbh) = &packet.hop_by_hop {
599            #[allow(unused)]
600            let next_header = last_header;
601
602            #[cfg(feature = "proto-ipv6-routing")]
603            let next_header = if packet.routing.is_some() {
604                SixlowpanNextHeader::Compressed
605            } else {
606                last_header
607            };
608
609            let options_size = hbh.options.iter().map(|o| o.buffer_len()).sum::<usize>();
610
611            let ext_hdr = SixlowpanExtHeaderRepr {
612                ext_header_id: SixlowpanExtHeaderId::HopByHopHeader,
613                next_header,
614                length: hbh.buffer_len() as u8 + options_size as u8,
615            };
616
617            total_size += ext_hdr.buffer_len() + options_size;
618            compressed_hdr_size += ext_hdr.buffer_len() + options_size;
619            uncompressed_hdr_size += hbh.buffer_len() + options_size;
620        }
621
622        // Add the routing header to the sizes.
623        #[cfg(feature = "proto-ipv6-routing")]
624        if let Some(routing) = &packet.routing {
625            let ext_hdr = SixlowpanExtHeaderRepr {
626                ext_header_id: SixlowpanExtHeaderId::RoutingHeader,
627                next_header,
628                length: routing.buffer_len() as u8,
629            };
630            total_size += ext_hdr.buffer_len() + routing.buffer_len();
631            compressed_hdr_size += ext_hdr.buffer_len() + routing.buffer_len();
632            uncompressed_hdr_size += routing.buffer_len();
633        }
634
635        match packet.payload {
636            #[cfg(any(feature = "socket-udp", feature = "socket-dns"))]
637            IpPayload::Udp(udp_hdr, payload) => {
638                uncompressed_hdr_size += udp_hdr.header_len();
639
640                let udp_hdr = SixlowpanUdpNhcRepr(udp_hdr);
641                compressed_hdr_size += udp_hdr.header_len();
642
643                total_size += udp_hdr.header_len() + payload.len();
644            }
645            _ => {
646                total_size += packet.header.payload_len;
647            }
648        }
649
650        (total_size, compressed_hdr_size, uncompressed_hdr_size)
651    }
652
653    #[cfg(feature = "proto-sixlowpan-fragmentation")]
654    pub(super) fn dispatch_sixlowpan_frag<Tx: TxToken>(
655        &mut self,
656        tx_token: Tx,
657        ieee_repr: Ieee802154Repr,
658        frag: &mut Fragmenter,
659    ) {
660        // Create the FRAG_N header.
661        let fragn = SixlowpanFragRepr::Fragment {
662            size: frag.sixlowpan.datagram_size,
663            tag: frag.sixlowpan.datagram_tag,
664            offset: (frag.sixlowpan.datagram_offset / 8) as u8,
665        };
666
667        let ieee_len = ieee_repr.buffer_len();
668        let frag_size = (frag.packet_len - frag.sent_bytes).min(frag.sixlowpan.fragn_size);
669
670        tx_token.consume(
671            ieee_repr.buffer_len() + fragn.buffer_len() + frag_size,
672            |mut tx_buf| {
673                let mut ieee_packet = Ieee802154Frame::new_unchecked(&mut tx_buf[..ieee_len]);
674                ieee_repr.emit(&mut ieee_packet);
675                tx_buf = &mut tx_buf[ieee_len..];
676
677                let mut frag_packet =
678                    SixlowpanFragPacket::new_unchecked(&mut tx_buf[..fragn.buffer_len()]);
679                fragn.emit(&mut frag_packet);
680                tx_buf = &mut tx_buf[fragn.buffer_len()..];
681
682                // Add the buffer part
683                tx_buf[..frag_size].copy_from_slice(&frag.buffer[frag.sent_bytes..][..frag_size]);
684
685                frag.sent_bytes += frag_size;
686                frag.sixlowpan.datagram_offset += frag_size;
687            },
688        );
689    }
690}
691
692/// Convert a 6LoWPAN next header to an IPv6 next header.
693#[inline]
694fn decompress_next_header(next_header: SixlowpanNextHeader, payload: &[u8]) -> Result<IpProtocol> {
695    match next_header {
696        SixlowpanNextHeader::Compressed => match SixlowpanNhcPacket::dispatch(payload)? {
697            SixlowpanNhcPacket::ExtHeader => {
698                let ext_hdr = SixlowpanExtHeaderPacket::new_checked(payload)?;
699                Ok(ext_hdr.extension_header_id().into())
700            }
701            SixlowpanNhcPacket::UdpHeader => Ok(IpProtocol::Udp),
702        },
703        SixlowpanNextHeader::Uncompressed(proto) => Ok(proto),
704    }
705}
706
707// NOTE: we always inline this function into the sixlowpan_to_ipv6 function, since it is only used there.
708#[inline(always)]
709fn decompress_ext_hdr<'d>(
710    mut data: &'d [u8],
711    next_header: &mut Option<SixlowpanNextHeader>,
712    mut buffer: &'d mut [u8],
713    payload_len: &mut usize,
714    decompressed_len: &mut usize,
715) -> Result<(&'d mut [u8], &'d [u8])> {
716    let ext_hdr = SixlowpanExtHeaderPacket::new_checked(data)?;
717    let ext_repr = SixlowpanExtHeaderRepr::parse(&ext_hdr)?;
718    let nh = decompress_next_header(
719        ext_repr.next_header,
720        &data[ext_repr.length as usize + ext_repr.buffer_len()..],
721    )?;
722    *next_header = Some(ext_repr.next_header);
723    let ipv6_ext_hdr = Ipv6ExtHeaderRepr {
724        next_header: nh,
725        length: ext_repr.length / 8,
726        data: ext_hdr.payload(),
727    };
728    if ipv6_ext_hdr.header_len() + ipv6_ext_hdr.data.len() > buffer.len() {
729        return Err(Error);
730    }
731    ipv6_ext_hdr.emit(&mut Ipv6ExtHeader::new_unchecked(
732        &mut buffer[..ipv6_ext_hdr.header_len()],
733    ));
734    buffer[ipv6_ext_hdr.header_len()..][..ipv6_ext_hdr.data.len()]
735        .copy_from_slice(ipv6_ext_hdr.data);
736    buffer = &mut buffer[ipv6_ext_hdr.header_len() + ipv6_ext_hdr.data.len()..];
737    *payload_len += ipv6_ext_hdr.header_len() + ipv6_ext_hdr.data.len();
738    *decompressed_len += ipv6_ext_hdr.header_len() + ipv6_ext_hdr.data.len();
739    data = &data[ext_repr.buffer_len() + ext_repr.length as usize..];
740    Ok((buffer, data))
741}
742
743// NOTE: we always inline this function into the sixlowpan_to_ipv6 function, since it is only used there.
744#[inline(always)]
745fn decompress_udp(
746    data: &[u8],
747    iphc_repr: &SixlowpanIphcRepr,
748    buffer: &mut [u8],
749    total_len: Option<usize>,
750    payload_len: &mut usize,
751    decompressed_len: &mut usize,
752) -> Result<()> {
753    let udp_packet = SixlowpanUdpNhcPacket::new_checked(data)?;
754    let payload = udp_packet.payload();
755    let udp_repr = SixlowpanUdpNhcRepr::parse(
756        &udp_packet,
757        &iphc_repr.src_addr,
758        &iphc_repr.dst_addr,
759        &ChecksumCapabilities::ignored(),
760    )?;
761    if udp_repr.header_len() + payload.len() > buffer.len() {
762        return Err(Error);
763    }
764    let udp_payload_len = if let Some(total_len) = total_len {
765        total_len - *payload_len - 8
766    } else {
767        payload.len()
768    };
769    *payload_len += udp_payload_len + 8;
770    *decompressed_len += udp_repr.0.header_len() + payload.len();
771    let mut udp = UdpPacket::new_unchecked(&mut buffer[..payload.len() + 8]);
772    udp_repr.0.emit_header(&mut udp, udp_payload_len);
773    buffer[8..][..payload.len()].copy_from_slice(payload);
774    Ok(())
775}
776
777#[cfg(test)]
778#[cfg(all(feature = "proto-rpl", feature = "proto-ipv6-hbh"))]
779mod tests {
780    use super::*;
781
782    static SIXLOWPAN_COMPRESSED_RPL_DAO: [u8; 99] = [
783        0x61, 0xdc, 0x45, 0xcd, 0xab, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x03, 0x00,
784        0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x7e, 0xf7, 0x00, 0xe0, 0x3a, 0x06, 0x63, 0x04, 0x00,
785        0x1e, 0x08, 0x00, 0x9b, 0x02, 0x3e, 0x63, 0x1e, 0x40, 0x00, 0xf1, 0xfd, 0x00, 0x00, 0x00,
786        0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x05, 0x12, 0x00,
787        0x80, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x00, 0x03, 0x00, 0x03,
788        0x00, 0x03, 0x06, 0x14, 0x00, 0x00, 0x00, 0x1e, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
789        0x00, 0x02, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
790    ];
791
792    static SIXLOWPAN_UNCOMPRESSED_RPL_DAO: [u8; 114] = [
793        0x60, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x40, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
794        0x00, 0x02, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00,
795        0x00, 0x00, 0x02, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x3a, 0x00, 0x63, 0x04, 0x00,
796        0x1e, 0x08, 0x00, 0x9b, 0x02, 0x3e, 0x63, 0x1e, 0x40, 0x00, 0xf1, 0xfd, 0x00, 0x00, 0x00,
797        0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x05, 0x12, 0x00,
798        0x80, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x00, 0x03, 0x00, 0x03,
799        0x00, 0x03, 0x06, 0x14, 0x00, 0x00, 0x00, 0x1e, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
800        0x00, 0x02, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
801    ];
802
803    #[test]
804    fn test_sixlowpan_decompress_hop_by_hop_with_icmpv6() {
805        let address_context = [SixlowpanAddressContext([
806            0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
807        ])];
808
809        let ieee_frame = Ieee802154Frame::new_checked(&SIXLOWPAN_COMPRESSED_RPL_DAO).unwrap();
810        let ieee_repr = Ieee802154Repr::parse(&ieee_frame).unwrap();
811
812        let mut buffer = [0u8; 256];
813        let len = InterfaceInner::sixlowpan_to_ipv6(
814            &address_context,
815            &ieee_repr,
816            ieee_frame.payload().unwrap(),
817            None,
818            &mut buffer[..],
819        )
820        .unwrap();
821
822        assert_eq!(&buffer[..len], &SIXLOWPAN_UNCOMPRESSED_RPL_DAO);
823    }
824
825    #[test]
826    fn test_sixlowpan_compress_hop_by_hop_with_icmpv6() {
827        let ieee_repr = Ieee802154Repr {
828            frame_type: Ieee802154FrameType::Data,
829            security_enabled: false,
830            frame_pending: false,
831            ack_request: true,
832            sequence_number: Some(69),
833            pan_id_compression: true,
834            frame_version: Ieee802154FrameVersion::Ieee802154_2006,
835            dst_pan_id: Some(Ieee802154Pan(43981)),
836            dst_addr: Some(Ieee802154Address::Extended([0, 1, 0, 1, 0, 1, 0, 1])),
837            src_pan_id: None,
838            src_addr: Some(Ieee802154Address::Extended([0, 3, 0, 3, 0, 3, 0, 3])),
839        };
840
841        let mut ip_packet = PacketV6 {
842            header: Ipv6Repr {
843                src_addr: Ipv6Address::from_octets([
844                    253, 0, 0, 0, 0, 0, 0, 0, 2, 3, 0, 3, 0, 3, 0, 3,
845                ]),
846                dst_addr: Ipv6Address::from_octets([
847                    253, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 0, 1, 0, 1,
848                ]),
849                next_header: IpProtocol::Icmpv6,
850                payload_len: 66,
851                hop_limit: 64,
852            },
853            #[cfg(feature = "proto-ipv6-hbh")]
854            hop_by_hop: None,
855            #[cfg(feature = "proto-ipv6-fragmentation")]
856            fragment: None,
857            #[cfg(feature = "proto-ipv6-routing")]
858            routing: None,
859            payload: IpPayload::Icmpv6(Icmpv6Repr::Rpl(RplRepr::DestinationAdvertisementObject {
860                rpl_instance_id: RplInstanceId::Global(30),
861                expect_ack: false,
862                sequence: 241,
863                dodag_id: Some(Ipv6Address::from_octets([
864                    253, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 0, 1, 0, 1,
865                ])),
866                options: &[],
867            })),
868        };
869
870        let (total_size, _, _) = InterfaceInner::compressed_packet_size(&mut ip_packet, &ieee_repr);
871        let mut buffer = vec![0u8; total_size];
872
873        InterfaceInner::ipv6_to_sixlowpan(
874            &ChecksumCapabilities::default(),
875            ip_packet,
876            &ieee_repr,
877            &mut buffer[..total_size],
878        );
879
880        let result = [
881            0x7e, 0x0, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x3, 0x0, 0x3, 0x0, 0x3, 0x0,
882            0x3, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1,
883            0xe0, 0x3a, 0x6, 0x63, 0x4, 0x0, 0x1e, 0x3, 0x0, 0x9b, 0x2, 0x3e, 0x63, 0x1e, 0x40,
884            0x0, 0xf1, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0,
885            0x1, 0x5, 0x12, 0x0, 0x80, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x3, 0x0, 0x3,
886            0x0, 0x3, 0x0, 0x3, 0x6, 0x14, 0x0, 0x0, 0x0, 0x1e, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
887            0x0, 0x2, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1,
888        ];
889
890        assert_eq!(&result, &result);
891    }
892
893    #[test]
894    fn test_sixlowpan_compress_hop_by_hop_with_udp() {
895        let ieee_repr = Ieee802154Repr {
896            frame_type: Ieee802154FrameType::Data,
897            security_enabled: false,
898            frame_pending: false,
899            ack_request: true,
900            sequence_number: Some(69),
901            pan_id_compression: true,
902            frame_version: Ieee802154FrameVersion::Ieee802154_2006,
903            dst_pan_id: Some(Ieee802154Pan(43981)),
904            dst_addr: Some(Ieee802154Address::Extended([0, 1, 0, 1, 0, 1, 0, 1])),
905            src_pan_id: None,
906            src_addr: Some(Ieee802154Address::Extended([0, 3, 0, 3, 0, 3, 0, 3])),
907        };
908
909        let addr = Ipv6Address::from_octets([253, 0, 0, 0, 0, 0, 0, 0, 2, 3, 0, 3, 0, 3, 0, 3]);
910        let parent_address =
911            Ipv6Address::from_octets([253, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 0, 1, 0, 1]);
912
913        let mut hbh_options = heapless::Vec::new();
914        hbh_options
915            .push(Ipv6OptionRepr::Rpl(RplHopByHopRepr {
916                down: false,
917                rank_error: false,
918                forwarding_error: false,
919                instance_id: RplInstanceId::from(0x1e),
920                sender_rank: 0x300,
921            }))
922            .unwrap();
923
924        let mut ip_packet = PacketV6 {
925            header: Ipv6Repr {
926                src_addr: addr,
927                dst_addr: parent_address,
928                next_header: IpProtocol::Icmpv6,
929                payload_len: 66,
930                hop_limit: 64,
931            },
932            #[cfg(feature = "proto-ipv6-hbh")]
933            hop_by_hop: Some(Ipv6HopByHopRepr {
934                options: hbh_options,
935            }),
936            #[cfg(feature = "proto-ipv6-fragmentation")]
937            fragment: None,
938            #[cfg(feature = "proto-ipv6-routing")]
939            routing: None,
940            payload: IpPayload::Icmpv6(Icmpv6Repr::Rpl(RplRepr::DestinationAdvertisementObject {
941                rpl_instance_id: RplInstanceId::Global(30),
942                expect_ack: false,
943                sequence: 241,
944                dodag_id: Some(Ipv6Address::from_octets([
945                    253, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 0, 1, 0, 1,
946                ])),
947                options: &[
948                    5, 18, 0, 128, 253, 0, 0, 0, 0, 0, 0, 0, 2, 3, 0, 3, 0, 3, 0, 3, 6, 20, 0, 0,
949                    0, 30, 253, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 0, 1, 0, 1,
950                ],
951            })),
952        };
953
954        let (total_size, _, _) = InterfaceInner::compressed_packet_size(&mut ip_packet, &ieee_repr);
955        let mut buffer = vec![0u8; total_size];
956
957        InterfaceInner::ipv6_to_sixlowpan(
958            &ChecksumCapabilities::default(),
959            ip_packet,
960            &ieee_repr,
961            &mut buffer[..total_size],
962        );
963
964        let result = [
965            0x7e, 0x0, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x3, 0x0, 0x3, 0x0, 0x3, 0x0,
966            0x3, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1,
967            0xe0, 0x3a, 0x6, 0x63, 0x4, 0x0, 0x1e, 0x3, 0x0, 0x9b, 0x2, 0x3e, 0x63, 0x1e, 0x40,
968            0x0, 0xf1, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0,
969            0x1, 0x5, 0x12, 0x0, 0x80, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x3, 0x0, 0x3,
970            0x0, 0x3, 0x0, 0x3, 0x6, 0x14, 0x0, 0x0, 0x0, 0x1e, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
971            0x0, 0x2, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1,
972        ];
973
974        assert_eq!(&buffer[..total_size], &result);
975    }
976}