capsules_extra/net/thread/
driver.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 2023.
4
5//! Structs and methods associated with the Thread networking layer.
6//!
7//! This represents a first attempt in Tock to support Thread
8//! networking. The current implementation successfully joins a Tock
9//! device as a child node to a Thread parent (tested using
10//! OpenThread). This Thread capsule is a client to the UDP Mux.  The
11//! associated ThreadNetwork struct must be created in the
12//! `thread_network.rs` component.
13//!
14//! The Userland interface is incredibly simple at this juncture. An application
15//! can begin the Thread child/parent joining by issuing a syscall command
16//! with the MLE/MAC key as an argument. Only one userspace application can use/join
17//! the Thread network. Once a userspace application has joined the Thread network,
18//! the Thread network is considered locked. After the Thread network
19//! is "locked", other userspace applications attempting to join the network
20//! will return a failure. This is temporary and will eventually be replaced.
21
22// ------------------------------------------------------------------------------
23// Current Limitations
24// ------------------------------------------------------------------------------
25// (1) A majority of the TLV fields used in the parent request/child id request
26//     are hardcoded. Future implementations need to provide options for specifying
27//     varied security policies.
28// (2) Current implementation joins the Thread network sucessfully and consistently
29//     but does not send update/heart beat messages to the parent prior to the child
30//     timing out.
31// (3) Currently no support for sending UDP messages across Thread interface. The
32//     current interface is unusable for sending data. It can only be used to
33//     join a network.
34
35use crate::ieee802154::framer::{self, get_ccm_nonce};
36use crate::net::ieee802154::{KeyId, MacAddress, Security, SecurityLevel};
37use crate::net::ipv6::ip_utils::IPAddr;
38use crate::net::network_capabilities::NetworkCapability;
39
40use crate::net::ieee802154;
41use crate::net::thread::thread_utils::generate_src_ipv6;
42use crate::net::thread::thread_utils::ThreadState;
43use crate::net::thread::thread_utils::MULTICAST_IPV6;
44use crate::net::thread::thread_utils::THREAD_PORT_NUMBER;
45use crate::net::thread::thread_utils::{
46    encode_cryp_data, form_child_id_req, form_parent_req, mac_from_ipv6, MleCommand, NetworkKey,
47    AUTH_DATA_LEN, AUX_SEC_HEADER_LENGTH, IPV6_LEN, SECURITY_SUITE_LEN,
48};
49use crate::net::udp::udp_port_table::UdpPortManager;
50use crate::net::udp::udp_recv::UDPRecvClient;
51use crate::net::udp::udp_send::{UDPSendClient, UDPSender};
52use capsules_core::driver;
53
54use core::cell::Cell;
55
56use kernel::capabilities::UdpDriverCapability;
57use kernel::errorcode::into_statuscode;
58use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
59use kernel::hil::symmetric_encryption::CCMClient;
60use kernel::hil::symmetric_encryption::AES128CCM;
61use kernel::hil::time;
62use kernel::processbuffer::ReadableProcessBuffer;
63use kernel::syscall::{CommandReturn, SyscallDriver};
64use kernel::utilities::cells::MapCell;
65use kernel::utilities::leasable_buffer::SubSliceMut;
66use kernel::{ErrorCode, ProcessId};
67
68const SECURITY_SUITE_ENCRYP: u8 = 0;
69pub const DRIVER_NUM: usize = driver::NUM::Thread as usize;
70
71/// Ids for read-only allow buffers
72mod ro_allow {
73    pub const WRITE: usize = 0;
74    /// The number of allow buffers the kernel stores for this grant
75    pub const COUNT: u8 = 1;
76}
77
78// /// Ids for read-write allow buffers
79// mod rw_allow {
80//     pub const READ: usize = 0;
81//     pub const CFG: usize = 1;
82//     pub const RX_CFG: usize = 2;
83//     /// The number of allow buffers the kernel stores for this grant
84//     pub const COUNT: u8 = 3;
85// }
86
87/// IDs for subscribed upcalls.
88mod upcall {
89    pub const JOINCOMPLETE: usize = 0;
90}
91
92#[derive(Default)]
93pub struct App {}
94
95#[allow(dead_code)]
96pub struct ThreadNetworkDriver<'a, A: time::Alarm<'a>> {
97    /// UDP sender
98    sender: &'a dyn UDPSender<'a>,
99
100    /// AES crypto engine for MLE encryption
101    aes_crypto: &'a dyn AES128CCM<'a>,
102
103    /// Alarm for timeouts
104    alarm: &'a A,
105
106    /// Grant of apps that use this thread driver.
107    apps: Grant<App, UpcallCount<1>, AllowRoCount<{ ro_allow::COUNT }>, AllowRwCount<0>>,
108
109    /// mac address of device
110    src_mac_addr: [u8; 8],
111
112    /// Maximum length payload that an app can transmit via this driver
113    max_tx_pyld_len: usize,
114
115    /// UDP bound port table (manages kernel bindings)
116    port_table: &'static UdpPortManager,
117
118    /// kernel buffer used for sending
119    send_buffer: MapCell<SubSliceMut<'static, u8>>,
120
121    /// kernel buffer used for receiving
122    recv_buffer: MapCell<SubSliceMut<'static, u8>>,
123
124    /// state machine for the Thread device
125    state: MapCell<ThreadState>,
126
127    /// UDP driver capability
128    driver_send_cap: &'static dyn UdpDriverCapability,
129
130    /// Network capability
131    net_cap: &'static NetworkCapability,
132
133    /// Frame counter for Thread MLE
134    frame_count: Cell<u32>,
135
136    /// Stored Thread network containing mac/MLE key
137    networkkey: MapCell<NetworkKey>,
138
139    /// Length of the message passed to the crypto engine
140    crypto_sizelock: MapCell<usize>,
141}
142
143// Note: For now, we initialize the Thread state as empty.
144// We replace the Thread state when the first userspace
145// application calls the Thread capsule to initiate a Thread network.
146// This serves to "lock" the Thread capsule to only one application.
147// For now, Tock only supports one application using the Thread network.
148// After the network is "locked" to one application, other userspace
149// applications requesting to join a Thread network will fail.
150impl<'a, A: time::Alarm<'a>> ThreadNetworkDriver<'a, A> {
151    pub fn new(
152        sender: &'a dyn UDPSender<'a>,
153        aes_crypto: &'a dyn AES128CCM<'a>,
154        alarm: &'a A,
155        grant: Grant<App, UpcallCount<1>, AllowRoCount<{ ro_allow::COUNT }>, AllowRwCount<0>>,
156        src_mac_addr: [u8; 8],
157        max_tx_pyld_len: usize,
158        port_table: &'static UdpPortManager,
159        send_buffer: SubSliceMut<'static, u8>,
160        recv_buffer: SubSliceMut<'static, u8>,
161        driver_send_cap: &'static dyn UdpDriverCapability,
162        net_cap: &'static NetworkCapability,
163    ) -> ThreadNetworkDriver<'a, A> {
164        ThreadNetworkDriver {
165            sender,
166            aes_crypto,
167            alarm,
168            apps: grant,
169            src_mac_addr,
170            max_tx_pyld_len,
171            port_table,
172            send_buffer: MapCell::new(send_buffer),
173            recv_buffer: MapCell::new(recv_buffer),
174            state: MapCell::empty(),
175            driver_send_cap,
176            net_cap,
177            frame_count: Cell::new(5),
178            networkkey: MapCell::empty(),
179            crypto_sizelock: MapCell::empty(),
180        }
181    }
182
183    /// Takes the MLE and MAC keys and replaces the networkkey
184    pub fn set_networkkey(&self, mle_key: [u8; 16], mac_key: [u8; 16]) {
185        self.networkkey.replace(NetworkKey { mle_key, mac_key });
186    }
187
188    fn send_parent_req(&self) {
189        // UNCOMMENT TO DEBUG THREAD //
190        // kernel::debug!("[Thread] Sending parent request...");
191
192        // Panicking on unwrap indicates the state was taken without replacement
193        // (unreachable with proper state machine implementation)
194        let curr_state = self.state.take().unwrap();
195
196        match curr_state {
197            ThreadState::Detached => {
198                // A parent request can only begin from a detached state. We utilize
199                // helper functions to form the request and send the parent request
200                // to the multicast IP/Mac Address
201                self.state.replace(ThreadState::SendParentReq);
202                let parent_req_mle = form_parent_req();
203                let src_ipv6 = generate_src_ipv6(&self.src_mac_addr);
204                self.thread_mle_send(&parent_req_mle, MULTICAST_IPV6, src_ipv6)
205                    .err()
206                    .map(|code| {
207                        // Thread send failed sending parent req so we terminate and return
208                        // to a detached state.
209                        self.state.replace(ThreadState::Detached);
210
211                        // UNCOMMENT TO DEBUG THREAD //
212                        // kernel::debug!(
213                        //     "[Thread] Failed sending MLE parent request - crypto operation error."
214                        // );
215                        self.terminate_child_join(Err(code));
216                    });
217            }
218            ThreadState::SEDActive(_, _)
219            | ThreadState::SendUpdate(_, _)
220            | ThreadState::SendUDPMsg => {
221                // These states constitute a device that has previously sucessfully
222                // joined the network. There is no need to issue a new parent request.
223                // Replace state, and terminate.
224                self.state.replace(curr_state);
225                self.terminate_child_join(Err(ErrorCode::ALREADY));
226            }
227            _ => {
228                // All other Thread states indicate that the thread device has already
229                // begun the process of connecting to a parent device. Terminate the parent request.
230                // Replace state, and terminate.
231                self.state.replace(curr_state);
232                self.terminate_child_join(Err(ErrorCode::BUSY));
233            }
234        }
235    }
236
237    fn thread_mle_send(
238        &self,
239        mle_buf: &[u8],
240        dest_addr: IPAddr,
241        src_addr: IPAddr,
242    ) -> Result<(), ErrorCode> {
243        // TODO: Hardcoded encryption suite and auxiliary security; add support to send encrypted/unencrypted MLE
244
245        // We hardcode the auxiliary security for now
246        let security = Security {
247            level: SecurityLevel::EncMic32,
248            asn_in_nonce: false,
249            frame_counter: Some(self.frame_count.get()),
250            key_id: KeyId::Source4Index([0, 0, 0, 0], 1),
251        };
252
253        // Begin cryptographic and sending procedure for the MLE message
254        self.send_buffer
255            .take()
256            .map_or(Err(ErrorCode::NOMEM), |send_buffer| {
257                self.perform_crypt_op(src_addr, dest_addr, security, mle_buf, send_buffer.take())
258                    .map_err(|(code, buf)| {
259                        // Error occured with cryptographic operation, replace buffer
260                        // for future transmissions and return error code
261                        self.send_buffer.replace(SubSliceMut::new(buf));
262                        code
263                    })
264            })
265    }
266
267    fn recv_logic(&self, sender_ip: IPAddr) -> Result<(), ErrorCode> {
268        // This function is called once the received MLE payload has been placed
269        // into the recv_buffer. The function handles the message and responds accordingly
270
271        self.recv_buffer
272            .take()
273            .map_or(Err(ErrorCode::NOMEM), |mut recv_buf| {
274                if recv_buf[0] == MleCommand::ParentResponse as u8 {
275                    // Received Parent Response -> form Child ID Request
276
277                    // UNCOMMENT TO DEBUG THREAD //
278                    // kernel::debug!("[Thread] Received Parent Response.");
279                    // kernel::debug!("[Thread] Sending Child ID Request...");
280
281                    let src_ipv6 = generate_src_ipv6(&self.src_mac_addr);
282
283                    let (output, offset) =
284                        form_child_id_req(recv_buf.as_slice(), self.frame_count.get())?;
285
286                    // Advance state machine
287                    self.state.replace(ThreadState::SendChildIdReq(sender_ip));
288
289                    self.thread_mle_send(&output[..offset], sender_ip, src_ipv6)?;
290                } else if recv_buf[0] == MleCommand::ChildIdResponse as u8 {
291                    // Receive child id response -> advance state machine
292                    self.state.replace(ThreadState::SEDActive(
293                        sender_ip,
294                        MacAddress::Long(mac_from_ipv6(sender_ip)),
295                    ));
296
297                    // TODO: once heart beats are implemented, we will set
298                    // the timer here (as seen below)
299                    // let curr_time = self.alarm.now();
300                    // self.alarm.set_alarm(
301                    //     curr_time,
302                    //     time::ConvertTicks::ticks_from_seconds(self.alarm, 5),
303                    // );
304                }
305
306                recv_buf.reset();
307                self.recv_buffer.replace(recv_buf);
308                Ok(())
309            })
310    }
311
312    fn terminate_child_join(&self, res: Result<(), ErrorCode>) {
313        // Function to schedule upcall to userland on parent request termination. Notifies
314        // userland of the reason for termination with the first argument.
315
316        self.apps.each(|_, _, kernel_data| {
317            kernel_data
318                .schedule_upcall(upcall::JOINCOMPLETE, (into_statuscode(res), 0, 0))
319                .ok();
320        });
321    }
322
323    fn perform_crypt_op(
324        &self,
325        src_addr: IPAddr,
326        dst_addr: IPAddr,
327        security: Security,
328        payload: &[u8],
329        buf: &'static mut [u8],
330    ) -> Result<(), (ErrorCode, &'static mut [u8])> {
331        // Wrapper function for performing the AES-128CCM encryption. This function generates the nonce,
332        // sets the nonce/key for the crypto engine, generates the authenticated data, and initiates
333        // the crypto operation.
334
335        // Note: The payload argument does not include aux sec header
336
337        // Obtain and unwrap frame counter
338        let frame_counter = security.frame_counter;
339        if frame_counter.is_none() {
340            // UNCOMMENT TO DEBUG THREAD //
341            // kernel::debug!("[Thread] Malformed auxiliary security header");
342            return Err((ErrorCode::INVAL, buf));
343        }
344
345        // Generate nonce, obtain network key and set crypto engine accordingly
346        let nonce = get_ccm_nonce(
347            &mac_from_ipv6(src_addr),
348            frame_counter.unwrap(),
349            security.level,
350        );
351        let mle_key = self.networkkey.get();
352        let mic_len = security.level.mic_len();
353        match mle_key {
354            Some(netkey) => {
355                if self.aes_crypto.set_key(&netkey.mle_key).is_err()
356                    || self.aes_crypto.set_nonce(&nonce).is_err()
357                {
358                    // UNCOMMENT TO DEBUG THREAD //
359                    // kernel::debug!("[Thread] Failure setting networkkey and/or nonce.");
360                    return Err((ErrorCode::FAIL, buf));
361                }
362            }
363            None => {
364                // UNCOMMENT TO DEBUG THREAD //
365                // kernel::debug!("[Thread] Attempt to access networkkey when no networkkey set.");
366                return Err((ErrorCode::NOSUPPORT, buf));
367            }
368        }
369
370        // Thread MLE security utilizes the AES128 CCM security used by 802.15.4 link layer security.
371        // Notably, there are a few minor modifications. AES128 requires authentication data (a data)
372        // and the secured message data (m data). Together, the a data is used to encrypt the m data
373        // while also generating a message integrity code (MIC). Thread subtly changes the
374        // a data from the 802.15.4 specification. For Thread MLE, the a data consists of a concatenation
375        // of the IP source address || IP destination address || auxiliary security header. It is especially important
376        // to note that the security control field is not included in the auxiliary security header. Likewise,
377        // the first byte of the payload is not included in the a data. For further information, refer to
378        // (Thread Spec v1.3.0 -- sect. 4.9)
379        //
380        // Because all MLE messages must be encrypted with MLE security (v1.3.0 sect 4.10), all MLE messages
381        // that are processed must possess an auxiliary security header of 10 bytes. The payload therefore consists of:
382        //
383        // |-----(1 byte)-----|----(10 bytes)------|---(UNKNOWN)---|--(DEPENDENT ON PROTOCOL)--|
384        // |  SECURITY SUITE  |   AUX SEC HEADER   |      MLE      |            Mic            |
385        //
386        // Since the aux sec header is fixed, the auth data is always 32 bytes [16 (IPV6) + 16 (IPV6) + 10 aux sec header].
387        // The m data len can be determined because it is the only unknown length.
388
389        let aux_sec_header = &mut [0u8; AUX_SEC_HEADER_LENGTH];
390        Security::encode(&security, aux_sec_header);
391
392        let m_data_len = payload.len();
393
394        // Encode auth data and payload into `buf`
395        let encode_res = encode_cryp_data(src_addr, dst_addr, aux_sec_header, payload, buf).done();
396
397        // Error check on result from encoding, failure likely means buf was not large enough
398        if encode_res.is_none() {
399            // UNCOMMENT TO DEBUG THREAD //
400            // kernel::debug!("[Thread] Error encoding cryptographic data into buffer");
401            return Err((ErrorCode::FAIL, buf));
402        }
403
404        let (offset, ()) = encode_res.unwrap();
405
406        // GENERAL NOTE: `self.crypto_sizelock`
407        // This does not seem to be the most elegant solution. The `crypto_sizelock` arose from the fact
408        // that we must know the length of the payload when the `crypt_done` callback
409        // occurs in order to only send/receive the portion of the 200 byte buffer that is the message.
410        // The other option to `crypto_sizelock` is to only pass to the crypto engine a buffer
411        // that is the size of the transmission/reception. This however is flawed
412        // as it leads to an inability to replace/restore the 200 byte buffer. The crypto engine
413        // requires a reference to static memory (leading to the recv/send buf being taken).
414        // This is only able to be replaced when the `crypt_done` callback occurs and returns the
415        // buf used by the crypto engine. If a partial buffer is used, it is impossible to then replace
416        // the full sized buffer to the send/recv buf. Likewise, I choose to use the `crypt_sizelock`
417        // and pass the whole 200 byte buffer. The `crypto_sizelock` works for
418        // now until a more elegant solution is implemented.
419
420        // The sizelock is empty except when a crypto operation
421        // is underway. If the sizelock is not empty, return error
422        if self.crypto_sizelock.is_some() {
423            // UNCOMMENT TO DEBUG THREAD //
424            // kernel::debug!(
425            //     "[Thread] Error - cryptographic resources in use; crypto_sizelock occupied"
426            // );
427            return Err((ErrorCode::BUSY, buf));
428        }
429
430        // Store the length of the payload.
431        self.crypto_sizelock.replace(offset + mic_len);
432        self.aes_crypto
433            .crypt(buf, 0, AUTH_DATA_LEN, m_data_len, mic_len, true, true)
434    }
435}
436
437impl<'a, A: time::Alarm<'a>> framer::KeyProcedure for ThreadNetworkDriver<'a, A> {
438    /// Gets the key corresponding to the key that matches the given security
439    /// level `level` and key ID `key_id`. If no such key matches, returns
440    /// `None`.
441    // TODO: This implementation only supports one key
442    fn lookup_key(&self, _level: SecurityLevel, _key_id: KeyId) -> Option<[u8; 16]> {
443        if let Some(netkey) = self.networkkey.get() {
444            Some(netkey.mac_key)
445        } else {
446            None
447        }
448    }
449}
450
451impl<'a, A: time::Alarm<'a>> framer::DeviceProcedure for ThreadNetworkDriver<'a, A> {
452    /// Gets the key corresponding to the key that matches the given security
453    /// level `level` and key ID `key_id`. If no such key matches, returns
454    /// `None`.
455    // TODO: This implementation only supports one key
456    fn lookup_addr_long(&self, _addr: MacAddress) -> Option<[u8; 8]> {
457        Some(self.src_mac_addr)
458    }
459}
460
461impl<'a, A: time::Alarm<'a>> SyscallDriver for ThreadNetworkDriver<'a, A> {
462    /// ### `command_num`
463    /// - `0`: Driver Check
464    /// - `1`: Add a new mle/mac networkkey and initiate a parent request.
465
466    fn command(
467        &self,
468        command_num: usize,
469        _arg1: usize,
470        _: usize,
471        processid: ProcessId,
472    ) -> CommandReturn {
473        match command_num {
474            0 => CommandReturn::success(),
475
476            1 => self
477                .apps
478                .enter(processid, |_, kernel_data| {
479                    kernel_data
480                        .get_readonly_processbuffer(ro_allow::WRITE)
481                        .and_then(|ro_buf| {
482                            ro_buf.enter(|src_key| {
483                                // check Thread state, if thread state is not empty,
484                                // another userspace application has control of the Thread
485                                // network and other requesting applications should fail.
486                                if self.state.is_some() {
487                                    return CommandReturn::failure(ErrorCode::BUSY);
488                                }
489
490                                // src key consists of the mle and mac keys; Thread
491                                // hash is performed in userland and 32 byte hash is
492                                // passed to thread capsule and entered as mac/mle key
493                                // (For key generation see Thread spec v1.3.0 7.1.4)
494                                if src_key.len() != 32 {
495                                    return CommandReturn::failure(ErrorCode::SIZE);
496                                }
497                                let mut mle_key = [0u8; 16];
498                                let mut mac_key = [0u8; 16];
499                                src_key[..16].copy_to_slice(&mut mle_key);
500                                src_key[16..32].copy_to_slice(&mut mac_key);
501                                self.set_networkkey(mle_key, mac_key);
502
503                                // Thread state begins as detached if sucessfully joined
504                                self.state.replace(ThreadState::Detached);
505                                CommandReturn::success()
506                            })
507                        })
508                        .unwrap_or(CommandReturn::failure(ErrorCode::INVAL))
509                })
510                .map_or_else(
511                    |err| CommandReturn::failure(err.into()),
512                    |ok_val| {
513                        // If no failure in saving the mle/mac key, initiate
514                        // sending the parent request
515                        self.send_parent_req();
516                        ok_val
517                    },
518                ),
519
520            _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
521        }
522    }
523
524    fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
525        self.apps.enter(processid, |_, _| {})
526    }
527}
528
529impl<'a, A: time::Alarm<'a>> UDPSendClient for ThreadNetworkDriver<'a, A> {
530    fn send_done(&self, _result: Result<(), ErrorCode>, mut dgram: SubSliceMut<'static, u8>) {
531        // TODO: handle result from send done and respond accordingly
532
533        // Panicking on unwrap indicates the state was taken without replacement
534        // (unreachable with proper state machine implementation)
535        let curr_state = self.state.take().unwrap();
536
537        // Advance state machine
538        let next_state = match curr_state {
539            ThreadState::SendUpdate(dst_ip, dst_mac) => ThreadState::SEDActive(dst_ip, dst_mac),
540            ThreadState::SendUDPMsg => unimplemented!(),
541            ThreadState::SendChildIdReq(_) => ThreadState::WaitingChildRsp,
542            ThreadState::SendParentReq => {
543                // UNCOMMENT TO DEBUG THREAD //
544                // kernel::debug!("[Thread] Completed sending parent request to multicast IP");
545                ThreadState::WaitingParentRsp
546            }
547            _ => panic!("Thread state machine diverged"),
548        };
549
550        self.frame_count.set(self.frame_count.get() + 1);
551
552        // Replace the returned buffer and state
553        dgram.reset();
554        self.send_buffer.replace(dgram);
555        self.state.replace(next_state);
556    }
557}
558
559impl<'a, A: time::Alarm<'a>> time::AlarmClient for ThreadNetworkDriver<'a, A> {
560    // TODO: This function is mainly here as a place holder as it will be needed
561    // for implementing timeouts/timing for sending heartbeat messages to the parent
562    // node
563    fn alarm(&self) {
564        match self.state.take().unwrap() {
565            // TODO: Implement retries as defined in the thread spec (when timeouts occur)
566            ThreadState::Detached => unimplemented!("[Thread ALARM] Detached"),
567            ThreadState::SendParentReq => unimplemented!("[Thread ALARM] Send Parent Req"),
568            ThreadState::SendChildIdReq(_) => unimplemented!("[Thread ALARM] Send Child ID Req"),
569            ThreadState::SEDActive(_ipaddr, _mac) => {
570                // TODO: SEND HEARTBEAT to parent node
571                unimplemented!("[Thread ALARM] Send Heartbeat")
572            }
573            _ => panic!(""),
574        }
575    }
576}
577
578impl<'a, A: time::Alarm<'a>> UDPRecvClient for ThreadNetworkDriver<'a, A> {
579    fn receive(
580        &self,
581        src_addr: IPAddr,
582        dst_addr: IPAddr,
583        _src_port: u16,
584        _dst_port: u16,
585        payload: &[u8],
586    ) {
587        if payload[0] != SECURITY_SUITE_ENCRYP {
588            // Tock's current implementation of Thread ignores all messages that do not possess MLE encryption. This
589            // is due to the Thread spec stating "Except for when specifically indicated, incoming
590            // messages that are not secured with either MLE or link-layer security SHOULD be ignored." (v.1.3.0 sect 4.10)
591            // UNCOMMENT TO DEBUG THREAD //
592            // kernel::debug!("[Thread] DROPPED PACKET - Received unencrypted MLE packet.");
593        }
594
595        // decode aux security header from packet into Security data type
596        let sec_res = ieee802154::Security::decode(&payload[1..]).done();
597
598        // Guard statement for improperly formated aux sec header
599        if sec_res.is_none() {
600            // UNCOMMENT TO DEBUG THREAD //
601            // kernel::debug!("[Thread] DROPPED PACKET - Malformed auxiliary security header.");
602            return;
603        }
604
605        let security = sec_res.unwrap().1;
606
607        // Take the receive buffer and pass to the `perform_crypto_op` wrapper function. This
608        // initiates encoding all relevant auth data, setting crypto engine and initiating the
609        // crypto operation.
610        self.recv_buffer.take().map_or_else(
611            || {
612                // UNCOMMENT TO DEBUG THREAD //
613                // kernel::debug!("[Thread] DROPPED PACKET - Receive buffer not available")
614            },
615            |recv_buf| {
616                self.perform_crypt_op(
617                    src_addr,
618                    dst_addr,
619                    security,
620                    &payload[SECURITY_SUITE_LEN + AUX_SEC_HEADER_LENGTH
621                        ..payload.len() - security.level.mic_len()],
622                    recv_buf.take(),
623                )
624                .map_or_else(
625                    // Error check on crypto operation. If the crypto operation
626                    // fails, we log the error and replace the receive buffer for
627                    // future receptions
628                    |(_code, buf)| {
629                        // UNCOMMENT TO DEBUG THREAD alter _code to code//
630                        // kernel::debug!(
631                        //     "[Thread] DROPPED PACKET - Crypto Operation Error *{:?}",
632                        //     code
633                        // );
634                        self.recv_buffer.replace(SubSliceMut::new(buf));
635                    },
636                    |()| (),
637                )
638            },
639        );
640    }
641}
642
643impl<'a, A: time::Alarm<'a>> CCMClient for ThreadNetworkDriver<'a, A> {
644    fn crypt_done(&self, buf: &'static mut [u8], _res: Result<(), ErrorCode>, _tag_is_valid: bool) {
645        // TODO: check validity of result/tag and handle accordingly
646
647        // Obtain the length of the payload from the sizelock
648        let buf_len = self.crypto_sizelock.take().unwrap();
649
650        // The auth data contains the src_addr || dest_addr || aux_sec_header;
651        // Recover src/dst addr from the auth data
652        let mut src_ipv6 = [0u8; IPV6_LEN];
653        let mut dst_ipv6 = [0u8; IPV6_LEN];
654        src_ipv6.copy_from_slice(&buf[..IPV6_LEN]);
655        dst_ipv6.copy_from_slice(&buf[IPV6_LEN..(2 * IPV6_LEN)]);
656
657        // The crypto operation requires 32 bytes (2 IPV6 addresses) as part of the auth data. We
658        // do not care about this data so we shift the data to overwrite this data with the aux sec header
659        // and payload. We shift this to an offset of 1 so that we can add the security suite field
660        let auth_addr_offset = AUTH_DATA_LEN - AUX_SEC_HEADER_LENGTH;
661        buf.copy_within(auth_addr_offset.., SECURITY_SUITE_LEN);
662
663        // Recover the length of the mic from the security information encoded in the aux_sec_header
664        let mic_len = ieee802154::Security::decode(&buf[SECURITY_SUITE_LEN..])
665            .done()
666            .unwrap()
667            .1
668            .level
669            .mic_len();
670
671        // We hard code the security suite to `0` for now as all messages are
672        // assumed to be encrypted for the current implementation
673        buf[..SECURITY_SUITE_LEN].copy_from_slice(&[SECURITY_SUITE_ENCRYP]);
674
675        // the assembled_buf_len is the length of: security suite || aux sec header || mle payload || mic
676        let assembled_buf_len = buf_len - auth_addr_offset + SECURITY_SUITE_LEN;
677
678        // We create a new subslice that we will slice accordingly depending on if we are sending/receiving
679        let mut assembled_subslice = SubSliceMut::new(buf);
680
681        // Panicking on unwrap indicates the state was taken without replacement
682        // (unreachable with proper state machine implementation)
683        let curr_state = self.state.take().unwrap();
684
685        match curr_state {
686            ThreadState::SendParentReq | ThreadState::SendChildIdReq(_) => {
687                //TODO: Add alarm for timeouts
688
689                // To send, we need to send: security suite || aux sec header || mle payload || mic
690                // which correlates to the assembled_buf_len
691                assembled_subslice.slice(..assembled_buf_len);
692
693                let dest_ipv6 = match curr_state {
694                    // Determine destination IP depending on message type
695                    ThreadState::SendParentReq => MULTICAST_IPV6,
696                    ThreadState::SendChildIdReq(dst_ipv6) => dst_ipv6,
697                    _ => unreachable!(),
698                };
699
700                // we replace the state with the current state here
701                // because we cannot advance the state machine until
702                // after the `send_done` callback is received
703                self.state.replace(curr_state);
704
705                // Begin sending the transmission
706                self.sender
707                    .driver_send_to(
708                        dest_ipv6,
709                        THREAD_PORT_NUMBER,
710                        THREAD_PORT_NUMBER,
711                        assembled_subslice,
712                        self.driver_send_cap,
713                        self.net_cap,
714                    )
715                    .map_err(|buf| {
716                        // if the sending fails prior to transmission, replace
717                        // the buffer and pass error accordingly to terminate_child_join
718                        // in following unwrap statement
719                        self.send_buffer.replace(buf);
720                        ErrorCode::FAIL
721                    })
722                    .unwrap_or_else(|code| self.terminate_child_join(Err(code)));
723            }
724            ThreadState::WaitingChildRsp => {
725                // TODO: Receive child response
726            }
727            ThreadState::WaitingParentRsp => {
728                // Upon receiving messages, the receive logic only requires the MLE payload. Subsequently,
729                // we slice the assembled_subslice to exclude the security suite, aux sec header, and mic.
730                assembled_subslice
731                    .slice(AUX_SEC_HEADER_LENGTH + SECURITY_SUITE_LEN..assembled_buf_len - mic_len);
732
733                // Move the decrypted MLE message into the recv_buf and execute the receiving logic. Upon
734                // an error in `recv_logic`, joining the network fails and schedule termination upcall
735                self.recv_buffer.replace(assembled_subslice);
736                if let Err(code) = self.recv_logic(IPAddr(src_ipv6)) {
737                    self.terminate_child_join(Err(code))
738                }
739            }
740            _ => (),
741        }
742    }
743}