components/
udp_mux.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//! Component to initialize the udp/6lowpan interface.
6//!
7//! This provides one Component, UDPMuxComponent. This component
8//! exposes a MuxUdpSender that other components can implement
9//! UDPSenders on top of to use the UDP/6Lowpan stack.
10//!
11//! Usage
12//! -----
13//! ```rust
14//!    let (udp_mux, udp_recv) = UDPMuxComponent::new(
15//!        mux_mac,
16//!        DEFAULT_CTX_PREFIX_LEN,
17//!        DEFAULT_CTX_PREFIX,
18//!        DST_MAC_ADDR,
19//!        src_mac_from_serial_num,
20//!        local_ip_ifaces,
21//!        mux_alarm,
22//!        MAX_PAYLOAD_LEN,
23//!    )
24//!    .finalize(components::udp_mux_component_static!());
25//! ```
26
27// Author: Hudson Ayers <hayers@stanford.edu>
28// Last Modified: 5/21/2019
29
30use capsules_core::virtualizers::virtual_alarm::{MuxAlarm, VirtualMuxAlarm};
31use capsules_extra::ieee802154::device::MacDevice;
32use capsules_extra::net::ieee802154::MacAddress;
33use capsules_extra::net::ipv6::ip_utils::IPAddr;
34use capsules_extra::net::ipv6::ipv6_recv::IP6Receiver;
35use capsules_extra::net::ipv6::ipv6_recv::IP6RecvStruct;
36use capsules_extra::net::ipv6::ipv6_send::IP6SendStruct;
37use capsules_extra::net::ipv6::ipv6_send::IP6Sender;
38use capsules_extra::net::ipv6::{IP6Packet, IPPayload, TransportHeader};
39use capsules_extra::net::network_capabilities::{IpVisibilityCapability, UdpVisibilityCapability};
40use capsules_extra::net::sixlowpan::{sixlowpan_compression, sixlowpan_state};
41use capsules_extra::net::udp::udp_port_table::{
42    SocketBindingEntry, UdpPortManager, MAX_NUM_BOUND_PORTS,
43};
44use capsules_extra::net::udp::udp_recv::MuxUdpReceiver;
45use capsules_extra::net::udp::udp_send::MuxUdpSender;
46use capsules_extra::net::udp::UDPHeader;
47use core::mem::MaybeUninit;
48use kernel::capabilities;
49use kernel::component::Component;
50use kernel::create_capability;
51use kernel::hil::radio;
52use kernel::hil::time::Alarm;
53
54// The UDP stack requires several packet buffers:
55//
56//   1. RADIO_BUF: buffer the IP6_Sender uses to pass frames to the radio after fragmentation
57//   2. SIXLOWPAN_RX_BUF: Buffer to hold full IP packets after they are decompressed by 6LoWPAN
58//   3. UDP_DGRAM: The payload of the IP6_Packet, which holds full IP Packets before they are tx'd.
59//
60//   Additionally, every capsule using the stack needs an additional buffer to craft packets for
61//   tx which can then be passed to the MuxUdpSender for tx.
62
63pub const MAX_PAYLOAD_LEN: usize = 200; //The max size UDP message that can be sent by userspace apps or capsules
64
65// Setup static space for the objects.
66#[macro_export]
67macro_rules! udp_mux_component_static {
68    ($A:ty, $M:ty $(,)?) => {{
69        use capsules_core;
70        use capsules_core::virtualizers::virtual_alarm::{MuxAlarm, VirtualMuxAlarm};
71        use capsules_extra::net::sixlowpan::{sixlowpan_compression, sixlowpan_state};
72        use capsules_extra::net::udp::udp_send::MuxUdpSender;
73        use components::udp_mux::MAX_PAYLOAD_LEN;
74        use core::mem::MaybeUninit;
75
76        let alarm = kernel::static_buf!(VirtualMuxAlarm<'static, $A>);
77        let mac_user =
78            kernel::static_buf!(capsules_extra::ieee802154::virtual_mac::MacUser<'static, $M>);
79        let sixlowpan = kernel::static_buf!(
80            sixlowpan_state::Sixlowpan<
81                'static,
82                VirtualMuxAlarm<'static, $A>,
83                sixlowpan_compression::Context,
84            >
85        );
86        let rx_state = kernel::static_buf!(sixlowpan_state::RxState<'static>);
87        let ip6_send = kernel::static_buf!(
88            capsules_extra::net::ipv6::ipv6_send::IP6SendStruct<
89                'static,
90                VirtualMuxAlarm<'static, $A>,
91            >
92        );
93        let mux_udp_send = kernel::static_buf!(
94            MuxUdpSender<
95                'static,
96                capsules_extra::net::ipv6::ipv6_send::IP6SendStruct<
97                    'static,
98                    VirtualMuxAlarm<'static, $A>,
99                >,
100            >
101        );
102        let mux_udp_recv =
103            kernel::static_buf!(capsules_extra::net::udp::udp_recv::MuxUdpReceiver<'static>);
104        let udp_port_manager =
105            kernel::static_buf!(capsules_extra::net::udp::udp_port_table::UdpPortManager);
106
107        let ip6_packet = kernel::static_buf!(capsules_extra::net::ipv6::IP6Packet<'static>);
108        let ip6_receive =
109            kernel::static_buf!(capsules_extra::net::ipv6::ipv6_recv::IP6RecvStruct<'static>);
110
111        // Rather than require a data structure with 65535 slots (number of UDP ports),
112        // we use a structure that can hold up to 16 port bindings. Any given capsule
113        // can bind at most one port. When a capsule obtains a socket, it is assigned a
114        // slot in this table. MAX_NUM_BOUND_PORTS represents the total number of
115        // capsules that can bind to different ports simultaneously within the Tock
116        // kernel.
117        //
118        // Each slot in the table tracks one socket that has been given to a capsule. If
119        // no slots in the table are free, no slots remain to be given out. If a socket
120        // is used to bind to a port, the port that is bound is saved in the slot to
121        // ensure that subsequent bindings do not also attempt to bind that port number.
122        let used_ports = kernel::static_buf!(
123            [Option<capsules_extra::net::udp::udp_port_table::SocketBindingEntry>;
124                capsules_extra::net::udp::udp_port_table::MAX_NUM_BOUND_PORTS]
125        );
126
127        let radio_buf = kernel::static_buf!([u8; kernel::hil::radio::MAX_BUF_SIZE]);
128        let sixlowpan_rx = kernel::static_buf!([u8; 1280]);
129        let udp_dgram = kernel::static_buf!([u8; MAX_PAYLOAD_LEN]);
130
131        let udp_vis_cap =
132            kernel::static_buf!(capsules_extra::net::network_capabilities::UdpVisibilityCapability);
133        let ip_vis_cap =
134            kernel::static_buf!(capsules_extra::net::network_capabilities::IpVisibilityCapability);
135
136        (
137            alarm,
138            mac_user,
139            sixlowpan,
140            rx_state,
141            ip6_send,
142            mux_udp_send,
143            mux_udp_recv,
144            udp_port_manager,
145            ip6_packet,
146            ip6_receive,
147            used_ports,
148            radio_buf,
149            sixlowpan_rx,
150            udp_dgram,
151            udp_vis_cap,
152            ip_vis_cap,
153        )
154    };};
155}
156
157pub struct UDPMuxComponent<A: Alarm<'static> + 'static, M: MacDevice<'static> + 'static> {
158    mux_mac: &'static capsules_extra::ieee802154::virtual_mac::MuxMac<'static, M>,
159    ctx_pfix_len: u8,
160    ctx_pfix: [u8; 16],
161    dst_mac_addr: MacAddress,
162    src_mac_addr: MacAddress,
163    interface_list: &'static [IPAddr],
164    alarm_mux: &'static MuxAlarm<'static, A>,
165}
166
167impl<A: Alarm<'static> + 'static, M: MacDevice<'static>> UDPMuxComponent<A, M> {
168    pub fn new(
169        mux_mac: &'static capsules_extra::ieee802154::virtual_mac::MuxMac<'static, M>,
170        ctx_pfix_len: u8,
171        ctx_pfix: [u8; 16],
172        dst_mac_addr: MacAddress,
173        src_mac_addr: MacAddress,
174        interface_list: &'static [IPAddr],
175        alarm_mux: &'static MuxAlarm<'static, A>,
176    ) -> Self {
177        Self {
178            mux_mac,
179            ctx_pfix_len,
180            ctx_pfix,
181            dst_mac_addr,
182            src_mac_addr,
183            interface_list,
184            alarm_mux,
185        }
186    }
187}
188
189impl<A: Alarm<'static> + 'static, M: MacDevice<'static>> Component for UDPMuxComponent<A, M> {
190    type StaticInput = (
191        &'static mut MaybeUninit<VirtualMuxAlarm<'static, A>>,
192        &'static mut MaybeUninit<capsules_extra::ieee802154::virtual_mac::MacUser<'static, M>>,
193        &'static mut MaybeUninit<
194            sixlowpan_state::Sixlowpan<
195                'static,
196                VirtualMuxAlarm<'static, A>,
197                sixlowpan_compression::Context,
198            >,
199        >,
200        &'static mut MaybeUninit<sixlowpan_state::RxState<'static>>,
201        &'static mut MaybeUninit<
202            capsules_extra::net::ipv6::ipv6_send::IP6SendStruct<
203                'static,
204                VirtualMuxAlarm<'static, A>,
205            >,
206        >,
207        &'static mut MaybeUninit<
208            MuxUdpSender<
209                'static,
210                capsules_extra::net::ipv6::ipv6_send::IP6SendStruct<
211                    'static,
212                    VirtualMuxAlarm<'static, A>,
213                >,
214            >,
215        >,
216        &'static mut MaybeUninit<MuxUdpReceiver<'static>>,
217        &'static mut MaybeUninit<UdpPortManager>,
218        &'static mut MaybeUninit<IP6Packet<'static>>,
219        &'static mut MaybeUninit<IP6RecvStruct<'static>>,
220        &'static mut MaybeUninit<[Option<SocketBindingEntry>; MAX_NUM_BOUND_PORTS]>,
221        &'static mut MaybeUninit<[u8; radio::MAX_BUF_SIZE]>,
222        &'static mut MaybeUninit<[u8; 1280]>,
223        &'static mut MaybeUninit<[u8; MAX_PAYLOAD_LEN]>,
224        &'static mut MaybeUninit<UdpVisibilityCapability>,
225        &'static mut MaybeUninit<IpVisibilityCapability>,
226    );
227    type Output = (
228        &'static MuxUdpSender<'static, IP6SendStruct<'static, VirtualMuxAlarm<'static, A>>>,
229        &'static MuxUdpReceiver<'static>,
230        &'static UdpPortManager,
231    );
232
233    fn finalize(self, s: Self::StaticInput) -> Self::Output {
234        let ipsender_virtual_alarm = s.0.write(VirtualMuxAlarm::new(self.alarm_mux));
235        ipsender_virtual_alarm.setup();
236
237        let udp_mac =
238            s.1.write(capsules_extra::ieee802154::virtual_mac::MacUser::new(
239                self.mux_mac,
240            ));
241        self.mux_mac.add_user(udp_mac);
242        let create_cap = create_capability!(capabilities::NetworkCapabilityCreationCapability);
243        let udp_vis = s.14.write(UdpVisibilityCapability::new(&create_cap));
244        let ip_vis = s.15.write(IpVisibilityCapability::new(&create_cap));
245
246        let sixlowpan = s.2.write(sixlowpan_state::Sixlowpan::new(
247            sixlowpan_compression::Context {
248                prefix: self.ctx_pfix,
249                prefix_len: self.ctx_pfix_len,
250                id: 0,
251                compress: false,
252            },
253            ipsender_virtual_alarm, // OK to reuse bc only used to get time, not set alarms
254        ));
255
256        let sixlowpan_rx_buffer = s.12.write([0; 1280]);
257        let sixlowpan_state = sixlowpan as &dyn sixlowpan_state::SixlowpanState;
258        let sixlowpan_tx = sixlowpan_state::TxState::new(sixlowpan_state);
259        let default_rx_state =
260            s.3.write(sixlowpan_state::RxState::new(sixlowpan_rx_buffer));
261        sixlowpan_state.add_rx_state(default_rx_state);
262        udp_mac.set_receive_client(sixlowpan);
263
264        let udp_dgram_buffer = s.13.write([0; MAX_PAYLOAD_LEN]);
265        let tr_hdr = TransportHeader::UDP(UDPHeader::new());
266        let ip_pyld: IPPayload = IPPayload {
267            header: tr_hdr,
268            payload: udp_dgram_buffer,
269        };
270        let ip6_dg = s.8.write(IP6Packet::new(ip_pyld));
271
272        let radio_buf = s.11.write([0; radio::MAX_BUF_SIZE]);
273
274        // In current design, all udp senders share same IP sender, and the IP
275        // sender holds the destination mac address. This means all UDP senders
276        // must send to the same mac address...this works fine under the
277        // assumption of all packets being routed via a single gateway router,
278        // but doesn't work if multiple senders want to send to different
279        // addresses on a local network. This will be fixed once we have an
280        // ipv6_nd cache mapping IP addresses to dst macs
281        let ip_send =
282            s.4.write(capsules_extra::net::ipv6::ipv6_send::IP6SendStruct::new(
283                ip6_dg,
284                ipsender_virtual_alarm,
285                radio_buf,
286                sixlowpan_tx,
287                udp_mac,
288                self.dst_mac_addr,
289                self.src_mac_addr,
290                ip_vis,
291            ));
292        ipsender_virtual_alarm.set_alarm_client(ip_send);
293
294        // Initially, set src IP of the sender to be the first IP in the
295        // Interface list. Userland apps can change this if they so choose.
296        // Notably, the src addr is the same regardless of if messages are sent
297        // from userland or capsules.
298        ip_send.set_addr(self.interface_list[0]);
299        udp_mac.set_transmit_client(ip_send);
300
301        let ip_receive =
302            s.9.write(capsules_extra::net::ipv6::ipv6_recv::IP6RecvStruct::new());
303        sixlowpan_state.set_rx_client(ip_receive);
304        let udp_recv_mux = s.6.write(MuxUdpReceiver::new());
305        ip_receive.set_client(udp_recv_mux);
306
307        let udp_send_mux = s.5.write(MuxUdpSender::new(ip_send));
308        ip_send.set_client(udp_send_mux);
309
310        let kernel_ports = s.10.write([None; MAX_NUM_BOUND_PORTS]);
311        let create_table_cap = create_capability!(capabilities::CreatePortTableCapability);
312        let udp_port_table = s.7.write(UdpPortManager::new(
313            &create_table_cap,
314            kernel_ports,
315            udp_vis,
316        ));
317
318        (udp_send_mux, udp_recv_mux, udp_port_table)
319    }
320}