capsules_extra/net/ipv6/
ipv6_send.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//! This file contains the interface definition for sending an IPv6 packet.
6//!
7//! The [IP6Sender](trait.IP6Sender.html) trait provides an interface
8//! for sending IPv6 packets, while the [IP6SendClient](trait.IP6SendClient) trait
9//! must be implemented by upper layers to receive the `send_done` callback
10//! when a transmission has completed.
11//!
12//! This file also includes an implementation of the `IP6Sender` trait, which
13//! sends an IPv6 packet using 6LoWPAN.
14
15// Additional Work and Known Problems
16// ----------------------------------
17// The main areas for additional work is with regards to the interface provided
18// by `IP6Sender`. The current interface differs from the one provided in
19// the networking stack overview document, and should be changed to better
20// reflect that document. Additionally, the specific implementation is
21// over 6LoWPAN, and should be separated from the generic IPv6 sending
22// interface.
23
24use crate::ieee802154::device::{MacDevice, TxClient};
25use crate::net::ieee802154::MacAddress;
26use crate::net::ipv6::ip_utils::IPAddr;
27use crate::net::ipv6::{IP6Header, IP6Packet, TransportHeader};
28use crate::net::network_capabilities::{IpVisibilityCapability, NetworkCapability};
29use crate::net::sixlowpan::sixlowpan_state::TxState;
30use crate::net::thread::thread_utils::{mac_from_ipv6, MULTICAST_IPV6};
31
32use core::cell::Cell;
33
34use kernel::debug;
35use kernel::hil::time::{self, ConvertTicks};
36use kernel::utilities::cells::{OptionalCell, TakeCell};
37use kernel::utilities::leasable_buffer::SubSliceMut;
38use kernel::ErrorCode;
39
40/// Client trait for receiving transmission completiong events.
41///
42/// The upper layer must then call `IP6Sender.set_client` in order to
43/// receive this callback.
44pub trait IP6SendClient {
45    fn send_done(&self, result: Result<(), ErrorCode>);
46}
47
48/// Provides a basic IPv6 sending interface.
49///
50/// It exposes basic configuration information for the IPv6 layer
51/// (setting the source address, setting the gateway MAC address), as
52/// well as a way to send an IPv6 packet.
53pub trait IP6Sender<'a> {
54    /// This method sets the `IP6SendClient` for the `IP6Sender` instance, which
55    /// receives the `send_done` callback when transmission has finished.
56    ///
57    /// # Arguments
58    /// `client` - Client that implements the `IP6SendClient` trait to receive the
59    /// `send_done` callback
60    fn set_client(&self, client: &'a dyn IP6SendClient);
61
62    /// This method sets the source address for packets sent from the
63    /// `IP6Sender` instance.
64    ///
65    /// # Arguments
66    /// `src_addr` - `IPAddr` to set as the source address for packets sent
67    /// from this instance of `IP6Sender`
68    fn set_addr(&self, src_addr: IPAddr);
69
70    /// This method sets the gateway/next hop MAC address for this `IP6Sender`
71    /// instance.
72    ///
73    /// # Arguments
74    /// `gateway` - MAC address to send the constructed packet to
75    fn set_gateway(&self, gateway: MacAddress);
76
77    /// This method sets the `IP6Header` for the `IP6Sender` instance
78    ///
79    /// # Arguments
80    /// `ip6_header` - New `IP6Header` that subsequent packets sent via this
81    /// `IP6Sender` instance will use
82    fn set_header(&mut self, ip6_header: IP6Header);
83
84    /// This method sends the provided transport header and payload to the
85    /// given destination IP address
86    ///
87    /// # Arguments
88    /// `dst` - IPv6 address to send the packet to
89    /// `transport_header` - The `TransportHeader` for the packet being sent
90    /// `payload` - The transport payload for the packet being sent
91    fn send_to(
92        &self,
93        dst: IPAddr,
94        transport_header: TransportHeader,
95        payload: &SubSliceMut<'static, u8>,
96        net_cap: &'static NetworkCapability,
97    ) -> Result<(), ErrorCode>;
98}
99
100/// This struct is a specific implementation of the `IP6Sender` trait. This
101/// struct sends the packet using 6LoWPAN over a generic `MacDevice` object.
102pub struct IP6SendStruct<'a, A: time::Alarm<'a>> {
103    // We want the ip6_packet field to be a TakeCell so that it is easy to mutate
104    ip6_packet: TakeCell<'static, IP6Packet<'static>>,
105    alarm: &'a A, // Alarm so we can introduce a small delay between fragments to ensure
106    // successful reception on receivers with slow copies out of the radio buffer
107    // (imix)
108    src_addr: Cell<IPAddr>,
109    gateway: Cell<MacAddress>,
110    tx_buf: TakeCell<'static, [u8]>,
111    sixlowpan: TxState<'a>,
112    radio: &'a dyn MacDevice<'a>,
113    dst_mac_addr: MacAddress,
114    src_mac_addr: MacAddress,
115    client: OptionalCell<&'a dyn IP6SendClient>,
116    ip_vis: &'static IpVisibilityCapability,
117}
118
119impl<'a, A: time::Alarm<'a>> IP6Sender<'a> for IP6SendStruct<'a, A> {
120    fn set_client(&self, client: &'a dyn IP6SendClient) {
121        self.client.set(client);
122    }
123
124    fn set_addr(&self, src_addr: IPAddr) {
125        self.src_addr.set(src_addr);
126    }
127
128    fn set_gateway(&self, gateway: MacAddress) {
129        self.gateway.set(gateway);
130    }
131
132    fn set_header(&mut self, ip6_header: IP6Header) {
133        self.ip6_packet
134            .map(|ip6_packet| ip6_packet.header = ip6_header);
135    }
136
137    fn send_to(
138        &self,
139        dst: IPAddr,
140        transport_header: TransportHeader,
141        payload: &SubSliceMut<'static, u8>,
142        net_cap: &'static NetworkCapability,
143    ) -> Result<(), ErrorCode> {
144        if !net_cap.remote_addr_valid(dst, self.ip_vis) {
145            return Err(ErrorCode::FAIL);
146        }
147
148        // This logic is used to update the dst mac address
149        // the given packet should be sent to. This complies
150        // with the manner in which Thread addresses packets,
151        // but may conflict with some other or future protocol
152        // that sits above and uses IPV6
153        let dst_mac_addr;
154        if dst == MULTICAST_IPV6 {
155            // use short multicast ipv6 for dst mac address
156            dst_mac_addr = MacAddress::Short(0xFFFF)
157        } else if dst.0[0..8] == [0xfe, 0x80, 0, 0, 0, 0, 0, 0] {
158            // ipv6 address is of form fe80::MAC; use mac_from_ipv6
159            // helper function to determine ipv6 to send to
160            dst_mac_addr = MacAddress::Long(mac_from_ipv6(dst))
161        } else {
162            dst_mac_addr = self.dst_mac_addr;
163        }
164
165        // TODO: add error handling here
166        let _ = self
167            .sixlowpan
168            .init(self.src_mac_addr, dst_mac_addr, self.radio.get_pan(), None);
169
170        self.init_packet(dst, transport_header, payload);
171        let ret = self.send_next_fragment();
172        ret
173    }
174}
175
176impl<'a, A: time::Alarm<'a>> IP6SendStruct<'a, A> {
177    pub fn new(
178        ip6_packet: &'static mut IP6Packet<'static>,
179        alarm: &'a A,
180        tx_buf: &'static mut [u8],
181        sixlowpan: TxState<'a>,
182        radio: &'a dyn MacDevice<'a>,
183        dst_mac_addr: MacAddress,
184        src_mac_addr: MacAddress,
185        ip_vis: &'static IpVisibilityCapability,
186    ) -> IP6SendStruct<'a, A> {
187        IP6SendStruct {
188            ip6_packet: TakeCell::new(ip6_packet),
189            alarm,
190            src_addr: Cell::new(IPAddr::new()),
191            gateway: Cell::new(dst_mac_addr),
192            tx_buf: TakeCell::new(tx_buf),
193            sixlowpan,
194            radio,
195            dst_mac_addr,
196            src_mac_addr,
197            client: OptionalCell::empty(),
198            ip_vis,
199        }
200    }
201
202    fn init_packet(
203        &self,
204        dst_addr: IPAddr,
205        transport_header: TransportHeader,
206        payload: &SubSliceMut<'static, u8>,
207    ) {
208        self.ip6_packet.map_or_else(
209            || {
210                debug!("init packet failed.");
211            },
212            |ip6_packet| {
213                ip6_packet.header = IP6Header::default();
214                ip6_packet.header.src_addr = self.src_addr.get();
215                ip6_packet.header.dst_addr = dst_addr;
216                ip6_packet.set_payload(transport_header, payload);
217                ip6_packet.set_transport_checksum();
218            },
219        );
220    }
221
222    // Returns BUSY if the tx_buf is not there
223    fn send_next_fragment(&self) -> Result<(), ErrorCode> {
224        // Originally send_complete() was called within the below closure.
225        // However, this led to a race condition where when multiple apps transmitted
226        // simultaneously, it was possible for send_complete to trigger another
227        // transmission before the below closure would exit, leading to this function
228        // being called again by another app before ip6_packet is replaced.
229        // To fix this, we pass a bool out of the closure to indicate whether send_completed()
230        // should be called once the closure exits
231        let (ret, call_send_complete) = self
232            .ip6_packet
233            .map(move |ip6_packet| match self.tx_buf.take() {
234                Some(tx_buf) => {
235                    let next_frame = self.sixlowpan.next_fragment(ip6_packet, tx_buf, self.radio);
236                    match next_frame {
237                        Ok((is_done, frame)) => {
238                            if is_done {
239                                self.tx_buf.replace(frame.into_buf());
240                                //self.send_completed(Ok(()));
241                                (Ok(()), true)
242                            } else {
243                                match self.radio.transmit(frame) {
244                                    Ok(()) => (Ok(()), false),
245                                    Err((ecode, _buf)) => (Err(ecode), false),
246                                }
247                            }
248                        }
249                        Err((retcode, buf)) => {
250                            self.tx_buf.replace(buf);
251                            //self.send_completed(retcode);
252                            (retcode, true)
253                        }
254                    }
255                }
256                None => {
257                    debug!("Missing tx_buf");
258                    (Err(ErrorCode::BUSY), false)
259                }
260            })
261            .unwrap_or((Err(ErrorCode::NOMEM), false));
262        if call_send_complete {
263            self.send_completed(ret);
264            return Ok(());
265        }
266        ret
267    }
268
269    fn send_completed(&self, result: Result<(), ErrorCode>) {
270        self.client.map(move |client| {
271            client.send_done(result);
272        });
273    }
274}
275
276impl<'a, A: time::Alarm<'a>> time::AlarmClient for IP6SendStruct<'a, A> {
277    fn alarm(&self) {
278        let result = self.send_next_fragment();
279        if result != Ok(()) {
280            self.send_completed(result);
281        }
282    }
283}
284
285impl<'a, A: time::Alarm<'a>> TxClient for IP6SendStruct<'a, A> {
286    fn send_done(&self, tx_buf: &'static mut [u8], acked: bool, result: Result<(), ErrorCode>) {
287        self.tx_buf.replace(tx_buf);
288        if result != Ok(()) {
289            debug!("Send Failed: {:?}, acked: {}", result, acked);
290            self.client.map(move |client| {
291                client.send_done(result);
292            });
293        } else {
294            // Below code adds delay between fragments. Despite some efforts
295            // to fix this bug, I find that without it the receiving imix cannot
296            // receive more than 2 fragments in a single packet without hanging
297            // waiting for the third fragments.
298            // Specifically, here we set a timer, which fires and sends the next fragment
299            // One flaw with this is that we also introduce a delay after sending the last
300            // fragment, before passing the send_done callback back to the client. This
301            // could be optimized by checking if it is the last fragment before setting the timer.
302            self.alarm
303                .set_alarm(self.alarm.now(), self.alarm.ticks_from_ms(100));
304        }
305    }
306}