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}