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}