openthread/ot/
border_router.rs

1// Copyright 2021 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::ot::WrongSize;
6use crate::prelude_internal::*;
7use core::pin::Pin;
8use core::task::{Context, Poll};
9use fuchsia_sync::Mutex;
10use std::sync::Arc;
11use std::task::Waker;
12
13/// Iterator type for external routes.
14#[allow(missing_debug_implementations)]
15pub struct LocalExternalRouteIterator<'a, T: ?Sized> {
16    ot_instance: &'a T,
17    ot_iter: otNetworkDataIterator,
18}
19
20impl<T: ?Sized + BorderRouter> Iterator for LocalExternalRouteIterator<'_, T> {
21    type Item = ExternalRouteConfig;
22    fn next(&mut self) -> Option<Self::Item> {
23        self.ot_instance.iter_next_local_external_route(&mut self.ot_iter)
24    }
25}
26
27/// Iterator type for on-mesh prefixes.
28#[allow(missing_debug_implementations)]
29pub struct LocalOnMeshPrefixIterator<'a, T: ?Sized> {
30    ot_instance: &'a T,
31    ot_iter: otNetworkDataIterator,
32}
33
34impl<T: ?Sized + BorderRouter> Iterator for LocalOnMeshPrefixIterator<'_, T> {
35    type Item = BorderRouterConfig;
36    fn next(&mut self) -> Option<Self::Item> {
37        self.ot_instance.iter_next_local_on_mesh_prefix(&mut self.ot_iter)
38    }
39}
40
41/// Methods from the [OpenThread "Border Router" Module][1].
42///
43/// [1]: https://openthread.io/reference/group/api-border-router
44pub trait BorderRouter {
45    /// Functional equivalent of
46    /// [`otsys::otBorderRouterAddRoute`](crate::otsys::otBorderRouterAddRoute).
47    fn add_external_route(&self, route: &ExternalRouteConfig) -> Result;
48
49    /// Functional equivalent of
50    /// [`otsys::otBorderRouterAddOnMeshPrefix`](crate::otsys::otBorderRouterAddOnMeshPrefix).
51    fn add_on_mesh_prefix(&self, route: &BorderRouterConfig) -> Result;
52
53    /// Functional equivalent of
54    /// [`otsys::otBorderRouterRemoveRoute`](crate::otsys::otBorderRouterRemoveRoute).
55    fn remove_external_route(&self, prefix: &Ip6Prefix) -> Result;
56
57    /// Functional equivalent of
58    /// [`otsys::otBorderRouterRemoveOnMeshPrefix`](crate::otsys::otBorderRouterRemoveOnMeshPrefix).
59    fn remove_on_mesh_prefix(&self, prefix: &Ip6Prefix) -> Result;
60
61    /// Functional equivalent of
62    /// [`otsys::otBorderRouterRegister`](crate::otsys::otBorderRouterRegister).
63    fn border_router_register(&self) -> Result;
64
65    /// Functional equivalent of
66    /// [`otsys::otBorderRoutingInit`](crate::otsys::otBorderRoutingInit).
67    fn border_routing_init(&self, infra_if: u32, infra_is_running: bool) -> Result;
68
69    /// Functional equivalent of
70    /// [`otsys::otBorderRoutingSetEnabled`](crate::otsys::otBorderRoutingSetEnabled).
71    fn border_routing_set_enabled(&self, enabled: bool) -> Result;
72
73    /// Functional equivalent of
74    /// [`otsys::otBorderRoutingDhcp6PdSetEnabled`](crate::otsys::otBorderRoutingDhcp6PdSetEnabled).
75    fn border_routing_dhcp6_pd_set_enabled(&self, enabled: bool);
76
77    /// Functional equivalent of
78    /// [`otsys::otBorderRoutingDhcp6PdGetState`](crate::otsys::otBorderRoutingDhcp6PdGetState).
79    fn border_routing_dhcp6_pd_get_state(&self) -> BorderRoutingDhcp6PdState;
80
81    /// Functional equivalent of
82    /// [`otsys::otBorderRoutingDhcp6PdSetRequestCallback`](crate::otsys::otBorderRoutingDhcp6PdSetRequestCallback).
83    fn border_routing_dhcp6_pd_set_request_fn<'a, F>(&'a self, f: Option<F>)
84    where
85        F: FnMut(BorderRoutingDhcp6PdState) + 'a;
86
87    /// Get the DHCPv6 PD state change stream
88    fn border_routing_dhcp6_pd_state_change_stream(&self)
89    -> BorderRoutingDhcp6PdStateChangedStream;
90
91    /// Functional equivalent of
92    /// [`otsys::otBorderRoutingGetPdOmrPrefix`](crate::otsys::otBorderRoutingGetPdOmrPrefix).
93    fn border_routing_get_pd_omr_prefix(&self) -> Result<ot::BorderRoutingPrefixTableEntry>;
94
95    /// Functional equivalent of
96    /// [`otsys::otBorderRoutingGetOmrPrefix`](crate::otsys::otBorderRoutingGetOmrPrefix).
97    fn border_routing_get_omr_prefix(&self) -> Result<ot::Ip6Prefix>;
98
99    /// Functional equivalent of
100    /// [`otsys::otBorderRoutingGetOnLinkPrefix`](crate::otsys::otBorderRoutingGetOnLinkPrefix).
101    fn border_routing_get_on_link_prefix(&self) -> Result<ot::Ip6Prefix>;
102
103    /// Functional equivalent of
104    /// [`otsys::otPlatBorderRoutingProcessIcmp6Ra`](crate::otsys::otPlatBorderRoutingProcessIcmp6Ra).
105    fn border_routing_process_icmp6_ra(&self, message: &[u8]) -> Result<(), WrongSize>;
106
107    /// Functional equivalent of
108    /// [`otsys::otBorderRoutingGetPdProcessedRaInfo`](crate::otsys::otBorderRoutingGetPdProcessedRaInfo).
109    fn border_routing_get_pd_processed_ra_info(&self) -> PdProcessedRaInfo;
110
111    /// Functional equivalent of
112    /// [`otsys::otBorderRoutingIsMultiAilDetected`](crate::otsys::otBorderRoutingIsMultiAilDetected).
113    fn border_routing_is_multi_ail_detected(&self) -> bool;
114
115    /// Functional equivalent of
116    /// [`otsys::otBorderRouterGetNextRoute`](crate::otsys::otBorderRouterGetNextRoute).
117    // TODO: Determine if the underlying implementation of
118    //       this method has undefined behavior when network data
119    //       is being mutated while iterating. If it is undefined,
120    //       we may need to make it unsafe and provide a safe method
121    //       that collects the results.
122    fn iter_next_local_external_route(
123        &self,
124        ot_iter: &mut otNetworkDataIterator,
125    ) -> Option<ExternalRouteConfig>;
126
127    /// Functional equivalent of
128    /// [`otsys::otBorderRouterGetNextOnMeshPrefix`](crate::otsys::otBorderRouterGetNextOnMeshPrefix).
129    // TODO: Determine if the underlying implementation of
130    //       this method has undefined behavior when network data
131    //       is being mutated while iterating. If it is undefined,
132    //       we may need to make it unsafe and provide a safe method
133    //       that collects the results.
134    fn iter_next_local_on_mesh_prefix(
135        &self,
136        ot_iter: &mut otNetworkDataIterator,
137    ) -> Option<BorderRouterConfig>;
138
139    /// Returns an iterator for iterating over external routes.
140    fn iter_local_external_routes(&self) -> LocalExternalRouteIterator<'_, Self> {
141        LocalExternalRouteIterator { ot_instance: self, ot_iter: OT_NETWORK_DATA_ITERATOR_INIT }
142    }
143
144    /// Returns an iterator for iterating over on-mesh prefixes
145    fn iter_local_on_mesh_prefixes(&self) -> LocalOnMeshPrefixIterator<'_, Self> {
146        LocalOnMeshPrefixIterator { ot_instance: self, ot_iter: OT_NETWORK_DATA_ITERATOR_INIT }
147    }
148}
149
150impl<T: BorderRouter + Boxable> BorderRouter for ot::Box<T> {
151    fn add_external_route(&self, route: &ExternalRouteConfig) -> Result {
152        self.as_ref().add_external_route(route)
153    }
154
155    fn add_on_mesh_prefix(&self, route: &BorderRouterConfig) -> Result {
156        self.as_ref().add_on_mesh_prefix(route)
157    }
158
159    fn remove_external_route(&self, prefix: &Ip6Prefix) -> Result {
160        self.as_ref().remove_external_route(prefix)
161    }
162
163    fn remove_on_mesh_prefix(&self, prefix: &Ip6Prefix) -> Result {
164        self.as_ref().remove_on_mesh_prefix(prefix)
165    }
166
167    fn border_router_register(&self) -> Result {
168        self.as_ref().border_router_register()
169    }
170
171    fn border_routing_init(&self, infra_if: u32, infra_is_running: bool) -> Result {
172        self.as_ref().border_routing_init(infra_if, infra_is_running)
173    }
174
175    fn border_routing_set_enabled(&self, enabled: bool) -> Result {
176        self.as_ref().border_routing_set_enabled(enabled)
177    }
178
179    fn border_routing_dhcp6_pd_set_enabled(&self, enabled: bool) {
180        self.as_ref().border_routing_dhcp6_pd_set_enabled(enabled)
181    }
182
183    fn border_routing_dhcp6_pd_get_state(&self) -> BorderRoutingDhcp6PdState {
184        self.as_ref().border_routing_dhcp6_pd_get_state()
185    }
186
187    fn border_routing_dhcp6_pd_set_request_fn<'a, F>(&'a self, f: Option<F>)
188    where
189        F: FnMut(BorderRoutingDhcp6PdState) + 'a,
190    {
191        self.as_ref().border_routing_dhcp6_pd_set_request_fn(f)
192    }
193
194    fn border_routing_dhcp6_pd_state_change_stream(
195        &self,
196    ) -> BorderRoutingDhcp6PdStateChangedStream {
197        self.as_ref().border_routing_dhcp6_pd_state_change_stream()
198    }
199
200    fn border_routing_get_pd_omr_prefix(&self) -> Result<ot::BorderRoutingPrefixTableEntry> {
201        self.as_ref().border_routing_get_pd_omr_prefix()
202    }
203
204    fn border_routing_get_omr_prefix(&self) -> Result<ot::Ip6Prefix> {
205        self.as_ref().border_routing_get_omr_prefix()
206    }
207
208    fn border_routing_get_on_link_prefix(&self) -> Result<ot::Ip6Prefix> {
209        self.as_ref().border_routing_get_on_link_prefix()
210    }
211
212    fn border_routing_process_icmp6_ra(&self, message: &[u8]) -> Result<(), WrongSize> {
213        self.as_ref().border_routing_process_icmp6_ra(message)
214    }
215
216    fn border_routing_get_pd_processed_ra_info(&self) -> PdProcessedRaInfo {
217        self.as_ref().border_routing_get_pd_processed_ra_info()
218    }
219
220    fn border_routing_is_multi_ail_detected(&self) -> bool {
221        self.as_ref().border_routing_is_multi_ail_detected()
222    }
223
224    fn iter_next_local_external_route(
225        &self,
226        ot_iter: &mut otNetworkDataIterator,
227    ) -> Option<ExternalRouteConfig> {
228        self.as_ref().iter_next_local_external_route(ot_iter)
229    }
230
231    fn iter_next_local_on_mesh_prefix(
232        &self,
233        ot_iter: &mut otNetworkDataIterator,
234    ) -> Option<BorderRouterConfig> {
235        self.as_ref().iter_next_local_on_mesh_prefix(ot_iter)
236    }
237}
238
239impl BorderRouter for Instance {
240    fn add_external_route(&self, route: &ExternalRouteConfig) -> Result {
241        Error::from(unsafe { otBorderRouterAddRoute(self.as_ot_ptr(), route.as_ot_ptr()) }).into()
242    }
243
244    fn add_on_mesh_prefix(&self, route: &BorderRouterConfig) -> Result {
245        Error::from(unsafe { otBorderRouterAddOnMeshPrefix(self.as_ot_ptr(), route.as_ot_ptr()) })
246            .into()
247    }
248
249    fn remove_external_route(&self, prefix: &Ip6Prefix) -> Result {
250        Error::from(unsafe { otBorderRouterRemoveRoute(self.as_ot_ptr(), prefix.as_ot_ptr()) })
251            .into()
252    }
253
254    fn remove_on_mesh_prefix(&self, prefix: &Ip6Prefix) -> Result {
255        Error::from(unsafe {
256            otBorderRouterRemoveOnMeshPrefix(self.as_ot_ptr(), prefix.as_ot_ptr())
257        })
258        .into()
259    }
260
261    fn border_router_register(&self) -> Result {
262        Error::from(unsafe { otBorderRouterRegister(self.as_ot_ptr()) }).into()
263    }
264
265    fn border_routing_init(&self, infra_if: u32, infra_is_running: bool) -> Result {
266        Error::from(unsafe { otBorderRoutingInit(self.as_ot_ptr(), infra_if, infra_is_running) })
267            .into()
268    }
269
270    fn border_routing_set_enabled(&self, enabled: bool) -> Result {
271        Error::from(unsafe { otBorderRoutingSetEnabled(self.as_ot_ptr(), enabled) }).into()
272    }
273
274    fn border_routing_dhcp6_pd_set_enabled(&self, enabled: bool) {
275        unsafe { otBorderRoutingDhcp6PdSetEnabled(self.as_ot_ptr(), enabled) }
276    }
277
278    fn border_routing_dhcp6_pd_get_state(&self) -> BorderRoutingDhcp6PdState {
279        BorderRoutingDhcp6PdState::from_isize(unsafe {
280            otBorderRoutingDhcp6PdGetState(self.as_ot_ptr())
281        } as isize)
282        .unwrap_or(BorderRoutingDhcp6PdState::Disabled)
283    }
284
285    fn border_routing_dhcp6_pd_set_request_fn<'a, F>(&'a self, f: Option<F>)
286    where
287        F: FnMut(BorderRoutingDhcp6PdState) + 'a,
288    {
289        unsafe extern "C" fn _ot_border_routing_dhcp6_pd_state_change_callback<
290            'a,
291            F: FnMut(BorderRoutingDhcp6PdState) + 'a,
292        >(
293            state: otBorderRoutingDhcp6PdState,
294            context: *mut ::std::os::raw::c_void,
295        ) {
296            trace!("_ot_border_routing_dhcp6_pd_state_change_callback");
297
298            // Convert `otBorderRoutingDhcp6PdState` to `BorderRoutingDhcp6PdState`
299            let state = BorderRoutingDhcp6PdState::from(state);
300
301            // Reconstitute a reference to our closure.
302            let sender = &mut *(context as *mut F);
303
304            sender(state)
305        }
306
307        let (fn_ptr, fn_box, cb): (_, _, otBorderRoutingRequestDhcp6PdCallback) = if let Some(f) = f
308        {
309            let mut x = Box::new(f);
310
311            (
312                x.as_mut() as *mut F as *mut ::std::os::raw::c_void,
313                Some(x as Box<dyn FnMut(BorderRoutingDhcp6PdState) + 'a>),
314                Some(_ot_border_routing_dhcp6_pd_state_change_callback::<F>),
315            )
316        } else {
317            (std::ptr::null_mut() as *mut ::std::os::raw::c_void, None, None)
318        };
319
320        unsafe {
321            otBorderRoutingDhcp6PdSetRequestCallback(self.as_ot_ptr(), cb, fn_ptr);
322
323            // Make sure our object eventually gets cleaned up.
324            // Here we must also transmute our closure to have a 'static lifetime.
325            // We need to do this because the borrow checker cannot infer the
326            // proper lifetime for the singleton instance backing, but
327            // this is guaranteed by the API.
328            self.borrow_backing().dhcp6pd_state_change_callback_fn.set(std::mem::transmute::<
329                Option<Box<dyn FnMut(BorderRoutingDhcp6PdState) + 'a>>,
330                Option<Box<dyn FnMut(BorderRoutingDhcp6PdState) + 'static>>,
331            >(fn_box));
332        }
333    }
334
335    fn border_routing_dhcp6_pd_state_change_stream(
336        &self,
337    ) -> BorderRoutingDhcp6PdStateChangedStream {
338        let state = Arc::new(Mutex::new((None, futures::task::noop_waker())));
339
340        let state_copy = state.clone();
341
342        self.border_routing_dhcp6_pd_set_request_fn(Some(
343            move |pd_state: BorderRoutingDhcp6PdState| {
344                let mut borrowed = state_copy.lock();
345                borrowed.0 = Some(pd_state);
346                borrowed.1.wake_by_ref();
347            },
348        ));
349
350        BorderRoutingDhcp6PdStateChangedStream(state)
351    }
352
353    fn border_routing_get_pd_omr_prefix(&self) -> Result<ot::BorderRoutingPrefixTableEntry> {
354        let mut ret: BorderRoutingPrefixTableEntry = Default::default();
355        Error::from(unsafe {
356            otBorderRoutingGetPdOmrPrefix(self.as_ot_ptr(), ret.as_ot_mut_ptr())
357        })
358        .into_result()?;
359        Ok(ret)
360    }
361
362    fn border_routing_get_omr_prefix(&self) -> Result<ot::Ip6Prefix> {
363        let mut ret: Ip6Prefix = Default::default();
364        Error::from(unsafe { otBorderRoutingGetOmrPrefix(self.as_ot_ptr(), ret.as_ot_mut_ptr()) })
365            .into_result()?;
366        Ok(ret)
367    }
368
369    fn border_routing_get_on_link_prefix(&self) -> Result<ot::Ip6Prefix> {
370        let mut ret: Ip6Prefix = Default::default();
371        Error::from(unsafe {
372            otBorderRoutingGetOnLinkPrefix(self.as_ot_ptr(), ret.as_ot_mut_ptr())
373        })
374        .into_result()?;
375        Ok(ret)
376    }
377
378    fn border_routing_process_icmp6_ra(&self, message: &[u8]) -> Result<(), WrongSize> {
379        unsafe {
380            otPlatBorderRoutingProcessIcmp6Ra(
381                self.as_ot_ptr(),
382                message.as_ptr(),
383                message.len().try_into().map_err(|_| WrongSize)?,
384            )
385        }
386        Ok(())
387    }
388
389    fn border_routing_get_pd_processed_ra_info(&self) -> PdProcessedRaInfo {
390        let mut info = PdProcessedRaInfo::default();
391        unsafe {
392            otBorderRoutingGetPdProcessedRaInfo(self.as_ot_ptr(), info.as_ot_mut_ptr());
393        }
394        info
395    }
396
397    fn border_routing_is_multi_ail_detected(&self) -> bool {
398        unsafe { otBorderRoutingIsMultiAilDetected(self.as_ot_ptr()) }
399    }
400
401    fn iter_next_local_external_route(
402        &self,
403        ot_iter: &mut otNetworkDataIterator,
404    ) -> Option<ExternalRouteConfig> {
405        unsafe {
406            let mut ret = ExternalRouteConfig::default();
407            match Error::from(otBorderRouterGetNextRoute(
408                self.as_ot_ptr(),
409                ot_iter as *mut otNetworkDataIterator,
410                ret.as_ot_mut_ptr(),
411            )) {
412                Error::NotFound => None,
413                Error::None => Some(ret),
414                err => panic!("Unexpected error from otBorderRouterGetNextRoute: {err:?}"),
415            }
416        }
417    }
418
419    fn iter_next_local_on_mesh_prefix(
420        &self,
421        ot_iter: &mut otNetworkDataIterator,
422    ) -> Option<BorderRouterConfig> {
423        unsafe {
424            let mut ret = BorderRouterConfig::default();
425            match Error::from(otBorderRouterGetNextOnMeshPrefix(
426                self.as_ot_ptr(),
427                ot_iter as *mut otNetworkDataIterator,
428                ret.as_ot_mut_ptr(),
429            )) {
430                Error::NotFound => None,
431                Error::None => Some(ret),
432                err => panic!("Unexpected error from otBorderRouterGetNextOnMeshPrefix: {err:?}"),
433            }
434        }
435    }
436}
437
438/// Stream for getting state changed events.
439#[derive(Debug, Clone)]
440pub struct BorderRoutingDhcp6PdStateChangedStream(
441    Arc<Mutex<(Option<BorderRoutingDhcp6PdState>, Waker)>>,
442);
443
444impl Stream for BorderRoutingDhcp6PdStateChangedStream {
445    type Item = BorderRoutingDhcp6PdState;
446
447    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
448        let mut state = self.0.lock();
449
450        state.1 = cx.waker().clone();
451
452        if let Some(pd_state) = state.0.take() {
453            Poll::Ready(Some(pd_state))
454        } else {
455            Poll::Pending
456        }
457    }
458}