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}