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            let _ = kernel_data.schedule_upcall(upcall::JOINCOMPLETE, (into_statuscode(res), 0, 0));
318        });
319    }
320
321    fn perform_crypt_op(
322        &self,
323        src_addr: IPAddr,
324        dst_addr: IPAddr,
325        security: Security,
326        payload: &[u8],
327        buf: &'static mut [u8],
328    ) -> Result<(), (ErrorCode, &'static mut [u8])> {
329        // Wrapper function for performing the AES-128CCM encryption. This function generates the nonce,
330        // sets the nonce/key for the crypto engine, generates the authenticated data, and initiates
331        // the crypto operation.
332
333        // Note: The payload argument does not include aux sec header
334
335        // Obtain and unwrap frame counter
336        let frame_counter = security.frame_counter;
337        if frame_counter.is_none() {
338            // UNCOMMENT TO DEBUG THREAD //
339            // kernel::debug!("[Thread] Malformed auxiliary security header");
340            return Err((ErrorCode::INVAL, buf));
341        }
342
343        // Generate nonce, obtain network key and set crypto engine accordingly
344        let nonce = get_ccm_nonce(
345            &mac_from_ipv6(src_addr),
346            frame_counter.unwrap(),
347            security.level,
348        );
349        let mle_key = self.networkkey.get();
350        let mic_len = security.level.mic_len();
351        match mle_key {
352            Some(netkey) => {
353                if self.aes_crypto.set_key(&netkey.mle_key).is_err()
354                    || self.aes_crypto.set_nonce(&nonce).is_err()
355                {
356                    // UNCOMMENT TO DEBUG THREAD //
357                    // kernel::debug!("[Thread] Failure setting networkkey and/or nonce.");
358                    return Err((ErrorCode::FAIL, buf));
359                }
360            }
361            None => {
362                // UNCOMMENT TO DEBUG THREAD //
363                // kernel::debug!("[Thread] Attempt to access networkkey when no networkkey set.");
364                return Err((ErrorCode::NOSUPPORT, buf));
365            }
366        }
367
368        // Thread MLE security utilizes the AES128 CCM security used by 802.15.4 link layer security.
369        // Notably, there are a few minor modifications. AES128 requires authentication data (a data)
370        // and the secured message data (m data). Together, the a data is used to encrypt the m data
371        // while also generating a message integrity code (MIC). Thread subtly changes the
372        // a data from the 802.15.4 specification. For Thread MLE, the a data consists of a concatenation
373        // of the IP source address || IP destination address || auxiliary security header. It is especially important
374        // to note that the security control field is not included in the auxiliary security header. Likewise,
375        // the first byte of the payload is not included in the a data. For further information, refer to
376        // (Thread Spec v1.3.0 -- sect. 4.9)
377        //
378        // Because all MLE messages must be encrypted with MLE security (v1.3.0 sect 4.10), all MLE messages
379        // that are processed must possess an auxiliary security header of 10 bytes. The payload therefore consists of:
380        //
381        // |-----(1 byte)-----|----(10 bytes)------|---(UNKNOWN)---|--(DEPENDENT ON PROTOCOL)--|
382        // |  SECURITY SUITE  |   AUX SEC HEADER   |      MLE      |            Mic            |
383        //
384        // Since the aux sec header is fixed, the auth data is always 32 bytes [16 (IPV6) + 16 (IPV6) + 10 aux sec header].
385        // The m data len can be determined because it is the only unknown length.
386
387        let aux_sec_header = &mut [0u8; AUX_SEC_HEADER_LENGTH];
388        Security::encode(&security, aux_sec_header);
389
390        let m_data_len = payload.len();
391
392        // Encode auth data and payload into `buf`
393        let encode_res = encode_cryp_data(src_addr, dst_addr, aux_sec_header, payload, buf).done();
394
395        // Error check on result from encoding, failure likely means buf was not large enough
396        if encode_res.is_none() {
397            // UNCOMMENT TO DEBUG THREAD //
398            // kernel::debug!("[Thread] Error encoding cryptographic data into buffer");
399            return Err((ErrorCode::FAIL, buf));
400        }
401
402        let (offset, ()) = encode_res.unwrap();
403
404        // GENERAL NOTE: `self.crypto_sizelock`
405        // This does not seem to be the most elegant solution. The `crypto_sizelock` arose from the fact
406        // that we must know the length of the payload when the `crypt_done` callback
407        // occurs in order to only send/receive the portion of the 200 byte buffer that is the message.
408        // The other option to `crypto_sizelock` is to only pass to the crypto engine a buffer
409        // that is the size of the transmission/reception. This however is flawed
410        // as it leads to an inability to replace/restore the 200 byte buffer. The crypto engine
411        // requires a reference to static memory (leading to the recv/send buf being taken).
412        // This is only able to be replaced when the `crypt_done` callback occurs and returns the
413        // buf used by the crypto engine. If a partial buffer is used, it is impossible to then replace
414        // the full sized buffer to the send/recv buf. Likewise, I choose to use the `crypt_sizelock`
415        // and pass the whole 200 byte buffer. The `crypto_sizelock` works for
416        // now until a more elegant solution is implemented.
417
418        // The sizelock is empty except when a crypto operation
419        // is underway. If the sizelock is not empty, return error
420        if self.crypto_sizelock.is_some() {
421            // UNCOMMENT TO DEBUG THREAD //
422            // kernel::debug!(
423            //     "[Thread] Error - cryptographic resources in use; crypto_sizelock occupied"
424            // );
425            return Err((ErrorCode::BUSY, buf));
426        }
427
428        // Store the length of the payload.
429        self.crypto_sizelock.replace(offset + mic_len);
430        self.aes_crypto
431            .crypt(buf, 0, AUTH_DATA_LEN, m_data_len, mic_len, true, true)
432    }
433}
434
435impl<'a, A: time::Alarm<'a>> framer::KeyProcedure for ThreadNetworkDriver<'a, A> {
436    /// Gets the key corresponding to the key that matches the given security
437    /// level `level` and key ID `key_id`. If no such key matches, returns
438    /// `None`.
439    // TODO: This implementation only supports one key
440    fn lookup_key(&self, _level: SecurityLevel, _key_id: KeyId) -> Option<[u8; 16]> {
441        if let Some(netkey) = self.networkkey.get() {
442            Some(netkey.mac_key)
443        } else {
444            None
445        }
446    }
447}
448
449impl<'a, A: time::Alarm<'a>> framer::DeviceProcedure for ThreadNetworkDriver<'a, A> {
450    /// Gets the key corresponding to the key that matches the given security
451    /// level `level` and key ID `key_id`. If no such key matches, returns
452    /// `None`.
453    // TODO: This implementation only supports one key
454    fn lookup_addr_long(&self, _addr: MacAddress) -> Option<[u8; 8]> {
455        Some(self.src_mac_addr)
456    }
457}
458
459impl<'a, A: time::Alarm<'a>> SyscallDriver for ThreadNetworkDriver<'a, A> {
460    /// ### `command_num`
461    /// - `0`: Driver Check
462    /// - `1`: Add a new mle/mac networkkey and initiate a parent request.
463
464    fn command(
465        &self,
466        command_num: usize,
467        _arg1: usize,
468        _: usize,
469        processid: ProcessId,
470    ) -> CommandReturn {
471        match command_num {
472            0 => CommandReturn::success(),
473
474            1 => self
475                .apps
476                .enter(processid, |_, kernel_data| {
477                    kernel_data
478                        .get_readonly_processbuffer(ro_allow::WRITE)
479                        .and_then(|ro_buf| {
480                            ro_buf.enter(|src_key| {
481                                // check Thread state, if thread state is not empty,
482                                // another userspace application has control of the Thread
483                                // network and other requesting applications should fail.
484                                if self.state.is_some() {
485                                    return CommandReturn::failure(ErrorCode::BUSY);
486                                }
487
488                                // src key consists of the mle and mac keys; Thread
489                                // hash is performed in userland and 32 byte hash is
490                                // passed to thread capsule and entered as mac/mle key
491                                // (For key generation see Thread spec v1.3.0 7.1.4)
492                                if src_key.len() != 32 {
493                                    return CommandReturn::failure(ErrorCode::SIZE);
494                                }
495                                let mut mle_key = [0u8; 16];
496                                let mut mac_key = [0u8; 16];
497                                src_key[..16].copy_to_slice(&mut mle_key);
498                                src_key[16..32].copy_to_slice(&mut mac_key);
499                                self.set_networkkey(mle_key, mac_key);
500
501                                // Thread state begins as detached if sucessfully joined
502                                self.state.replace(ThreadState::Detached);
503                                CommandReturn::success()
504                            })
505                        })
506                        .unwrap_or(CommandReturn::failure(ErrorCode::INVAL))
507                })
508                .map_or_else(
509                    |err| CommandReturn::failure(err.into()),
510                    |ok_val| {
511                        // If no failure in saving the mle/mac key, initiate
512                        // sending the parent request
513                        self.send_parent_req();
514                        ok_val
515                    },
516                ),
517
518            _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
519        }
520    }
521
522    fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
523        self.apps.enter(processid, |_, _| {})
524    }
525}
526
527impl<'a, A: time::Alarm<'a>> UDPSendClient for ThreadNetworkDriver<'a, A> {
528    fn send_done(&self, _result: Result<(), ErrorCode>, mut dgram: SubSliceMut<'static, u8>) {
529        // TODO: handle result from send done and respond accordingly
530
531        // Panicking on unwrap indicates the state was taken without replacement
532        // (unreachable with proper state machine implementation)
533        let curr_state = self.state.take().unwrap();
534
535        // Advance state machine
536        let next_state = match curr_state {
537            ThreadState::SendUpdate(dst_ip, dst_mac) => ThreadState::SEDActive(dst_ip, dst_mac),
538            ThreadState::SendUDPMsg => unimplemented!(),
539            ThreadState::SendChildIdReq(_) => ThreadState::WaitingChildRsp,
540            ThreadState::SendParentReq => {
541                // UNCOMMENT TO DEBUG THREAD //
542                // kernel::debug!("[Thread] Completed sending parent request to multicast IP");
543                ThreadState::WaitingParentRsp
544            }
545            _ => panic!("Thread state machine diverged"),
546        };
547
548        self.frame_count.set(self.frame_count.get() + 1);
549
550        // Replace the returned buffer and state
551        dgram.reset();
552        self.send_buffer.replace(dgram);
553        self.state.replace(next_state);
554    }
555}
556
557impl<'a, A: time::Alarm<'a>> time::AlarmClient for ThreadNetworkDriver<'a, A> {
558    // TODO: This function is mainly here as a place holder as it will be needed
559    // for implementing timeouts/timing for sending heartbeat messages to the parent
560    // node
561    fn alarm(&self) {
562        match self.state.take().unwrap() {
563            // TODO: Implement retries as defined in the thread spec (when timeouts occur)
564            ThreadState::Detached => unimplemented!("[Thread ALARM] Detached"),
565            ThreadState::SendParentReq => unimplemented!("[Thread ALARM] Send Parent Req"),
566            ThreadState::SendChildIdReq(_) => unimplemented!("[Thread ALARM] Send Child ID Req"),
567            ThreadState::SEDActive(_ipaddr, _mac) => {
568                // TODO: SEND HEARTBEAT to parent node
569                unimplemented!("[Thread ALARM] Send Heartbeat")
570            }
571            _ => panic!(""),
572        }
573    }
574}
575
576impl<'a, A: time::Alarm<'a>> UDPRecvClient for ThreadNetworkDriver<'a, A> {
577    fn receive(
578        &self,
579        src_addr: IPAddr,
580        dst_addr: IPAddr,
581        _src_port: u16,
582        _dst_port: u16,
583        payload: &[u8],
584    ) {
585        if payload[0] != SECURITY_SUITE_ENCRYP {
586            // Tock's current implementation of Thread ignores all messages that do not possess MLE encryption. This
587            // is due to the Thread spec stating "Except for when specifically indicated, incoming
588            // messages that are not secured with either MLE or link-layer security SHOULD be ignored." (v.1.3.0 sect 4.10)
589            // UNCOMMENT TO DEBUG THREAD //
590            // kernel::debug!("[Thread] DROPPED PACKET - Received unencrypted MLE packet.");
591        }
592
593        // decode aux security header from packet into Security data type
594        let sec_res = ieee802154::Security::decode(&payload[1..]).done();
595
596        // Guard statement for improperly formated aux sec header
597        if sec_res.is_none() {
598            // UNCOMMENT TO DEBUG THREAD //
599            // kernel::debug!("[Thread] DROPPED PACKET - Malformed auxiliary security header.");
600            return;
601        }
602
603        let security = sec_res.unwrap().1;
604
605        // Take the receive buffer and pass to the `perform_crypto_op` wrapper function. This
606        // initiates encoding all relevant auth data, setting crypto engine and initiating the
607        // crypto operation.
608        self.recv_buffer.take().map_or_else(
609            || {
610                // UNCOMMENT TO DEBUG THREAD //
611                // kernel::debug!("[Thread] DROPPED PACKET - Receive buffer not available")
612            },
613            |recv_buf| {
614                self.perform_crypt_op(
615                    src_addr,
616                    dst_addr,
617                    security,
618                    &payload[SECURITY_SUITE_LEN + AUX_SEC_HEADER_LENGTH
619                        ..payload.len() - security.level.mic_len()],
620                    recv_buf.take(),
621                )
622                .map_or_else(
623                    // Error check on crypto operation. If the crypto operation
624                    // fails, we log the error and replace the receive buffer for
625                    // future receptions
626                    |(_code, buf)| {
627                        // UNCOMMENT TO DEBUG THREAD alter _code to code//
628                        // kernel::debug!(
629                        //     "[Thread] DROPPED PACKET - Crypto Operation Error *{:?}",
630                        //     code
631                        // );
632                        self.recv_buffer.replace(SubSliceMut::new(buf));
633                    },
634                    |()| (),
635                )
636            },
637        );
638    }
639}
640
641impl<'a, A: time::Alarm<'a>> CCMClient for ThreadNetworkDriver<'a, A> {
642    fn crypt_done(&self, buf: &'static mut [u8], _res: Result<(), ErrorCode>, _tag_is_valid: bool) {
643        // TODO: check validity of result/tag and handle accordingly
644
645        // Obtain the length of the payload from the sizelock
646        let buf_len = self.crypto_sizelock.take().unwrap();
647
648        // The auth data contains the src_addr || dest_addr || aux_sec_header;
649        // Recover src/dst addr from the auth data
650        let mut src_ipv6 = [0u8; IPV6_LEN];
651        let mut dst_ipv6 = [0u8; IPV6_LEN];
652        src_ipv6.copy_from_slice(&buf[..IPV6_LEN]);
653        dst_ipv6.copy_from_slice(&buf[IPV6_LEN..(2 * IPV6_LEN)]);
654
655        // The crypto operation requires 32 bytes (2 IPV6 addresses) as part of the auth data. We
656        // do not care about this data so we shift the data to overwrite this data with the aux sec header
657        // and payload. We shift this to an offset of 1 so that we can add the security suite field
658        let auth_addr_offset = AUTH_DATA_LEN - AUX_SEC_HEADER_LENGTH;
659        buf.copy_within(auth_addr_offset.., SECURITY_SUITE_LEN);
660
661        // Recover the length of the mic from the security information encoded in the aux_sec_header
662        let mic_len = ieee802154::Security::decode(&buf[SECURITY_SUITE_LEN..])
663            .done()
664            .unwrap()
665            .1
666            .level
667            .mic_len();
668
669        // We hard code the security suite to `0` for now as all messages are
670        // assumed to be encrypted for the current implementation
671        buf[..SECURITY_SUITE_LEN].copy_from_slice(&[SECURITY_SUITE_ENCRYP]);
672
673        // the assembled_buf_len is the length of: security suite || aux sec header || mle payload || mic
674        let assembled_buf_len = buf_len - auth_addr_offset + SECURITY_SUITE_LEN;
675
676        // We create a new subslice that we will slice accordingly depending on if we are sending/receiving
677        let mut assembled_subslice = SubSliceMut::new(buf);
678
679        // Panicking on unwrap indicates the state was taken without replacement
680        // (unreachable with proper state machine implementation)
681        let curr_state = self.state.take().unwrap();
682
683        match curr_state {
684            ThreadState::SendParentReq | ThreadState::SendChildIdReq(_) => {
685                //TODO: Add alarm for timeouts
686
687                // To send, we need to send: security suite || aux sec header || mle payload || mic
688                // which correlates to the assembled_buf_len
689                assembled_subslice.slice(..assembled_buf_len);
690
691                let dest_ipv6 = match curr_state {
692                    // Determine destination IP depending on message type
693                    ThreadState::SendParentReq => MULTICAST_IPV6,
694                    ThreadState::SendChildIdReq(dst_ipv6) => dst_ipv6,
695                    _ => unreachable!(),
696                };
697
698                // we replace the state with the current state here
699                // because we cannot advance the state machine until
700                // after the `send_done` callback is received
701                self.state.replace(curr_state);
702
703                // Begin sending the transmission
704                self.sender
705                    .driver_send_to(
706                        dest_ipv6,
707                        THREAD_PORT_NUMBER,
708                        THREAD_PORT_NUMBER,
709                        assembled_subslice,
710                        self.driver_send_cap,
711                        self.net_cap,
712                    )
713                    .map_err(|buf| {
714                        // if the sending fails prior to transmission, replace
715                        // the buffer and pass error accordingly to terminate_child_join
716                        // in following unwrap statement
717                        self.send_buffer.replace(buf);
718                        ErrorCode::FAIL
719                    })
720                    .unwrap_or_else(|code| self.terminate_child_join(Err(code)));
721            }
722            ThreadState::WaitingChildRsp => {
723                // TODO: Receive child response
724            }
725            ThreadState::WaitingParentRsp => {
726                // Upon receiving messages, the receive logic only requires the MLE payload. Subsequently,
727                // we slice the assembled_subslice to exclude the security suite, aux sec header, and mic.
728                assembled_subslice
729                    .slice(AUX_SEC_HEADER_LENGTH + SECURITY_SUITE_LEN..assembled_buf_len - mic_len);
730
731                // Move the decrypted MLE message into the recv_buf and execute the receiving logic. Upon
732                // an error in `recv_logic`, joining the network fails and schedule termination upcall
733                self.recv_buffer.replace(assembled_subslice);
734                if let Err(code) = self.recv_logic(IPAddr(src_ipv6)) {
735                    self.terminate_child_join(Err(code))
736                }
737            }
738            _ => (),
739        }
740    }
741}