net_cli/
ser.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
5//! Rather than reuse existing _ext types, we define intermediary types for
6//! JSON serialization to avoid coupling too closely to particular FIDL
7//! protocols.
8
9use fidl_fuchsia_net_routes_ext as froutes_ext;
10use net_types::Witness as _;
11use net_types::ip::IpAddress as _;
12use thiserror::Error;
13
14#[derive(serde::Serialize, Ord, PartialOrd, Eq, PartialEq)]
15pub(crate) struct Subnet<T> {
16    pub(crate) addr: T,
17    pub(crate) prefix_len: u8,
18}
19
20impl From<fidl_fuchsia_net_ext::Subnet> for Subnet<std::net::IpAddr> {
21    fn from(ext: fidl_fuchsia_net_ext::Subnet) -> Subnet<std::net::IpAddr> {
22        let fidl_fuchsia_net_ext::Subnet {
23            addr: fidl_fuchsia_net_ext::IpAddress(addr),
24            prefix_len,
25        } = ext;
26        Subnet { addr, prefix_len }
27    }
28}
29
30impl<A: net_types::ip::IpAddress> From<net_types::ip::Subnet<A>> for Subnet<std::net::IpAddr> {
31    fn from(sub: net_types::ip::Subnet<A>) -> Subnet<std::net::IpAddr> {
32        let addr = sub.network().to_ip_addr().into();
33        let prefix_len = sub.prefix();
34        Subnet { addr, prefix_len }
35    }
36}
37
38#[derive(serde::Serialize, Ord, PartialOrd, Eq, PartialEq)]
39pub(crate) enum AddressAssignmentState {
40    Tentative,
41    Assigned,
42    Unavailable,
43}
44
45impl From<fidl_fuchsia_net_interfaces::AddressAssignmentState> for AddressAssignmentState {
46    fn from(value: fidl_fuchsia_net_interfaces::AddressAssignmentState) -> Self {
47        match value {
48            fidl_fuchsia_net_interfaces::AddressAssignmentState::Tentative => Self::Tentative,
49            fidl_fuchsia_net_interfaces::AddressAssignmentState::Assigned => Self::Assigned,
50            fidl_fuchsia_net_interfaces::AddressAssignmentState::Unavailable => Self::Unavailable,
51        }
52    }
53}
54
55#[derive(serde::Serialize, Ord, PartialOrd, Eq, PartialEq)]
56pub(crate) struct Address<I> {
57    #[serde(flatten)]
58    pub(crate) subnet: Subnet<I>,
59    pub(crate) valid_until: Option<i64>,
60    pub(crate) assignment_state: AddressAssignmentState,
61}
62
63impl<I> Address<I> {
64    fn map<I2, F: Fn(I) -> I2>(self, f: F) -> Address<I2> {
65        let Self { subnet: Subnet { addr, prefix_len }, valid_until, assignment_state } = self;
66        Address { subnet: Subnet { addr: f(addr), prefix_len }, valid_until, assignment_state }
67    }
68}
69
70#[derive(serde::Serialize)]
71pub(crate) struct Addresses {
72    pub(crate) ipv4: Vec<Address<std::net::Ipv4Addr>>,
73    pub(crate) ipv6: Vec<Address<std::net::Ipv6Addr>>,
74}
75
76impl Addresses {
77    pub(crate) fn all_addresses(self) -> impl Iterator<Item = Address<std::net::IpAddr>> {
78        let Self { ipv4, ipv6 } = self;
79        ipv4.into_iter()
80            .map(|a| a.map(Into::into))
81            .chain(ipv6.into_iter().map(|a| a.map(Into::into)))
82    }
83}
84
85impl<
86    I: Iterator<
87        Item = fidl_fuchsia_net_interfaces_ext::Address<
88            fidl_fuchsia_net_interfaces_ext::AllInterest,
89        >,
90    >,
91> From<I> for Addresses
92{
93    fn from(addresses: I) -> Addresses {
94        use itertools::Itertools as _;
95
96        let (mut ipv4, mut ipv6): (Vec<_>, Vec<_>) = addresses.into_iter().partition_map(
97            |fidl_fuchsia_net_interfaces_ext::Address {
98                 addr,
99                 valid_until,
100                 assignment_state,
101                 // TODO(https://fxbug.dev/42051655): Expose address lifetimes.
102                 preferred_lifetime_info: _,
103             }| {
104                let fidl_fuchsia_net_ext::Subnet {
105                    addr: fidl_fuchsia_net_ext::IpAddress(addr),
106                    prefix_len,
107                } = addr.into();
108                let assignment_state = assignment_state.into();
109
110                fn new_address<I>(
111                    addr: I,
112                    prefix_len: u8,
113                    valid_until: fidl_fuchsia_net_interfaces_ext::PositiveMonotonicInstant,
114                    assignment_state: AddressAssignmentState,
115                ) -> Address<I> {
116                    let valid_until =
117                        (!valid_until.is_infinite()).then_some(valid_until.into_nanos());
118                    Address { subnet: Subnet { addr, prefix_len }, valid_until, assignment_state }
119                }
120                match addr {
121                    std::net::IpAddr::V4(addr) => itertools::Either::Left(new_address(
122                        addr,
123                        prefix_len,
124                        valid_until,
125                        assignment_state,
126                    )),
127                    std::net::IpAddr::V6(addr) => itertools::Either::Right(new_address(
128                        addr,
129                        prefix_len,
130                        valid_until,
131                        assignment_state,
132                    )),
133                }
134            },
135        );
136        ipv4.sort();
137        ipv6.sort();
138        Addresses { ipv4, ipv6 }
139    }
140}
141
142#[derive(serde::Serialize)]
143pub(crate) enum DeviceClass {
144    Loopback,
145    Blackhole,
146    Virtual,
147    Ethernet,
148    WlanClient,
149    Ppp,
150    Bridge,
151    WlanAp,
152    Lowpan,
153}
154
155impl From<fidl_fuchsia_net_interfaces_ext::PortClass> for DeviceClass {
156    fn from(port_class: fidl_fuchsia_net_interfaces_ext::PortClass) -> Self {
157        match port_class {
158            fidl_fuchsia_net_interfaces_ext::PortClass::Loopback => Self::Loopback,
159            fidl_fuchsia_net_interfaces_ext::PortClass::Blackhole => Self::Blackhole,
160            fidl_fuchsia_net_interfaces_ext::PortClass::Virtual => Self::Virtual,
161            fidl_fuchsia_net_interfaces_ext::PortClass::Ethernet => Self::Ethernet,
162            fidl_fuchsia_net_interfaces_ext::PortClass::WlanClient => Self::WlanClient,
163            fidl_fuchsia_net_interfaces_ext::PortClass::WlanAp => Self::WlanAp,
164            fidl_fuchsia_net_interfaces_ext::PortClass::Ppp => Self::Ppp,
165            fidl_fuchsia_net_interfaces_ext::PortClass::Bridge => Self::Bridge,
166            fidl_fuchsia_net_interfaces_ext::PortClass::Lowpan => Self::Lowpan,
167        }
168    }
169}
170
171#[derive(serde::Serialize)]
172/// Intermediary struct for serializing interface properties into JSON.
173pub(crate) struct InterfaceView {
174    pub(crate) nicid: u64,
175    pub(crate) name: String,
176    pub(crate) device_class: DeviceClass,
177    pub(crate) online: bool,
178    pub(crate) addresses: Addresses,
179    pub(crate) has_default_ipv4_route: bool,
180    pub(crate) has_default_ipv6_route: bool,
181    pub(crate) mac: Option<fidl_fuchsia_net_ext::MacAddress>,
182    pub(crate) port_identity_koid: Option<zx_types::zx_koid_t>,
183}
184
185impl
186    From<(
187        fidl_fuchsia_net_interfaces_ext::Properties<fidl_fuchsia_net_interfaces_ext::AllInterest>,
188        Option<fidl_fuchsia_net::MacAddress>,
189    )> for InterfaceView
190{
191    fn from(
192        t: (
193            fidl_fuchsia_net_interfaces_ext::Properties<
194                fidl_fuchsia_net_interfaces_ext::AllInterest,
195            >,
196            Option<fidl_fuchsia_net::MacAddress>,
197        ),
198    ) -> InterfaceView {
199        let (
200            fidl_fuchsia_net_interfaces_ext::Properties {
201                id,
202                name,
203                port_class,
204                online,
205                addresses,
206                has_default_ipv4_route,
207                has_default_ipv6_route,
208                port_identity_koid,
209            },
210            mac,
211        ) = t;
212        InterfaceView {
213            nicid: id.get(),
214            name,
215            device_class: port_class.into(),
216            online,
217            addresses: addresses.into_iter().into(),
218            has_default_ipv4_route,
219            has_default_ipv6_route,
220            mac: mac.map(Into::into),
221            port_identity_koid: port_identity_koid.map(|e| e.raw_koid()),
222        }
223    }
224}
225
226#[derive(serde::Serialize, Ord, PartialOrd, Eq, PartialEq)]
227/// Intermediary struct for serializing IP forwarding table entries into JSON.
228pub struct ForwardingEntry {
229    #[serde(rename = "destination")]
230    subnet: Subnet<std::net::IpAddr>,
231    #[serde(rename = "nicid")]
232    device_id: u64,
233    #[serde(rename = "gateway")]
234    next_hop: Option<std::net::IpAddr>,
235    metric: u32,
236    table_id: u32,
237}
238
239/// Errors returned when converting from [`froutes_ext::InstalledRoute`]
240/// to [`ForwardingEntry`].
241#[derive(Debug, Error)]
242pub enum ForwardingEntryConversionError {
243    #[error("the route's action was unknown")]
244    UnknownRouteAction,
245}
246
247impl<I: net_types::ip::Ip> TryFrom<froutes_ext::InstalledRoute<I>> for ForwardingEntry {
248    type Error = ForwardingEntryConversionError;
249    fn try_from(route: froutes_ext::InstalledRoute<I>) -> Result<Self, Self::Error> {
250        let froutes_ext::InstalledRoute {
251            route: froutes_ext::Route { destination, action, properties: _ },
252            effective_properties: froutes_ext::EffectiveRouteProperties { metric },
253            table_id,
254        } = route;
255        let (device_id, next_hop) = match action {
256            froutes_ext::RouteAction::Forward(froutes_ext::RouteTarget {
257                outbound_interface,
258                next_hop,
259            }) => (outbound_interface, next_hop),
260            froutes_ext::RouteAction::Unknown => {
261                return Err(ForwardingEntryConversionError::UnknownRouteAction);
262            }
263        };
264        let subnet = destination.into();
265        let next_hop = next_hop.map(|next_hop| next_hop.get().to_ip_addr().into());
266        Ok(Self { subnet, device_id, next_hop, metric, table_id: table_id.get() })
267    }
268}
269
270pub struct NeighborTableEntryIteratorItemVariants<T> {
271    existing: T,
272    added: T,
273    changed: T,
274    removed: T,
275    idle: T,
276}
277
278impl<T> NeighborTableEntryIteratorItemVariants<T> {
279    pub fn select(self, item: &fidl_fuchsia_net_neighbor::EntryIteratorItem) -> T {
280        use fidl_fuchsia_net_neighbor::EntryIteratorItem;
281        let Self { existing, added, changed, removed, idle } = self;
282        match item {
283            EntryIteratorItem::Existing(_) => existing,
284            EntryIteratorItem::Added(_) => added,
285            EntryIteratorItem::Changed(_) => changed,
286            EntryIteratorItem::Removed(_) => removed,
287            EntryIteratorItem::Idle(_) => idle,
288        }
289    }
290}
291
292impl<T> IntoIterator for NeighborTableEntryIteratorItemVariants<T> {
293    type Item = T;
294    type IntoIter = <[T; 5] as IntoIterator>::IntoIter;
295
296    fn into_iter(self) -> Self::IntoIter {
297        let Self { existing, added, changed, removed, idle } = self;
298        [existing, added, changed, removed, idle].into_iter()
299    }
300}
301
302pub const DISPLAYED_NEIGH_ENTRY_VARIANTS: NeighborTableEntryIteratorItemVariants<&'static str> =
303    NeighborTableEntryIteratorItemVariants {
304        existing: "EXISTING",
305        added: "ADDED",
306        changed: "CHANGED",
307        removed: "REMOVED",
308        idle: "IDLE",
309    };
310
311/// Intermediary type for serializing Entry (e.g. into JSON).
312#[derive(serde::Serialize)]
313pub struct NeighborTableEntry {
314    interface: u64,
315    neighbor: std::net::IpAddr,
316    state: &'static str,
317    mac: Option<fidl_fuchsia_net_ext::MacAddress>,
318}
319
320impl From<fidl_fuchsia_net_neighbor_ext::Entry> for NeighborTableEntry {
321    fn from(
322        fidl_fuchsia_net_neighbor_ext::Entry {
323            interface,
324            neighbor,
325            state,
326            mac,
327            // Ignored since the tabular format ignores this field.
328            updated_at: _,
329        }: fidl_fuchsia_net_neighbor_ext::Entry,
330    ) -> NeighborTableEntry {
331        let fidl_fuchsia_net_ext::IpAddress(neighbor) = neighbor.into();
332        NeighborTableEntry {
333            interface,
334            neighbor,
335            state: fidl_fuchsia_net_neighbor_ext::display_entry_state(&state),
336            mac: mac.map(|mac| mac.into()),
337        }
338    }
339}