Skip to main content

sl4f_lib/netstack/
facade.rs

1// Copyright 2019 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 anyhow::{Context as _, Error};
6use component_debug::dirs::{OpenDirType, connect_to_instance_protocol};
7use once_cell::sync::OnceCell;
8use serde::Serialize;
9
10fn serialize_ipv4<S: serde::Serializer>(
11    addresses: &Vec<std::net::Ipv4Addr>,
12    serializer: S,
13) -> Result<S::Ok, S::Error> {
14    serializer.collect_seq(addresses.iter().map(|address| address.octets()))
15}
16
17fn serialize_ipv6<S: serde::Serializer>(
18    addresses: &Vec<std::net::Ipv6Addr>,
19    serializer: S,
20) -> Result<S::Ok, S::Error> {
21    serializer.collect_seq(addresses.iter().map(|address| address.octets()))
22}
23
24fn serialize_mac<S: serde::Serializer>(
25    mac: &Option<fidl_fuchsia_net_ext::MacAddress>,
26    serializer: S,
27) -> Result<S::Ok, S::Error> {
28    match mac {
29        None => serializer.serialize_none(),
30        Some(fidl_fuchsia_net_ext::MacAddress { octets }) => serializer.collect_seq(octets.iter()),
31    }
32}
33
34#[derive(Serialize)]
35enum DeviceClass {
36    Loopback,
37    Blackhole,
38    Virtual,
39    Ethernet,
40    WlanClient,
41    Ppp,
42    Bridge,
43    WlanAp,
44    Lowpan,
45}
46
47#[derive(Serialize)]
48pub struct Properties {
49    id: u64,
50    name: String,
51    device_class: DeviceClass,
52    online: bool,
53    #[serde(serialize_with = "serialize_ipv4")]
54    ipv4_addresses: Vec<std::net::Ipv4Addr>,
55    #[serde(serialize_with = "serialize_ipv6")]
56    ipv6_addresses: Vec<std::net::Ipv6Addr>,
57    #[serde(serialize_with = "serialize_mac")]
58    mac: Option<fidl_fuchsia_net_ext::MacAddress>,
59}
60
61impl
62    From<(
63        fidl_fuchsia_net_interfaces_ext::Properties<fidl_fuchsia_net_interfaces_ext::AllInterest>,
64        Option<fidl_fuchsia_net::MacAddress>,
65    )> for Properties
66{
67    fn from(
68        t: (
69            fidl_fuchsia_net_interfaces_ext::Properties<
70                fidl_fuchsia_net_interfaces_ext::AllInterest,
71            >,
72            Option<fidl_fuchsia_net::MacAddress>,
73        ),
74    ) -> Self {
75        use itertools::Itertools as _;
76
77        let (
78            fidl_fuchsia_net_interfaces_ext::Properties {
79                id,
80                name,
81                port_class,
82                online,
83                addresses,
84                has_default_ipv4_route: _,
85                has_default_ipv6_route: _,
86                port_identity_koid: _,
87            },
88            mac,
89        ) = t;
90        let device_class = match port_class {
91            fidl_fuchsia_net_interfaces_ext::PortClass::Loopback => DeviceClass::Loopback,
92            fidl_fuchsia_net_interfaces_ext::PortClass::Blackhole => DeviceClass::Blackhole,
93            fidl_fuchsia_net_interfaces_ext::PortClass::Virtual => DeviceClass::Virtual,
94            fidl_fuchsia_net_interfaces_ext::PortClass::Ethernet => DeviceClass::Ethernet,
95            fidl_fuchsia_net_interfaces_ext::PortClass::WlanClient => DeviceClass::WlanClient,
96            fidl_fuchsia_net_interfaces_ext::PortClass::WlanAp => DeviceClass::WlanAp,
97            fidl_fuchsia_net_interfaces_ext::PortClass::Ppp => DeviceClass::Ppp,
98            fidl_fuchsia_net_interfaces_ext::PortClass::Bridge => DeviceClass::Bridge,
99            fidl_fuchsia_net_interfaces_ext::PortClass::Lowpan => DeviceClass::Lowpan,
100        };
101        let (ipv4_addresses, ipv6_addresses) =
102            addresses.into_iter().partition_map::<_, _, _, std::net::Ipv4Addr, std::net::Ipv6Addr>(
103                |fidl_fuchsia_net_interfaces_ext::Address {
104                     addr,
105                     valid_until: _,
106                     preferred_lifetime_info: _,
107                     assignment_state,
108                 }| {
109                    // Event stream is created with `IncludedAddresses::OnlyAssigned`.
110                    assert_eq!(
111                        assignment_state,
112                        fidl_fuchsia_net_interfaces::AddressAssignmentState::Assigned,
113                        "Support for unassigned addresses have not been implemented",
114                    );
115                    let fidl_fuchsia_net_ext::Subnet { addr, prefix_len: _ } = addr.into();
116                    let fidl_fuchsia_net_ext::IpAddress(addr) = addr;
117                    match addr {
118                        std::net::IpAddr::V4(addr) => itertools::Either::Left(addr),
119                        std::net::IpAddr::V6(addr) => itertools::Either::Right(addr),
120                    }
121                },
122            );
123        Self {
124            id: id.get(),
125            name,
126            device_class,
127            online,
128            ipv4_addresses,
129            ipv6_addresses,
130            mac: mac.map(Into::into),
131        }
132    }
133}
134
135/// Network stack operations.
136#[derive(Debug, Default)]
137pub struct NetstackFacade {
138    interfaces_state: OnceCell<fidl_fuchsia_net_interfaces::StateProxy>,
139    root_interfaces: OnceCell<fidl_fuchsia_net_root::InterfacesProxy>,
140}
141
142async fn get_netstack_proxy<P: fidl::endpoints::DiscoverableProtocolMarker>()
143-> Result<P::Proxy, Error> {
144    let query =
145        fuchsia_component::client::connect_to_protocol::<fidl_fuchsia_sys2::RealmQueryMarker>()?;
146    let moniker = "./core/network/netstack".try_into()?;
147    let proxy = connect_to_instance_protocol::<P>(&moniker, OpenDirType::Exposed, &query).await?;
148    Ok(proxy)
149}
150
151impl NetstackFacade {
152    async fn get_interfaces_state(
153        &self,
154    ) -> Result<&fidl_fuchsia_net_interfaces::StateProxy, Error> {
155        let Self { interfaces_state, root_interfaces: _ } = self;
156        if let Some(state_proxy) = interfaces_state.get() {
157            Ok(state_proxy)
158        } else {
159            let state_proxy =
160                get_netstack_proxy::<fidl_fuchsia_net_interfaces::StateMarker>().await?;
161            interfaces_state.set(state_proxy).unwrap();
162            let state_proxy = interfaces_state.get().unwrap();
163            Ok(state_proxy)
164        }
165    }
166
167    async fn get_root_interfaces(&self) -> Result<&fidl_fuchsia_net_root::InterfacesProxy, Error> {
168        let Self { interfaces_state: _, root_interfaces } = self;
169        if let Some(interfaces_proxy) = root_interfaces.get() {
170            Ok(interfaces_proxy)
171        } else {
172            let interfaces_proxy =
173                get_netstack_proxy::<fidl_fuchsia_net_root::InterfacesMarker>().await?;
174            root_interfaces.set(interfaces_proxy).unwrap();
175            let interfaces_proxy = root_interfaces.get().unwrap();
176            Ok(interfaces_proxy)
177        }
178    }
179
180    async fn get_control(
181        &self,
182        id: u64,
183    ) -> Result<fidl_fuchsia_net_interfaces_ext::admin::Control, Error> {
184        let root_interfaces = self.get_root_interfaces().await?;
185        let (control, server_end) =
186            fidl_fuchsia_net_interfaces_ext::admin::Control::create_endpoints()
187                .context("create admin control endpoints")?;
188        let () = root_interfaces.get_admin(id, server_end).context("send get admin request")?;
189        Ok(control)
190    }
191
192    pub async fn enable_interface(&self, id: u64) -> Result<(), Error> {
193        let control = self.get_control(id).await?;
194        let _did_enable: bool = control
195            .enable()
196            .await
197            .map_err(anyhow::Error::new)
198            .and_then(|res| {
199                res.map_err(|e: fidl_fuchsia_net_interfaces_admin::ControlEnableError| {
200                    anyhow::anyhow!("{:?}", e)
201                })
202            })
203            .with_context(|| format!("failed to enable interface {}", id))?;
204        Ok(())
205    }
206
207    pub async fn disable_interface(&self, id: u64) -> Result<(), Error> {
208        let control = self.get_control(id).await?;
209        let _did_disable: bool = control
210            .disable()
211            .await
212            .map_err(anyhow::Error::new)
213            .and_then(|res| {
214                res.map_err(|e: fidl_fuchsia_net_interfaces_admin::ControlDisableError| {
215                    anyhow::anyhow!("{:?}", e)
216                })
217            })
218            .with_context(|| format!("failed to disable interface {}", id))?;
219        Ok(())
220    }
221
222    pub async fn list_interfaces(&self) -> Result<Vec<Properties>, Error> {
223        let interfaces_state = self.get_interfaces_state().await?;
224        let root_interfaces = self.get_root_interfaces().await?;
225        // Only `OnlyAssigned` is implemented; additional work is required to
226        // support unassigned addresses.
227        let stream = fidl_fuchsia_net_interfaces_ext::event_stream_from_state(
228            interfaces_state,
229            Default::default(),
230        )?;
231        let response = fidl_fuchsia_net_interfaces_ext::existing(
232            stream,
233            std::collections::HashMap::<u64, _>::new(),
234        )
235        .await?;
236        let response = response.into_values().map(
237            |fidl_fuchsia_net_interfaces_ext::PropertiesAndState { properties, state: () }| async {
238                match root_interfaces.get_mac(properties.id.get()).await? {
239                    Ok(mac) => {
240                        let mac = mac.map(|boxed_mac| *boxed_mac);
241                        let view: Properties = (properties, mac).into();
242                        Ok::<_, Error>(Some(view))
243                    }
244                    Err(fidl_fuchsia_net_root::InterfacesGetMacError::NotFound) => {
245                        // Interface with given id not found; this occurs when state
246                        // is reported for an interface that has since been removed.
247                        Ok::<_, Error>(None)
248                    }
249                }
250            },
251        );
252        let mut response: Vec<Properties> =
253            futures::future::try_join_all(response).await?.into_iter().filter_map(|r| r).collect();
254        let () = response.sort_by_key(|&Properties { id, .. }| id);
255        Ok(response)
256    }
257
258    async fn get_addresses<T, F: Copy + FnMut(fidl_fuchsia_net::Subnet) -> Option<T>>(
259        &self,
260        f: F,
261    ) -> Result<Vec<T>, Error> {
262        let mut output = Vec::new();
263
264        let interfaces_state = self.get_interfaces_state().await?;
265        let (watcher, server) =
266            fidl::endpoints::create_proxy::<fidl_fuchsia_net_interfaces::WatcherMarker>();
267        let () = interfaces_state
268            .get_watcher(&fidl_fuchsia_net_interfaces::WatcherOptions::default(), server)?;
269
270        loop {
271            match watcher.watch().await? {
272                fidl_fuchsia_net_interfaces::Event::Existing(
273                    fidl_fuchsia_net_interfaces::Properties { addresses, .. },
274                ) => {
275                    let addresses = addresses.unwrap();
276                    let () = output.extend(
277                        addresses
278                            .into_iter()
279                            .map(
280                                |fidl_fuchsia_net_interfaces::Address {
281                                     addr,
282                                     valid_until: _,
283                                     ..
284                                 }| addr.unwrap(),
285                            )
286                            .filter_map(f),
287                    );
288                }
289                fidl_fuchsia_net_interfaces::Event::Idle(fidl_fuchsia_net_interfaces::Empty {}) => {
290                    break;
291                }
292                event => unreachable!("{:?}", event),
293            }
294        }
295
296        Ok(output)
297    }
298
299    pub fn get_ipv6_addresses(
300        &self,
301    ) -> impl std::future::Future<Output = Result<Vec<std::net::Ipv6Addr>, Error>> + '_ {
302        self.get_addresses(|addr| {
303            let fidl_fuchsia_net_ext::Subnet { addr, prefix_len: _ } = addr.into();
304            let fidl_fuchsia_net_ext::IpAddress(addr) = addr;
305            match addr {
306                std::net::IpAddr::V4(_) => None,
307                std::net::IpAddr::V6(addr) => Some(addr),
308            }
309        })
310    }
311
312    pub fn get_link_local_ipv6_addresses(
313        &self,
314    ) -> impl std::future::Future<Output = Result<Vec<std::net::Ipv6Addr>, Error>> + '_ {
315        use futures::TryFutureExt as _;
316
317        self.get_ipv6_addresses().map_ok(|addresses| {
318            addresses.into_iter().filter(|address| address.octets()[..2] == [0xfe, 0x80]).collect()
319        })
320    }
321}
322
323#[cfg(test)]
324mod tests {
325    use super::*;
326    use fidl_fuchsia_net as fnet;
327    use fidl_fuchsia_net_interfaces as finterfaces;
328    use fuchsia_async as fasync;
329    use futures::StreamExt as _;
330
331    struct MockStateTester {
332        expected_state: Vec<Box<dyn FnOnce(finterfaces::WatcherRequest) + Send + 'static>>,
333    }
334
335    impl MockStateTester {
336        fn new() -> Self {
337            Self { expected_state: vec![] }
338        }
339
340        pub fn create_facade_and_serve_state(
341            self,
342        ) -> (NetstackFacade, impl std::future::Future<Output = ()>) {
343            let (interfaces_state, stream_future) = self.build_state_and_watcher();
344            (
345                NetstackFacade { interfaces_state: interfaces_state.into(), ..Default::default() },
346                stream_future,
347            )
348        }
349
350        fn push_state(
351            mut self,
352            request: impl FnOnce(finterfaces::WatcherRequest) + Send + 'static,
353        ) -> Self {
354            self.expected_state.push(Box::new(request));
355            self
356        }
357
358        fn build_state_and_watcher(
359            self,
360        ) -> (finterfaces::StateProxy, impl std::future::Future<Output = ()>) {
361            let (proxy, mut stream) =
362                fidl::endpoints::create_proxy_and_stream::<finterfaces::StateMarker>();
363            let stream_fut = async move {
364                match stream.next().await {
365                    Some(Ok(finterfaces::StateRequest::GetWatcher { watcher, .. })) => {
366                        let mut into_stream = watcher.into_stream();
367                        for expected in self.expected_state {
368                            let () = expected(into_stream.next().await.unwrap().unwrap());
369                        }
370                        let finterfaces::WatcherRequest::Watch { responder } =
371                            into_stream.next().await.unwrap().unwrap();
372                        let () = responder
373                            .send(&finterfaces::Event::Idle(finterfaces::Empty {}))
374                            .unwrap();
375                    }
376                    err => panic!("Error in request handler: {:?}", err),
377                }
378            };
379            (proxy, stream_fut)
380        }
381
382        fn expect_get_ipv6_addresses(self, result: Vec<fnet::Subnet>) -> Self {
383            let addresses = result
384                .into_iter()
385                .map(|addr| finterfaces::Address { addr: Some(addr), ..Default::default() })
386                .collect();
387            self.push_state(move |req| match req {
388                finterfaces::WatcherRequest::Watch { responder } => responder
389                    .send(&finterfaces::Event::Existing(finterfaces::Properties {
390                        addresses: Some(addresses),
391                        ..Default::default()
392                    }))
393                    .unwrap(),
394            })
395        }
396    }
397
398    #[fasync::run_singlethreaded(test)]
399    async fn test_get_ipv6_addresses() {
400        let ipv6_octets = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
401
402        let ipv6_address = fnet::Subnet {
403            addr: fnet::IpAddress::Ipv6(fnet::Ipv6Address { addr: ipv6_octets }),
404            // NB: prefix length is ignored, use invalid value to prove it.
405            prefix_len: 137,
406        };
407        let ipv4_address = fnet::Subnet {
408            addr: fnet::IpAddress::Ipv4(fnet::Ipv4Address { addr: [0, 1, 2, 3] }),
409            // NB: prefix length is ignored, use invalid value to prove it.
410            prefix_len: 139,
411        };
412        let all_addresses = [ipv6_address.clone(), ipv4_address.clone()];
413        let (facade, stream_fut) = MockStateTester::new()
414            .expect_get_ipv6_addresses(all_addresses.to_vec())
415            .create_facade_and_serve_state();
416        let facade_fut = async move {
417            let result_address: Vec<_> = facade.get_ipv6_addresses().await.unwrap();
418            assert_eq!(result_address, [std::net::Ipv6Addr::from(ipv6_octets)]);
419        };
420        futures::future::join(facade_fut, stream_fut).await;
421    }
422
423    #[fasync::run_singlethreaded(test)]
424    async fn test_get_link_local_ipv6_addresses() {
425        let ipv6_address = fnet::Subnet {
426            addr: fnet::IpAddress::Ipv6(fnet::Ipv6Address {
427                addr: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
428            }),
429            // NB: prefix length is ignored, use invalid value to prove it.
430            prefix_len: 137,
431        };
432        let link_local_ipv6_octets = [0xfe, 0x80, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
433        let link_local_ipv6_address = fnet::Subnet {
434            addr: fnet::IpAddress::Ipv6(fnet::Ipv6Address { addr: link_local_ipv6_octets }),
435            // NB: prefix length is ignored, use invalid value to prove it.
436            prefix_len: 139,
437        };
438        let ipv4_address = fnet::Subnet {
439            addr: fnet::IpAddress::Ipv4(fnet::Ipv4Address { addr: [0, 1, 2, 3] }),
440            // NB: prefix length is ignored, use invalid value to prove it.
441            prefix_len: 141,
442        };
443        let all_addresses =
444            [ipv6_address.clone(), link_local_ipv6_address.clone(), ipv4_address.clone()];
445        let (facade, stream_fut) = MockStateTester::new()
446            .expect_get_ipv6_addresses(all_addresses.to_vec())
447            .create_facade_and_serve_state();
448        let facade_fut = async move {
449            let result_address: Vec<_> = facade.get_link_local_ipv6_addresses().await.unwrap();
450            assert_eq!(result_address, [std::net::Ipv6Addr::from(link_local_ipv6_octets)]);
451        };
452        futures::future::join(facade_fut, stream_fut).await;
453    }
454}