capsules_extra/net/sixlowpan/
sixlowpan_state.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! 6loWPAN compression and reception.
6//!
7//! 6loWPAN (IPv6 over Low-Power Wireless Networks) is standard for compressing
8//! and fragmenting IPv6 packets over low power wireless networks, particularly
9//! ones with MTUs (Minimum Transmission Units) smaller than 1280 octets, like
10//! IEEE 802.15.4. 6loWPAN compression and fragmentation are defined in RFC 4944
11//! and RFC 6282.
12//!
13//! This module implements 6LoWPAN compression and reception, including
14//! compression, fragmentation, and reassembly. It allows a client to convert
15//! between a complete IPv6 packets and a series of Mac-layer frames, and vice
16//! versa. On the transmission end, IPv6 headers are compressed and packets
17//! fragmented if they are larger than the Mac layer MTU size.  For reception,
18//! IPv6 packets are decompressed and reassembled from fragments and clients
19//! recieve callbacks for each full IPv6 packet.
20//!
21//! Usage
22//! -----
23//!
24//! The Sixlowpan library exposes two different interfaces for the transmit path
25//! and the receive path. Below, both interfaces are described in detail.
26//!
27//! Transmit
28//! --------
29//! For a layer interested in sending a packet, this library exposes a
30//! [TxState](struct.TxState.html) struct that statefully compresses an
31//! [IP6Packet](struct.IP6Packet.html) struct. First, the `TxState` object
32//! is initialized for compressing a new packet by calling the `TxState.init`
33//! method. The caller then repeatedly calls `TxState.next_fragment`, which
34//! returns the next frame to be send (or indicates that the transmission
35//! is complete). Note that the upper layer is responsible for sending each
36//! frame, and this library is only responsible for producing compressed frames.
37//!
38//! Receive
39//! -------
40//! The Sixlowpan library is responsible for receiving and reassembling
41//! individual 6LoWPAN-compressed frames. Upper layers interested in receiving
42//! the fully reassembled and decompressed IPv6 packet implement the
43//! [SixlowpanRxClient](trait.SixlowpanRxClient.html) trait, which is called
44//! after a packet is fully received.
45//!
46//! At a high level, clients interact with this module as shown in the diagrams
47//! below:
48//!
49//! ```txt
50//! Transmit:
51//!
52//!           +-----------+
53//!           |Upper Layer|
54//!           +-----------+
55//!             |      ^
56//!             |      |
57//!     next_fragment(..packet..)
58//!             |      |
59//!             v      |
60//!            +---------+
61//!            |Sixlowpan|
62//!            +---------+
63//! ...
64//!         +---------------+
65//!         |SixlowpanClient|
66//!         +---------------+
67//!                 ^
68//!                 |
69//!            send_done(..)
70//!                 |
71//!            +---------+
72//!            |Sixlowpan|
73//!            +---------+
74//! ```
75//!
76//! ```txt
77//! Receive:
78//!
79//!         +---------------+
80//!         |SixlowpanClient|
81//!         +---------------+
82//!                ^
83//!                |
84//!          receive(..buf..)
85//!                |
86//!           +---------+
87//!           |Sixlowpan|
88//!           +---------+
89//! ```
90//!
91//! ```txt
92//! Initialization:
93//!
94//!           +-----------+
95//!           |Upper Layer|
96//!           +-----------+
97//!                 |
98//!          set_client(client)
99//!                 |
100//!                 v
101//!            +---------+
102//!            |Sixlowpan|
103//!            +---------+
104//! ```
105//!
106//! Examples
107//! -----
108//! Examples of how to interface and use this layer are included in the file
109//! `boards/imix/src/lowpan_frag_dummy.rs`. Some set up is required in
110//! the `boards/imix/src/main.rs` file, but for the testing suite, a helper
111//! initialization function is included in the `lowpan_frag_dummy.rs` file.
112
113// Internal Design
114// ---------------
115// The overall 6LoWPAN protocol is non-trivial, and as a result, this layer
116// is fairly complex. There are two main aspects of the 6LoWPAN layer; first
117// is compression, which is abstracted as a distinct library (found at
118// `capsules/src/net/sixlowpan/sixlowpan_compression.rs`), and second is the
119// fragmentation and reassembly layer, which is implemented in this file.
120// The documentation below describes the different components of the
121// fragmentation/reassembly functionality (for 6LoWPAN compression
122// documentation, please consult `capsules/src/net/sixlowpan/sixlowpan_compression.rs`).
123//
124// This layer adds several new structures; principally, it implements the
125// Sixlowpan, TxState, and RxState structs and it also defines the
126// SixlowpanRxClient trait. The Sixlowpan struct is responsible
127// for keeping track of the global *receive* state at this layer, and contains
128// a list of RxState objects. The TxState is responsible for
129// maintaining the current transmit compression state, and how much of the current
130// IPv6 packet has been compressed. The RxState structs maintain the
131// reassembly state corresponding to a single IPv6 packet. Note that since
132// they are maintained as a list, several RxStates can be allocated at compile
133// time, and each RxState corresponds to a distinct IPv6 packet that can be
134// reassembled simultaneously. Finally, the SixlowpanRxClient trait defines
135// the interface between the upper (IP) layer and the Sixlowpan layer for
136// reception. Each object is examined in greater detail below:
137//
138// Sixlowpan:
139// The main `Sixlowpan` struct is responsible for maintaining global reception
140// and reassembly state for received radio frames. The struct contains a list
141// of RxState objects, which serve as reassembly buffers for different IPv6
142// packets. This object implements the RxClient trait, and is set to be the
143// client for the MAC-layer radio. Whenever an RxState is fully reassembled,
144// the upper layers receive a callback through the `SixlowpanRxState` trait.
145//
146// TxState:
147// The TxState struct maintains the state necessary to incrementally fragment
148// a full IPv6 packet. This includes the source/destination Mac
149// addresses and PanIDs, frame-level security options, a total datagram size,
150// and the current offset into the datagram. This struct also maintains some
151// minimal global transmit state, including the global datagram tag and a
152// buffer to pass to the radio.
153//
154// RxState:
155// The RxState struct is analogous to the TxState struct, in that it maintains
156// state specific to reassembling an IPv6 packet. Unlike the TxState struct
157// however, the Sixlowpan object manages multiple RxState structs. These
158// RxStates serve as a pool of objects, and when a fragment arrives, the
159// Sixlowpan object either dispatches it to an in-progress packet reassembly
160// managed by a busy RxState struct, or initializes a free RxState struct
161// to start reassembling the rest of the fragments. Similar to TxState,
162// RxState objects should only be visible to the Sixlowpan object, aside
163// from one caveat - the initialization of RxStates must occur statically
164// outside the Sixlowpan struct (this may change in the future).
165//
166// The RxState struct maintains the in-progress packet buffer, a bitmap
167// indicating which 8-byte chunks have not yet been received, the source/dest
168// mac address pair, datagram size and tag, and a start time (to lazily
169// expire timed-out reassembly processes).
170//
171// SixlowpanRxClient:
172// The SixlowpanRxClient trait has a single function, `receive`. Upper layers
173// that implement this trait can set themselves as the client for the Sixlowpan
174// struct, and will receive a callback once an IPv6 packet has been fully
175// reassembled. Note that the Sixlowpan struct allows for the client to be
176// set or changed at runtime, but the current assumption is that a single,
177// static client sits above the 6LoWPAN receive layer.
178//
179//
180// Design Decisions
181// ----------------
182// Throughout designing this layer, there were a number of critical design
183// decisions made. Several of the most prominent are listed below, with a
184// short rationale as to why they were necessary or the most optimal solution.
185//
186// Multiple RxStates:
187// This design decision is one of the more complicated and contentious ones.
188// Due to the wording of the 6LoWPAN specification and the data associated
189// with 6LoWPAN fragments, it is entirely reasonable to expect that even
190// an edge node (a node not doing routing) might receive 6LoWPAN fragments
191// for different IP packets interleaved. In particular, a 6LoWPAN fragment
192// header contains a datagram tag, which is different for each IPv6 packet
193// fragmented even from the same layer 2 source/destination pairs. Thus,
194// a single node could send multiple, distinct, fragmented IPv6 packets
195// simultaneously (or at least, a node is not prohibited from doing so). In
196// addition, the reassembly timeout for 6LoWPAN fragments is on the order of
197// seconds, and with a single RxState, a single lost fragment could
198// substantially hamper or delay the ability of a client to receive additional
199// packets. As a result of these two issues, the ability to add several
200// RxStates to the 6LoWPAN layer was provided. Unfortunately, this
201// increased the complexity of this layer substantially, and further,
202// necessitated additional initialization complexity by the upper layer.
203//
204// Single TxState:
205// Although both the RxState and TxState structs are treated similarly by
206// the Sixlowpan layer, many aspects of their control flow differ
207// significantly. The final design decision was to have a single upper layer
208// that serialized (or virtualized) both the reception and transmission of
209// IPv6 packets. As a result, only a single outstanding transmission made
210// sense, and thus the layer was designed to have a serial transmit path.
211// Note that this differs greatly from the RxState model, but since we
212// cannot serialize reception in the same way, it did not make sense to treat
213// both RxState and TxState structs identically.
214//
215// TODOs and Known Issues
216// ----------------------------------
217//
218// TODOs:
219//
220//   * Implement and expose a ConfigClient interface?
221//
222//   * Implement the disassociation event, integrate with lower layer
223//
224//   * Move network constants/tuning parameters to a separate file
225//
226// Issues:
227//
228//   * On imix, the receiver sometimes fails to receive a fragment. This
229//     occurs below the Mac layer, and prevents the packet from being fully
230//     reassembled.
231//
232
233use crate::ieee802154::device::{MacDevice, RxClient};
234use crate::ieee802154::framer::Frame;
235use crate::net::frag_utils::Bitmap;
236use crate::net::ieee802154::{Header, KeyId, MacAddress, PanID, SecurityLevel};
237use crate::net::ipv6::IP6Packet;
238use crate::net::sixlowpan::sixlowpan_compression;
239use crate::net::sixlowpan::sixlowpan_compression::{is_lowpan, ContextStore};
240use crate::net::util::{network_slice_to_u16, u16_to_network_slice};
241
242use core::cell::Cell;
243use core::cmp::min;
244
245use kernel::collections::list::{List, ListLink, ListNode};
246use kernel::hil::radio;
247use kernel::hil::time;
248use kernel::hil::time::{Frequency, Ticks};
249use kernel::utilities::cells::{MapCell, TakeCell};
250use kernel::ErrorCode;
251
252// Reassembly timeout in seconds
253const FRAG_TIMEOUT: u32 = 60;
254
255/// Client trait for receiving 6lowpan frames.
256///
257/// Objects that implement this trait can set themselves to be the client
258/// for the [Sixlowpan](struct.Sixlowpan.html) struct, and will then receive
259/// a callback once an IPv6 packet has been fully reassembled.
260pub trait SixlowpanRxClient {
261    fn receive(&self, buf: &[u8], len: usize, result: Result<(), ErrorCode>);
262}
263
264pub mod lowpan_frag {
265    pub const FRAGN_HDR: u8 = 0b11100000;
266    pub const FRAG1_HDR: u8 = 0b11000000;
267    pub const FRAG1_HDR_SIZE: usize = 4;
268    pub const FRAGN_HDR_SIZE: usize = 5;
269}
270
271fn set_frag_hdr(
272    dgram_size: u16,
273    dgram_tag: u16,
274    dgram_offset: usize,
275    hdr: &mut [u8],
276    is_frag1: bool,
277) {
278    let mask = if is_frag1 {
279        lowpan_frag::FRAG1_HDR
280    } else {
281        lowpan_frag::FRAGN_HDR
282    };
283    u16_to_network_slice(dgram_size, &mut hdr[0..2]);
284    hdr[0] = mask | (hdr[0] & !mask);
285    u16_to_network_slice(dgram_tag, &mut hdr[2..4]);
286    if !is_frag1 {
287        hdr[4] = (dgram_offset / 8) as u8;
288    }
289}
290
291fn get_frag_hdr(hdr: &[u8]) -> (bool, u16, u16, usize) {
292    let is_frag1 = match hdr[0] & lowpan_frag::FRAGN_HDR {
293        lowpan_frag::FRAG1_HDR => true,
294        _ => false,
295    };
296    // Zero out upper bits
297    let dgram_size = network_slice_to_u16(&hdr[0..2]) & !(0xf << 12);
298    let dgram_tag = network_slice_to_u16(&hdr[2..4]);
299    let dgram_offset = if is_frag1 { 0 } else { hdr[4] };
300    (is_frag1, dgram_size, dgram_tag, (dgram_offset as usize) * 8)
301}
302
303fn is_fragment(packet: &[u8]) -> bool {
304    let mask = packet[0] & lowpan_frag::FRAGN_HDR;
305    (mask == lowpan_frag::FRAGN_HDR) || (mask == lowpan_frag::FRAG1_HDR)
306}
307
308pub trait SixlowpanState<'a> {
309    fn next_dgram_tag(&self) -> u16;
310    fn get_ctx_store(&self) -> &dyn ContextStore;
311    fn add_rx_state(&self, rx_state: &'a RxState<'a>);
312    fn set_rx_client(&'a self, client: &'a dyn SixlowpanRxClient);
313}
314
315/// Tracks the compression state for a single IPv6 packet.
316///
317/// When an upper layer is interested in sending a packet using Sixlowpan,
318/// they must first call `TxState.init`, which initializes the compression
319/// state for a new packet. The upper layer then repeatedly calls
320/// `TxState.next_fragment` until there are no more frames to compress.
321/// Note that the upper layer is responsible for sending the compressed
322/// frames; the `TxState` struct simply produces compressed MAC frames.
323pub struct TxState<'a> {
324    /// State for the current transmission
325    pub dst_pan: Cell<PanID>, // Pub to allow for setting to broadcast PAN and back
326    src_pan: Cell<PanID>,
327    src_mac_addr: Cell<MacAddress>,
328    dst_mac_addr: Cell<MacAddress>,
329    security: Cell<Option<(SecurityLevel, KeyId)>>,
330    dgram_tag: Cell<u16>, // Used to identify particular fragment streams
331    dgram_size: Cell<u16>,
332    dgram_offset: Cell<usize>,
333
334    busy: Cell<bool>,
335    // We need a reference to sixlowpan to compute and increment
336    // the global dgram_tag value
337    sixlowpan: &'a dyn SixlowpanState<'a>,
338}
339
340impl<'a> TxState<'a> {
341    /// Creates a new `TxState`
342    ///
343    /// # Arguments
344    ///
345    /// `sixlowpan` - A reference to a `SixlowpanState` object, which contains
346    /// global state for the entire Sixlowpan layer.
347    pub fn new(sixlowpan: &'a dyn SixlowpanState<'a>) -> TxState<'a> {
348        TxState {
349            // Externally settable fields
350            src_pan: Cell::new(0),
351            dst_pan: Cell::new(0),
352            src_mac_addr: Cell::new(MacAddress::Short(0)),
353            dst_mac_addr: Cell::new(MacAddress::Short(0)),
354            security: Cell::new(None),
355
356            // Internal fields
357            dgram_tag: Cell::new(0),
358            dgram_size: Cell::new(0),
359            dgram_offset: Cell::new(0),
360
361            busy: Cell::new(false),
362            sixlowpan,
363        }
364    }
365
366    /// Initializes `TxState` for a new packet
367    ///
368    /// # Arguments
369    ///
370    /// `src_mac_addr` - The MAC address the frame will be sent from
371    /// `dst_mac_addr` - The MAC address the frame will be sent to
372    /// `radio_pan` - The PAN ID held by the radio underlying this stack
373    /// `security` - Any security options (necessary since the size of the
374    /// produced MAC frame is dependent on the security options)
375    ///
376    /// # Return Value
377    ///
378    /// This function returns a `Result<(), ErrorCode>`, which indicates success or
379    /// failure. Note that if `init` has already been called and we are
380    /// currently sending a packet, this function will return
381    /// `Err(ErrorCode::BUSY)`
382    pub fn init(
383        &self,
384        src_mac_addr: MacAddress,
385        dst_mac_addr: MacAddress,
386        radio_pan: u16,
387        security: Option<(SecurityLevel, KeyId)>,
388    ) -> Result<(), ErrorCode> {
389        if self.busy.get() {
390            Err(ErrorCode::BUSY)
391        } else {
392            self.src_mac_addr.set(src_mac_addr);
393            self.dst_mac_addr.set(dst_mac_addr);
394            self.security.set(security);
395            self.busy.set(false);
396            self.src_pan.set(radio_pan);
397            self.dst_pan.set(radio_pan);
398            Ok(())
399        }
400    }
401
402    /// Gets the next 6LoWPAN Fragment (as a MAC frame) to be sent. Note that
403    /// this layer **does not** send the frame, and assumes that `init` has
404    /// already been called.
405    ///
406    /// # Arguments
407    ///
408    /// `ip6_packet` - A reference to the IPv6 packet to be compressed
409    /// `frag_buf` - The buffer to write the MAC frame to
410    /// `radio` - A reference to a MacDevice, which is used to prepare the
411    /// MAC frame
412    ///
413    /// # Return Value
414    ///
415    /// This function returns a `Result` type:
416    /// `Ok(bool, frame)` - If `Ok`, then `bool` indicates whether the
417    /// transmission is complete, and `Frame` is the filled out next MAC frame
418    /// `Err(Result<(), ErrorCode>, &'static mut [u8])` - If `Err`, then `Result<(), ErrorCode>`
419    /// is the reason for the error, and the return buffer is the (non-consumed)
420    /// `frag_buf` passed in as an argument
421    pub fn next_fragment<'b>(
422        &self,
423        ip6_packet: &'b IP6Packet<'b>,
424        frag_buf: &'static mut [u8],
425        radio: &dyn MacDevice,
426    ) -> Result<(bool, Frame), (Result<(), ErrorCode>, &'static mut [u8])> {
427        // This consumes frag_buf
428        let frame = radio
429            .prepare_data_frame(
430                frag_buf,
431                self.dst_pan.get(),
432                self.dst_mac_addr.get(),
433                self.src_pan.get(),
434                self.src_mac_addr.get(),
435                self.security.get(),
436            )
437            .map_err(|frame| (Err(ErrorCode::FAIL), frame))?;
438
439        // If this is the first fragment
440        if !self.busy.get() {
441            let frame = self.start_transmit(ip6_packet, frame, self.sixlowpan.get_ctx_store())?;
442            Ok((false, frame))
443        } else if self.is_transmit_done() {
444            self.end_transmit();
445            Ok((true, frame))
446        } else {
447            // Want the total datagram size we are sending to be less than
448            // the length of the packet - otherwise, we risk reading off the
449            // end of the array
450            if self.dgram_size.get() != ip6_packet.get_total_len() {
451                return Err((Err(ErrorCode::NOMEM), frame.into_buf()));
452            }
453
454            let frame = self.prepare_next_fragment(ip6_packet, frame)?;
455            Ok((false, frame))
456        }
457    }
458
459    fn is_transmit_done(&self) -> bool {
460        self.dgram_size.get() as usize <= self.dgram_offset.get()
461    }
462
463    // Frag_buf needs to be >= 802.15.4 MTU
464    // The radio takes frag_buf, consumes it, returns Frame or Error
465    fn start_transmit<'b>(
466        &self,
467        ip6_packet: &'b IP6Packet<'b>,
468        frame: Frame,
469        ctx_store: &dyn ContextStore,
470    ) -> Result<Frame, (Result<(), ErrorCode>, &'static mut [u8])> {
471        self.busy.set(true);
472        self.dgram_size.set(ip6_packet.get_total_len());
473        self.dgram_tag.set(self.sixlowpan.next_dgram_tag());
474        self.prepare_first_fragment(ip6_packet, frame, ctx_store)
475    }
476
477    fn prepare_first_fragment<'b>(
478        &self,
479        ip6_packet: &'b IP6Packet<'b>,
480        mut frame: Frame,
481        ctx_store: &dyn ContextStore,
482    ) -> Result<Frame, (Result<(), ErrorCode>, &'static mut [u8])> {
483        // Here, we assume that the compressed headers fit in the first MTU
484        // fragment. This is consistent with RFC 6282.
485        let mut lowpan_packet = [0_u8; radio::MAX_FRAME_SIZE];
486        let (consumed, written) = {
487            match sixlowpan_compression::compress(
488                ctx_store,
489                ip6_packet,
490                self.src_mac_addr.get(),
491                self.dst_mac_addr.get(),
492                &mut lowpan_packet,
493            ) {
494                Err(()) => return Err((Err(ErrorCode::FAIL), frame.into_buf())),
495                Ok(result) => result,
496            }
497        };
498
499        let remaining_payload = ip6_packet.get_total_len() as usize - consumed;
500        let lowpan_len = written + remaining_payload;
501
502        // TODO: This -2 is added to account for the FCS; this should be changed
503        // in the MAC code
504        let mut remaining_capacity = frame.remaining_data_capacity() - 2;
505
506        // Need to fragment
507        if lowpan_len > remaining_capacity {
508            remaining_capacity -= self.write_frag_hdr(&mut frame, true);
509        }
510
511        // Write the 6lowpan header
512        if written <= remaining_capacity {
513            // TODO: Check success
514            let _ = frame.append_payload(&lowpan_packet[0..written]);
515            remaining_capacity -= written;
516        } else {
517            return Err((Err(ErrorCode::SIZE), frame.into_buf()));
518        }
519
520        // Write the remainder of the payload, rounding down to a multiple
521        // of 8 if the entire payload won't fit
522        let payload_len = if remaining_payload > remaining_capacity {
523            remaining_capacity & !0b111
524        } else {
525            remaining_payload
526        };
527        // TODO: Check success
528        let (payload_len, consumed) =
529            self.write_additional_headers(ip6_packet, &mut frame, consumed, payload_len);
530
531        let _ = frame.append_payload(&ip6_packet.get_payload()[0..payload_len]);
532        self.dgram_offset.set(consumed + payload_len);
533        Ok(frame)
534    }
535
536    fn prepare_next_fragment<'b>(
537        &self,
538        ip6_packet: &'b IP6Packet<'b>,
539        mut frame: Frame,
540    ) -> Result<Frame, (Result<(), ErrorCode>, &'static mut [u8])> {
541        let dgram_offset = self.dgram_offset.get();
542        let mut remaining_capacity = frame.remaining_data_capacity();
543        remaining_capacity -= self.write_frag_hdr(&mut frame, false);
544
545        // This rounds payload_len down to the nearest multiple of 8 if it
546        // is not the last fragment (per RFC 4944)
547        let remaining_payload = (self.dgram_size.get() as usize) - dgram_offset;
548        let payload_len = if remaining_payload > remaining_capacity {
549            remaining_capacity & !0b111
550        } else {
551            remaining_payload
552        };
553
554        let (payload_len, dgram_offset) =
555            self.write_additional_headers(ip6_packet, &mut frame, dgram_offset, payload_len);
556
557        if payload_len > 0 {
558            let payload_offset = dgram_offset - ip6_packet.get_total_hdr_size();
559            let _ = frame.append_payload(
560                &ip6_packet.get_payload()[payload_offset..payload_offset + payload_len],
561            );
562        }
563
564        // Update the offset to be used for the next fragment
565        self.dgram_offset.set(dgram_offset + payload_len);
566        Ok(frame)
567    }
568
569    // NOTE: This function will not work for headers that span past the first
570    // frame.
571    fn write_additional_headers<'b>(
572        &self,
573        ip6_packet: &'b IP6Packet<'b>,
574        frame: &mut Frame,
575        dgram_offset: usize,
576        payload_len: usize,
577    ) -> (usize, usize) {
578        let total_hdr_len = ip6_packet.get_total_hdr_size();
579        let mut payload_len = payload_len;
580        let mut dgram_offset = dgram_offset;
581        if total_hdr_len > dgram_offset {
582            let headers_to_write = min(payload_len, total_hdr_len - dgram_offset);
583            // TODO: Note that in order to serialize the headers, we need to
584            // statically allocate room on the stack. However, we do not know
585            // how many additional headers we have until runtime. This
586            // functionality should be fixed in the future.
587            let mut headers = [0_u8; 60];
588            ip6_packet.encode(&mut headers);
589            let _ = frame.append_payload(&headers[dgram_offset..dgram_offset + headers_to_write]);
590            payload_len -= headers_to_write;
591            dgram_offset += headers_to_write;
592        }
593        (payload_len, dgram_offset)
594    }
595
596    fn write_frag_hdr(&self, frame: &mut Frame, first_frag: bool) -> usize {
597        if first_frag {
598            let mut frag_header = [0_u8; lowpan_frag::FRAG1_HDR_SIZE];
599            set_frag_hdr(
600                self.dgram_size.get(),
601                self.dgram_tag.get(),
602                /*offset = */
603                0,
604                &mut frag_header,
605                true,
606            );
607            // TODO: Check success
608            let _ = frame.append_payload(&frag_header);
609            lowpan_frag::FRAG1_HDR_SIZE
610        } else {
611            let mut frag_header = [0_u8; lowpan_frag::FRAGN_HDR_SIZE];
612            set_frag_hdr(
613                self.dgram_size.get(),
614                self.dgram_tag.get(),
615                self.dgram_offset.get(),
616                &mut frag_header,
617                first_frag,
618            );
619            // TODO: Check success
620            let _ = frame.append_payload(&frag_header);
621            lowpan_frag::FRAGN_HDR_SIZE
622        }
623    }
624
625    fn end_transmit(&self) {
626        self.busy.set(false);
627    }
628}
629
630/// Tracks the decompression and defragmentation of an IPv6 packet
631///
632/// A list of `RxState`s is maintained by [Sixlowpan](struct.Sixlowpan.html) to
633/// keep track of ongoing packet reassemblies. The number of `RxState`s is the
634/// number of packets that can be reassembled at the same time. Generally,
635/// two `RxState`s are sufficient for normal-case operation.
636pub struct RxState<'a> {
637    packet: TakeCell<'static, [u8]>,
638    bitmap: MapCell<Bitmap>,
639    dst_mac_addr: Cell<MacAddress>,
640    src_mac_addr: Cell<MacAddress>,
641    dgram_tag: Cell<u16>,
642    dgram_size: Cell<u16>,
643    // Marks if this instance is being used for a packet reassembly or if it is
644    // free to use for a new packet.
645    busy: Cell<bool>,
646    // The time when packet reassembly started for the current packet.
647    start_time: Cell<u32>,
648
649    next: ListLink<'a, RxState<'a>>,
650}
651
652impl<'a> ListNode<'a, RxState<'a>> for RxState<'a> {
653    fn next(&'a self) -> &'a ListLink<'a, RxState<'a>> {
654        &self.next
655    }
656}
657
658impl<'a> RxState<'a> {
659    /// Creates a new `RxState`
660    ///
661    /// # Arguments
662    ///
663    /// `packet` - A buffer for reassembling an IPv6 packet. Currently, we
664    /// assume this to be 1280 bytes long (the minimum IPv6 MTU size).
665    pub fn new(packet: &'static mut [u8]) -> RxState<'a> {
666        RxState {
667            packet: TakeCell::new(packet),
668            bitmap: MapCell::new(Bitmap::new()),
669            dst_mac_addr: Cell::new(MacAddress::Short(0)),
670            src_mac_addr: Cell::new(MacAddress::Short(0)),
671            dgram_tag: Cell::new(0),
672            dgram_size: Cell::new(0),
673            busy: Cell::new(false),
674            start_time: Cell::new(0),
675            next: ListLink::empty(),
676        }
677    }
678
679    fn is_my_fragment(
680        &self,
681        src_mac_addr: MacAddress,
682        dst_mac_addr: MacAddress,
683        dgram_size: u16,
684        dgram_tag: u16,
685    ) -> bool {
686        self.busy.get()
687            && (self.dgram_tag.get() == dgram_tag)
688            && (self.dgram_size.get() == dgram_size)
689            && (self.src_mac_addr.get() == src_mac_addr)
690            && (self.dst_mac_addr.get() == dst_mac_addr)
691    }
692
693    // Checks if a given RxState is free or expired (and thus, can be freed).
694    // This function implements the reassembly timeout for 6LoWPAN lazily.
695    fn is_busy(&self, frequency: u32, current_time: u32) -> bool {
696        let expired = current_time >= (self.start_time.get() + FRAG_TIMEOUT * frequency);
697        if expired {
698            self.end_receive(None, Err(ErrorCode::FAIL));
699        }
700        self.busy.get()
701    }
702
703    fn start_receive(
704        &self,
705        src_mac_addr: MacAddress,
706        dst_mac_addr: MacAddress,
707        dgram_size: u16,
708        dgram_tag: u16,
709        current_tics: u32,
710    ) {
711        self.dst_mac_addr.set(dst_mac_addr);
712        self.src_mac_addr.set(src_mac_addr);
713        self.dgram_tag.set(dgram_tag);
714        self.dgram_size.set(dgram_size);
715        self.busy.set(true);
716        self.bitmap.map(|bitmap| bitmap.clear());
717        self.start_time.set(current_tics);
718    }
719
720    // This function assumes that the payload is a slice starting from the
721    // actual payload (no 802.15.4 headers, no fragmentation headers), and
722    // returns true if the packet is completely reassembled.
723    fn receive_next_frame(
724        &self,
725        payload: &[u8],
726        payload_len: usize,
727        dgram_size: u16,
728        dgram_offset: usize,
729        ctx_store: &dyn ContextStore,
730    ) -> Result<bool, Result<(), ErrorCode>> {
731        let packet = self.packet.take().ok_or(Err(ErrorCode::NOMEM))?;
732        let uncompressed_len = if dgram_offset == 0 {
733            let (consumed, written) = sixlowpan_compression::decompress(
734                ctx_store,
735                &payload[0..payload_len],
736                self.src_mac_addr.get(),
737                self.dst_mac_addr.get(),
738                packet,
739                dgram_size,
740                true,
741            )
742            .map_err(|()| Err(ErrorCode::FAIL))?;
743            let remaining = payload_len - consumed;
744            packet[written..written + remaining]
745                .copy_from_slice(&payload[consumed..consumed + remaining]);
746            written + remaining
747        } else {
748            packet[dgram_offset..dgram_offset + payload_len]
749                .copy_from_slice(&payload[0..payload_len]);
750            payload_len
751        };
752        self.packet.replace(packet);
753        if !self.bitmap.map_or(false, |bitmap| {
754            bitmap.set_bits(dgram_offset / 8, (dgram_offset + uncompressed_len) / 8)
755        }) {
756            // If this fails, we received an overlapping fragment. We can simply
757            // drop the packet in this case.
758            Err(Err(ErrorCode::FAIL))
759        } else {
760            self.bitmap
761                .map(|bitmap| bitmap.is_complete((dgram_size as usize) / 8))
762                .ok_or(Err(ErrorCode::FAIL))
763        }
764    }
765
766    fn end_receive(
767        &self,
768        client: Option<&'a dyn SixlowpanRxClient>,
769        result: Result<(), ErrorCode>,
770    ) {
771        self.busy.set(false);
772        self.bitmap.map(|bitmap| bitmap.clear());
773        self.start_time.set(0);
774        client.map(move |client| {
775            // Since packet is borrowed from the upper layer, failing to return it
776            // in the callback represents a significant error that should never
777            // occur - all other calls to `packet.take()` replace the packet,
778            // and thus the packet should always be here.
779            self.packet
780                .map(|packet| {
781                    client.receive(packet, self.dgram_size.get() as usize, result);
782                })
783                .unwrap(); // Unwrap fail = Error: `packet` is None in call to end_receive.
784        });
785    }
786}
787
788/// Sends a receives IPv6 packets via 6loWPAN compression and fragmentation.
789///
790/// # Initialization
791///
792/// The `new` method creates an instance of `Sixlowpan` that can send packets.
793/// To receive packets, `Sixlowpan` needs one or more
794/// [RxState](struct.RxState.html)s which can be added with `add_rx_state`. More
795/// [RxState](struct.RxState.html)s allow the `Sixlowpan` to receive more
796/// packets concurrently.
797///
798/// Finally, `set_client` controls the client that will receive transmission
799/// completion and reception callbacks.
800pub struct Sixlowpan<'a, A: time::Alarm<'a>, C: ContextStore> {
801    pub ctx_store: C,
802    clock: &'a A,
803    tx_dgram_tag: Cell<u16>,
804    rx_client: Cell<Option<&'a dyn SixlowpanRxClient>>,
805
806    // Receive state
807    rx_states: List<'a, RxState<'a>>,
808}
809
810// This function is called after receiving a frame
811impl<'a, A: time::Alarm<'a>, C: ContextStore> RxClient for Sixlowpan<'a, A, C> {
812    fn receive<'b>(
813        &self,
814        buf: &'b [u8],
815        header: Header<'b>,
816        _lqi: u8,
817        data_offset: usize,
818        data_len: usize,
819    ) {
820        // We return if retcode is not valid, as it does not make sense to issue
821        // a callback for an invalid frame reception
822        // TODO: Handle the case where the addresses are None/elided - they
823        // should not default to the zero address
824        let src_mac_addr = header.src_addr.unwrap_or(MacAddress::Short(0));
825        let dst_mac_addr = header.dst_addr.unwrap_or(MacAddress::Short(0));
826
827        let (rx_state, returncode) = self.receive_frame(
828            &buf[data_offset..data_offset + data_len],
829            data_len,
830            src_mac_addr,
831            dst_mac_addr,
832        );
833        // Reception completed if rx_state is not None. Note that this can
834        // also occur for some fail states (e.g. dropping an invalid packet)
835        rx_state.map(|state| state.end_receive(self.rx_client.get(), returncode));
836    }
837}
838
839impl<'a, A: time::Alarm<'a>, C: ContextStore> SixlowpanState<'a> for Sixlowpan<'a, A, C> {
840    fn next_dgram_tag(&self) -> u16 {
841        // Increment dgram_tag
842        let dgram_tag = if (self.tx_dgram_tag.get() + 1) == 0 {
843            1
844        } else {
845            self.tx_dgram_tag.get() + 1
846        };
847        self.tx_dgram_tag.set(dgram_tag);
848        dgram_tag
849    }
850
851    fn get_ctx_store(&self) -> &dyn ContextStore {
852        &self.ctx_store
853    }
854
855    /// Adds an additional `RxState` for reassembling IPv6 packets
856    ///
857    /// Each [RxState](struct.RxState.html) struct allows an additional IPv6
858    /// packet to be reassembled concurrently.
859    fn add_rx_state(&self, rx_state: &'a RxState<'a>) {
860        self.rx_states.push_head(rx_state);
861    }
862
863    /// Sets the [SixlowpanClient](trait.SixlowpanClient.html) that will receive
864    /// transmission completion and new packet reception callbacks.
865    fn set_rx_client(&'a self, client: &'a dyn SixlowpanRxClient) {
866        self.rx_client.set(Some(client));
867    }
868}
869
870impl<'a, A: time::Alarm<'a>, C: ContextStore> Sixlowpan<'a, A, C> {
871    /// Creates a new `Sixlowpan`
872    ///
873    /// # Arguments
874    ///
875    /// * `ctx_store` - Stores IPv6 address nextwork context mappings
876    ///
877    /// * `tx_buf` - A buffer used for storing individual fragments of a packet
878    /// in transmission. This buffer must be at least the length of an 802.15.4
879    /// frame.
880    ///
881    /// * `clock` - A implementation of `Alarm` used for tracking the timing of
882    /// frame arrival. The clock should be continue running during sleep and
883    /// have an accuracy of at least 60 seconds.
884    pub fn new(ctx_store: C, clock: &'a A) -> Sixlowpan<'a, A, C> {
885        Sixlowpan {
886            ctx_store,
887            clock,
888            tx_dgram_tag: Cell::new(0),
889            rx_client: Cell::new(None),
890
891            rx_states: List::new(),
892        }
893    }
894
895    fn receive_frame(
896        &self,
897        packet: &[u8],
898        packet_len: usize,
899        src_mac_addr: MacAddress,
900        dst_mac_addr: MacAddress,
901    ) -> (Option<&RxState<'a>>, Result<(), ErrorCode>) {
902        if is_fragment(packet) {
903            let (is_frag1, dgram_size, dgram_tag, dgram_offset) = get_frag_hdr(&packet[0..5]);
904            let offset_to_payload = if is_frag1 {
905                lowpan_frag::FRAG1_HDR_SIZE
906            } else {
907                lowpan_frag::FRAGN_HDR_SIZE
908            };
909            self.receive_fragment(
910                &packet[offset_to_payload..],
911                packet_len - offset_to_payload,
912                src_mac_addr,
913                dst_mac_addr,
914                dgram_size,
915                dgram_tag,
916                dgram_offset,
917            )
918        } else {
919            self.receive_single_packet(packet, packet_len, src_mac_addr, dst_mac_addr)
920        }
921    }
922
923    fn receive_single_packet(
924        &self,
925        payload: &[u8],
926        payload_len: usize,
927        src_mac_addr: MacAddress,
928        dst_mac_addr: MacAddress,
929    ) -> (Option<&RxState<'a>>, Result<(), ErrorCode>) {
930        let rx_state = self
931            .rx_states
932            .iter()
933            .find(|state| !state.is_busy(self.clock.now().into_u32(), A::Frequency::frequency()));
934        rx_state.map_or((None, Err(ErrorCode::NOMEM)), |state| {
935            state.start_receive(
936                src_mac_addr,
937                dst_mac_addr,
938                payload_len as u16,
939                0,
940                self.clock.now().into_u32(),
941            );
942            // The packet buffer should *always* be there; in particular,
943            // since this state is not busy, it must have the packet buffer.
944            // Otherwise, we are in an inconsistent state and can fail.
945            let packet = state.packet.take().unwrap();
946
947            // Filter non 6LoWPAN packets and return
948            if !is_lowpan(payload) {
949                return (None, Ok(()));
950            }
951
952            let decompressed = sixlowpan_compression::decompress(
953                &self.ctx_store,
954                &payload[0..payload_len],
955                src_mac_addr,
956                dst_mac_addr,
957                packet,
958                0,
959                false,
960            );
961            match decompressed {
962                Ok((consumed, written)) => {
963                    let remaining = payload_len - consumed;
964                    packet[written..written + remaining]
965                        .copy_from_slice(&payload[consumed..consumed + remaining]);
966                    // Want dgram_size to contain decompressed size of packet
967                    state.dgram_size.set((written + remaining) as u16);
968                }
969                Err(()) => {
970                    return (None, Err(ErrorCode::FAIL));
971                }
972            }
973
974            state.packet.replace(packet);
975            (Some(state), Ok(()))
976        })
977    }
978
979    // This function returns an Err if an error occurred, returns Ok(Some(RxState))
980    // if the packet has been fully reassembled, or returns Ok(None) if there
981    // are still pending fragments
982    fn receive_fragment(
983        &self,
984        frag_payload: &[u8],
985        payload_len: usize,
986        src_mac_addr: MacAddress,
987        dst_mac_addr: MacAddress,
988        dgram_size: u16,
989        dgram_tag: u16,
990        dgram_offset: usize,
991    ) -> (Option<&RxState<'a>>, Result<(), ErrorCode>) {
992        // First try to find an rx_state in the middle of assembly
993        let mut rx_state = self
994            .rx_states
995            .iter()
996            .find(|state| state.is_my_fragment(src_mac_addr, dst_mac_addr, dgram_size, dgram_tag));
997
998        // Else find a free state
999        if rx_state.is_none() {
1000            rx_state = self.rx_states.iter().find(|state| {
1001                !state.is_busy(self.clock.now().into_u32(), A::Frequency::frequency())
1002            });
1003            // Initialize new state
1004            rx_state.map(|state| {
1005                state.start_receive(
1006                    src_mac_addr,
1007                    dst_mac_addr,
1008                    dgram_size,
1009                    dgram_tag,
1010                    self.clock.now().into_u32(),
1011                )
1012            });
1013            if rx_state.is_none() {
1014                return (None, Err(ErrorCode::NOMEM));
1015            }
1016        }
1017        rx_state.map_or((None, Err(ErrorCode::NOMEM)), |state| {
1018            // Returns true if the full packet is reassembled
1019            let res = state.receive_next_frame(
1020                frag_payload,
1021                payload_len,
1022                dgram_size,
1023                dgram_offset,
1024                &self.ctx_store,
1025            );
1026            match res {
1027                // Some error occurred
1028                Err(_) => (Some(state), Err(ErrorCode::FAIL)),
1029                Ok(complete) => {
1030                    if complete {
1031                        // Packet fully reassembled
1032                        (Some(state), Ok(()))
1033                    } else {
1034                        // Packet not fully reassembled
1035                        (None, Ok(()))
1036                    }
1037                }
1038            }
1039        })
1040    }
1041
1042    #[allow(dead_code)]
1043    // TODO: This code is currently unimplemented
1044    // This function is called when a disassociation event occurs, as we need
1045    // to expire all pending state.
1046    fn discard_all_state(&self) {
1047        for rx_state in self.rx_states.iter() {
1048            rx_state.end_receive(None, Err(ErrorCode::FAIL));
1049        }
1050        unimplemented!();
1051        // TODO: Need to get buffer back from Mac layer on disassociation
1052    }
1053}