openthread/ot/
border_agent.rs

1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::prelude_internal::*;
6use anyhow::format_err;
7use num::FromPrimitive;
8use std::os::raw::c_char;
9
10// This mirrors the behavior from ot-br-posix
11// https://github.com/openthread/ot-br-posix/blob/main/src/border_agent/border_agent.cpp
12const EPSKC_RANDOM_GEN_LEN: usize = 8;
13
14/// Represents the thread joiner state.
15///
16/// Functional equivalent of [`otsys::otJoinerState`](crate::otsys::otJoinerState).
17#[derive(
18    Debug,
19    Copy,
20    Clone,
21    Eq,
22    Ord,
23    PartialOrd,
24    PartialEq,
25    num_derive::FromPrimitive,
26    num_derive::ToPrimitive,
27)]
28pub enum BorderAgentEphemeralKeyState {
29    /// Functional equivalent of [`otsys::OT_BORDER_AGENT_STATE_DISABLED`](crate::otsys::OT_BORDER_AGENT_STATE_DISABLED).
30    Disabled = OT_BORDER_AGENT_STATE_DISABLED as isize,
31
32    /// Functional equivalent of [`otsys::OT_BORDER_AGENT_STATE_STOPPED`](crate::otsys::OT_BORDER_AGENT_STATE_STOPPED).
33    Stopped = OT_BORDER_AGENT_STATE_STOPPED as isize,
34
35    /// Functional equivalent of [`otsys::OT_BORDER_AGENT_STATE_STARTED`](crate::otsys::OT_BORDER_AGENT_STATE_STARTED).
36    Started = OT_BORDER_AGENT_STATE_STARTED as isize,
37
38    /// Functional equivalent of [`otsys::OT_BORDER_AGENT_STATE_CONNECTED`](crate::otsys::OT_BORDER_AGENT_STATE_CONNECTED).
39    Connected = OT_BORDER_AGENT_STATE_CONNECTED as isize,
40
41    /// Functional equivalent of [`otsys::OT_BORDER_AGENT_STATE_ACCEPTED`](crate::otsys::OT_BORDER_AGENT_STATE_ACCEPTED).
42    Accepted = OT_BORDER_AGENT_STATE_ACCEPTED as isize,
43}
44
45impl From<otBorderAgentEphemeralKeyState> for BorderAgentEphemeralKeyState {
46    fn from(x: otBorderAgentEphemeralKeyState) -> Self {
47        Self::from_u32(x)
48            .unwrap_or_else(|| panic!("Unknown otBorderAgentEphemeralKeyState value: {x}"))
49    }
50}
51
52impl From<BorderAgentEphemeralKeyState> for otBorderAgentEphemeralKeyState {
53    fn from(x: BorderAgentEphemeralKeyState) -> Self {
54        x as otBorderAgentEphemeralKeyState
55    }
56}
57
58#[derive(Debug, PartialEq)]
59#[allow(missing_docs)]
60pub struct BorderAgentCounters {
61    pub epskc_activations: u32,
62    pub epskc_deactivation_clears: u32,
63    pub epskc_deactivation_timeouts: u32,
64    pub epskc_deactivation_max_attempts: u32,
65    pub epskc_deactivation_disconnects: u32,
66    pub epskc_invalid_ba_state_errors: u32,
67    pub epskc_invalid_args_errors: u32,
68    pub epskc_start_secure_session_errors: u32,
69    pub epskc_secure_session_successes: u32,
70    pub epskc_secure_session_failures: u32,
71    pub epskc_commissioner_petitions: u32,
72    pub pskc_secure_session_successes: u32,
73    pub pskc_secure_session_failures: u32,
74    pub pskc_commissioner_petitions: u32,
75    pub mgmt_active_gets: u32,
76    pub mgmt_pending_gets: u32,
77}
78
79impl BorderAgentCounters {
80    unsafe fn from_ot_counters(
81        counters: *const otBorderAgentCounters,
82    ) -> Option<BorderAgentCounters> {
83        counters.as_ref().map(|&counters| BorderAgentCounters {
84            epskc_activations: counters.mEpskcActivations,
85            epskc_deactivation_clears: counters.mEpskcDeactivationClears,
86            epskc_deactivation_timeouts: counters.mEpskcDeactivationTimeouts,
87            epskc_deactivation_max_attempts: counters.mEpskcDeactivationMaxAttempts,
88            epskc_deactivation_disconnects: counters.mEpskcDeactivationDisconnects,
89            epskc_invalid_ba_state_errors: counters.mEpskcInvalidBaStateErrors,
90            epskc_invalid_args_errors: counters.mEpskcInvalidArgsErrors,
91            epskc_start_secure_session_errors: counters.mEpskcStartSecureSessionErrors,
92            epskc_secure_session_successes: counters.mEpskcSecureSessionSuccesses,
93            epskc_secure_session_failures: counters.mEpskcSecureSessionFailures,
94            epskc_commissioner_petitions: counters.mEpskcCommissionerPetitions,
95            pskc_secure_session_successes: counters.mPskcSecureSessionSuccesses,
96            pskc_secure_session_failures: counters.mPskcSecureSessionFailures,
97            pskc_commissioner_petitions: counters.mPskcCommissionerPetitions,
98            mgmt_active_gets: counters.mMgmtActiveGets,
99            mgmt_pending_gets: counters.mMgmtPendingGets,
100        })
101    }
102}
103
104/// Methods from the [OpenThread "Border Agent" Module][1].
105///
106/// [1]: https://openthread.io/reference/group/api-border-agent
107pub trait BorderAgent {
108    /// Functional equivalent of
109    /// [`otsys::otBorderAgentIsActive`](crate::otsys::otBorderAgentIsActive).
110    fn border_agent_is_active(&self) -> bool;
111
112    /// Functional equivalent of
113    /// [`otsys::otBorderAgentUdpPort`](crate::otsys::otBorderAgentGetUdpPort).
114    fn border_agent_get_udp_port(&self) -> u16;
115
116    /// Functional equivalent of
117    /// [`otsys::otBorderAgentGetMeshCoPServiceTxtData`](crate::otsys::otBorderAgentGetMeshCoPServiceTxtData).
118    fn border_agent_get_meshcop_service_txt_data(&self) -> Result<Vec<u8>>;
119
120    /// Functional equivalent of
121    /// [`otsys::otBorderAgentEphemeralKeyGetState`](crate::otsys::otBorderAgentEphemeralKeyGetState).
122    fn border_agent_ephemeral_key_get_state(&self) -> BorderAgentEphemeralKeyState;
123
124    /// Functional equivalent of
125    /// [`otsys::otBorderAgentEphemeralKeySetEnabled`](crate::otsys::otBorderAgentEphemeralKeySetEnabled).
126    fn border_agent_ephemeral_key_set_enabled(&self, enabled: bool);
127
128    /// Functional equivalent of
129    /// [`otsys::otBorderAgentEphemeralKeyStart`](crate::otsys::otBorderAgentEphemeralKeyStart).
130    fn border_agent_ephemeral_key_start(
131        &self,
132        key_string: &CStr,
133        timeout: u32,
134        port: u16,
135    ) -> Result;
136
137    /// Functional equivalent of
138    /// [`otsys::otBorderAgentEphemeralKeyStop`](crate::otsys::otBorderAgentEphemeralKeyStop).
139    fn border_agent_ephemeral_key_stop(&self);
140
141    /// Functional equivalent of
142    /// [`otsys::otBorderAgentEphemeralKeyGetUdpPort`](crate::otsys::otBorderAgentEphemeralKeyGetUdpPort).
143    fn border_agent_ephemeral_key_get_udp_port(&self) -> u16;
144
145    /// Functional equivalent of
146    /// [`otsys::otBorderAgentEphemeralKeySetCallback`](crate::otsys::otBorderAgentEphemeralKeySetCallback).
147    fn border_agent_set_ephemeral_key_callback<'a, F>(&'a self, f: Option<F>)
148    where
149        F: FnMut() + 'a;
150
151    /// Functional equivalent of
152    /// [`otsys::otBorderAgentGetCounters`](crate::otsys::otBorderAgentGetCounters).
153    fn border_agent_get_counters(&self) -> Option<BorderAgentCounters>;
154
155    /// [`otsys::otBorderAgentSetMeshCoPServiceChangedCallback`](crate::otsys::otBorderAgentSetMeshCoPServiceChangedCallback).
156    fn border_agent_set_meshcop_service_changed_fn<'a, F>(&'a self, f: Option<F>)
157    where
158        F: FnMut() + 'a;
159}
160
161impl<T: BorderAgent + Boxable> BorderAgent for ot::Box<T> {
162    fn border_agent_is_active(&self) -> bool {
163        self.as_ref().border_agent_is_active()
164    }
165
166    fn border_agent_get_udp_port(&self) -> u16 {
167        self.as_ref().border_agent_get_udp_port()
168    }
169
170    fn border_agent_get_meshcop_service_txt_data(&self) -> Result<Vec<u8>> {
171        self.as_ref().border_agent_get_meshcop_service_txt_data()
172    }
173
174    fn border_agent_ephemeral_key_get_state(&self) -> BorderAgentEphemeralKeyState {
175        self.as_ref().border_agent_ephemeral_key_get_state()
176    }
177
178    fn border_agent_ephemeral_key_set_enabled(&self, enabled: bool) {
179        self.as_ref().border_agent_ephemeral_key_set_enabled(enabled)
180    }
181
182    fn border_agent_ephemeral_key_start(&self, key: &CStr, timeout: u32, port: u16) -> Result {
183        self.as_ref().border_agent_ephemeral_key_start(key, timeout, port)
184    }
185
186    fn border_agent_ephemeral_key_stop(&self) {
187        self.as_ref().border_agent_ephemeral_key_stop()
188    }
189
190    fn border_agent_ephemeral_key_get_udp_port(&self) -> u16 {
191        self.as_ref().border_agent_ephemeral_key_get_udp_port()
192    }
193
194    fn border_agent_set_ephemeral_key_callback<'a, F>(&'a self, f: Option<F>)
195    where
196        F: FnMut() + 'a,
197    {
198        self.as_ref().border_agent_set_ephemeral_key_callback(f)
199    }
200
201    fn border_agent_get_counters(&self) -> Option<BorderAgentCounters> {
202        self.as_ref().border_agent_get_counters()
203    }
204
205    fn border_agent_set_meshcop_service_changed_fn<'a, F>(&'a self, f: Option<F>)
206    where
207        F: FnMut() + 'a,
208    {
209        self.as_ref().border_agent_set_meshcop_service_changed_fn(f)
210    }
211}
212
213impl BorderAgent for Instance {
214    fn border_agent_is_active(&self) -> bool {
215        unsafe { otBorderAgentIsActive(self.as_ot_ptr()) }
216    }
217
218    fn border_agent_get_udp_port(&self) -> u16 {
219        unsafe { otBorderAgentGetUdpPort(self.as_ot_ptr()) }
220    }
221
222    fn border_agent_get_meshcop_service_txt_data(&self) -> Result<Vec<u8>> {
223        let mut txt_data = otBorderAgentMeshCoPServiceTxtData::default();
224        let result: Result = Error::from(unsafe {
225            otBorderAgentGetMeshCoPServiceTxtData(self.as_ot_ptr(), &mut txt_data)
226        })
227        .into();
228        result?;
229        Ok(txt_data.mData[..txt_data.mLength as usize].to_vec())
230    }
231
232    fn border_agent_ephemeral_key_get_state(&self) -> BorderAgentEphemeralKeyState {
233        unsafe { otBorderAgentEphemeralKeyGetState(self.as_ot_ptr()).into() }
234    }
235
236    fn border_agent_ephemeral_key_set_enabled(&self, enabled: bool) {
237        unsafe { otBorderAgentEphemeralKeySetEnabled(self.as_ot_ptr(), enabled) }
238    }
239
240    fn border_agent_ephemeral_key_start(&self, key: &CStr, timeout: u32, port: u16) -> Result {
241        unsafe {
242            Error::from(otBorderAgentEphemeralKeyStart(
243                self.as_ot_ptr(),
244                key.as_ptr(),
245                timeout,
246                port,
247            ))
248            .into()
249        }
250    }
251
252    fn border_agent_ephemeral_key_stop(&self) {
253        unsafe { otBorderAgentEphemeralKeyStop(self.as_ot_ptr()) }
254    }
255
256    fn border_agent_ephemeral_key_get_udp_port(&self) -> u16 {
257        unsafe { otBorderAgentEphemeralKeyGetUdpPort(self.as_ot_ptr()) }
258    }
259
260    fn border_agent_set_ephemeral_key_callback<'a, F>(&'a self, f: Option<F>)
261    where
262        F: FnMut() + 'a,
263    {
264        unsafe extern "C" fn _border_agent_set_ephemeral_key_callback<'a, F: FnMut() + 'a>(
265            context: *mut ::std::os::raw::c_void,
266        ) {
267            trace!("_border_agent_set_ephemeral_key_callback");
268
269            // Reconstitute a reference to our closure.
270            let sender = &mut *(context as *mut F);
271
272            sender()
273        }
274
275        let (fn_ptr, fn_box, cb): (_, _, otBorderAgentEphemeralKeyCallback) = if let Some(f) = f {
276            let mut x = Box::new(f);
277
278            (
279                x.as_mut() as *mut F as *mut ::std::os::raw::c_void,
280                Some(x as Box<dyn FnMut() + 'a>),
281                Some(_border_agent_set_ephemeral_key_callback::<F>),
282            )
283        } else {
284            (std::ptr::null_mut() as *mut ::std::os::raw::c_void, None, None)
285        };
286
287        unsafe {
288            otBorderAgentEphemeralKeySetCallback(self.as_ot_ptr(), cb, fn_ptr);
289
290            // Make sure our object eventually gets cleaned up.
291            // Here we must also transmute our closure to have a 'static lifetime.
292            // We need to do this because the borrow checker cannot infer the
293            // proper lifetime for the singleton instance backing, but
294            // this is guaranteed by the API.
295            self.borrow_backing().ephemeral_key_callback.set(std::mem::transmute::<
296                Option<Box<dyn FnMut() + 'a>>,
297                Option<Box<dyn FnMut() + 'static>>,
298            >(fn_box));
299        }
300    }
301
302    fn border_agent_get_counters(&self) -> Option<BorderAgentCounters> {
303        unsafe { BorderAgentCounters::from_ot_counters(otBorderAgentGetCounters(self.as_ot_ptr())) }
304    }
305
306    fn border_agent_set_meshcop_service_changed_fn<'a, F>(&'a self, f: Option<F>)
307    where
308        F: FnMut() + 'a,
309    {
310        unsafe extern "C" fn _border_agent_set_meshcop_service_changed_callback<
311            'a,
312            F: FnMut() + 'a,
313        >(
314            context: *mut ::std::os::raw::c_void,
315        ) {
316            trace!("_border_agent_set_meshcop_service_changed_callback");
317
318            // Reconstitute a reference to our closure.
319            let sender = &mut *(context as *mut F);
320
321            sender()
322        }
323
324        let (fn_ptr, fn_box, cb): (_, _, otBorderAgentMeshCoPServiceChangedCallback) =
325            if let Some(f) = f {
326                let mut x = Box::new(f);
327
328                (
329                    x.as_mut() as *mut F as *mut ::std::os::raw::c_void,
330                    Some(x as Box<dyn FnMut() + 'a>),
331                    Some(_border_agent_set_meshcop_service_changed_callback::<F>),
332                )
333            } else {
334                (std::ptr::null_mut() as *mut ::std::os::raw::c_void, None, None)
335            };
336
337        unsafe {
338            otBorderAgentSetMeshCoPServiceChangedCallback(self.as_ot_ptr(), cb, fn_ptr);
339
340            // Make sure our object eventually gets cleaned up.
341            // Here we must also transmute our closure to have a 'static lifetime.
342            // We need to do this because the borrow checker cannot infer the
343            // proper lifetime for the singleton instance backing, but
344            // this is guaranteed by the API.
345            self.borrow_backing().meshcop_service_changed_callback.set(std::mem::transmute::<
346                Option<Box<dyn FnMut() + 'a>>,
347                Option<Box<dyn FnMut() + 'static>>,
348            >(fn_box));
349        }
350    }
351}
352
353/// Constructs a random key for use with ePSKc utilizing the algorithm from ot-br-posix.
354///
355/// [1]: https://github.com/openthread/ot-br-posix/blob/main/src/border_agent/border_agent.cpp
356pub fn create_ephemeral_key() -> Result<CString, anyhow::Error> {
357    let mut key: Vec<u8> = Vec::new();
358
359    // Generate a sequence of integers from 0-9 with equal probability.
360    for _ in 0..EPSKC_RANDOM_GEN_LEN {
361        loop {
362            let mut new_value: u8 = 0;
363            let rand_result = unsafe { otRandomCryptoFillBuffer(&mut new_value as *mut u8, 1) };
364
365            ot::Error::from(rand_result)
366                .into_result()
367                .map_err(|e| format_err!("Random number generation failed: {}", e))?;
368
369            if new_value < 250 {
370                key.push(b'0' + new_value % 10);
371                break;
372            }
373        }
374    }
375
376    // The final element in the key is a checksum.
377    let mut checksum_char: c_char = 0;
378    let checksum_result = unsafe {
379        otVerhoeffChecksumCalculate(key[0] as *const c_char, &mut checksum_char as *mut c_char)
380    };
381    ot::Error::from(checksum_result)
382        .into_result()
383        .map_err(|e| format_err!("Verhoeff checksum calculation failed: {}", e))?;
384
385    key.push(checksum_char as u8);
386    CString::new(key).map_err(|e| format_err!("Ephemeral key is not a valid string: {}", e))
387}
388
389#[cfg(test)]
390mod tests {
391    use super::*;
392
393    #[test]
394    fn test_counter_conversion_succeeds() {
395        let epskc_activations = 0;
396        let epskc_deactivation_clears = 1;
397        let epskc_deactivation_timeouts = 2;
398        let epskc_deactivation_max_attempts = 3;
399        let epskc_deactivation_disconnects = 4;
400        let epskc_invalid_ba_state_errors = 5;
401        let epskc_invalid_args_errors = 6;
402        let epskc_start_secure_session_errors = 7;
403        let epskc_secure_session_successes = 8;
404        let epskc_secure_session_failures = 9;
405        let epskc_commissioner_petitions = 10;
406        let pskc_secure_session_successes = 11;
407        let pskc_secure_session_failures = 12;
408        let pskc_commissioner_petitions = 13;
409        let mgmt_active_gets = 14;
410        let mgmt_pending_gets = 15;
411
412        let ot_counters = otBorderAgentCounters {
413            mEpskcActivations: epskc_activations,
414            mEpskcDeactivationClears: epskc_deactivation_clears,
415            mEpskcDeactivationTimeouts: epskc_deactivation_timeouts,
416            mEpskcDeactivationMaxAttempts: epskc_deactivation_max_attempts,
417            mEpskcDeactivationDisconnects: epskc_deactivation_disconnects,
418            mEpskcInvalidBaStateErrors: epskc_invalid_ba_state_errors,
419            mEpskcInvalidArgsErrors: epskc_invalid_args_errors,
420            mEpskcStartSecureSessionErrors: epskc_start_secure_session_errors,
421            mEpskcSecureSessionSuccesses: epskc_secure_session_successes,
422            mEpskcSecureSessionFailures: epskc_secure_session_failures,
423            mEpskcCommissionerPetitions: epskc_commissioner_petitions,
424            mPskcSecureSessionSuccesses: pskc_secure_session_successes,
425            mPskcSecureSessionFailures: pskc_secure_session_failures,
426            mPskcCommissionerPetitions: pskc_commissioner_petitions,
427            mMgmtActiveGets: mgmt_active_gets,
428            mMgmtPendingGets: mgmt_pending_gets,
429        };
430
431        let ot_counters_ptr: *const otBorderAgentCounters = &ot_counters;
432
433        let converted_counters = unsafe { BorderAgentCounters::from_ot_counters(ot_counters_ptr) }
434            .expect("Failed to convert OT Border Agent counters");
435
436        assert_eq!(
437            converted_counters,
438            BorderAgentCounters {
439                epskc_activations,
440                epskc_deactivation_clears,
441                epskc_deactivation_timeouts,
442                epskc_deactivation_max_attempts,
443                epskc_deactivation_disconnects,
444                epskc_invalid_ba_state_errors,
445                epskc_invalid_args_errors,
446                epskc_start_secure_session_errors,
447                epskc_secure_session_successes,
448                epskc_secure_session_failures,
449                epskc_commissioner_petitions,
450                pskc_secure_session_successes,
451                pskc_secure_session_failures,
452                pskc_commissioner_petitions,
453                mgmt_active_gets,
454                mgmt_pending_gets,
455            }
456        );
457    }
458
459    #[test]
460    fn test_counter_conversion_fails() {
461        let null_ptr: *const otBorderAgentCounters = std::ptr::null();
462        assert!(unsafe { BorderAgentCounters::from_ot_counters(null_ptr) }.is_none());
463    }
464}