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}