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::ip::IpAddress as _;
11use net_types::Witness 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}
183
184impl
185    From<(
186        fidl_fuchsia_net_interfaces_ext::Properties<fidl_fuchsia_net_interfaces_ext::AllInterest>,
187        Option<fidl_fuchsia_net::MacAddress>,
188    )> for InterfaceView
189{
190    fn from(
191        t: (
192            fidl_fuchsia_net_interfaces_ext::Properties<
193                fidl_fuchsia_net_interfaces_ext::AllInterest,
194            >,
195            Option<fidl_fuchsia_net::MacAddress>,
196        ),
197    ) -> InterfaceView {
198        let (
199            fidl_fuchsia_net_interfaces_ext::Properties {
200                id,
201                name,
202                port_class,
203                online,
204                addresses,
205                has_default_ipv4_route,
206                has_default_ipv6_route,
207            },
208            mac,
209        ) = t;
210        InterfaceView {
211            nicid: id.get(),
212            name,
213            device_class: port_class.into(),
214            online,
215            addresses: addresses.into_iter().into(),
216            has_default_ipv4_route,
217            has_default_ipv6_route,
218            mac: mac.map(Into::into),
219        }
220    }
221}
222
223#[derive(serde::Serialize, Ord, PartialOrd, Eq, PartialEq)]
224/// Intermediary struct for serializing IP forwarding table entries into JSON.
225pub struct ForwardingEntry {
226    #[serde(rename = "destination")]
227    subnet: Subnet<std::net::IpAddr>,
228    #[serde(rename = "nicid")]
229    device_id: u64,
230    #[serde(rename = "gateway")]
231    next_hop: Option<std::net::IpAddr>,
232    metric: u32,
233    table_id: u32,
234}
235
236/// Errors returned when converting from [`froutes_ext::InstalledRoute`]
237/// to [`ForwardingEntry`].
238#[derive(Debug, Error)]
239pub enum ForwardingEntryConversionError {
240    #[error("the route's action was unknown")]
241    UnknownRouteAction,
242}
243
244impl<I: net_types::ip::Ip> TryFrom<froutes_ext::InstalledRoute<I>> for ForwardingEntry {
245    type Error = ForwardingEntryConversionError;
246    fn try_from(route: froutes_ext::InstalledRoute<I>) -> Result<Self, Self::Error> {
247        let froutes_ext::InstalledRoute {
248            route: froutes_ext::Route { destination, action, properties: _ },
249            effective_properties: froutes_ext::EffectiveRouteProperties { metric },
250            table_id,
251        } = route;
252        let (device_id, next_hop) = match action {
253            froutes_ext::RouteAction::Forward(froutes_ext::RouteTarget {
254                outbound_interface,
255                next_hop,
256            }) => (outbound_interface, next_hop),
257            froutes_ext::RouteAction::Unknown => {
258                return Err(ForwardingEntryConversionError::UnknownRouteAction)
259            }
260        };
261        let subnet = destination.into();
262        let next_hop = next_hop.map(|next_hop| next_hop.get().to_ip_addr().into());
263        Ok(Self { subnet, device_id, next_hop, metric, table_id: table_id.get() })
264    }
265}
266
267pub struct NeighborTableEntryIteratorItemVariants<T> {
268    existing: T,
269    added: T,
270    changed: T,
271    removed: T,
272    idle: T,
273}
274
275impl<T> NeighborTableEntryIteratorItemVariants<T> {
276    pub fn select(self, item: &fidl_fuchsia_net_neighbor::EntryIteratorItem) -> T {
277        use fidl_fuchsia_net_neighbor::EntryIteratorItem;
278        let Self { existing, added, changed, removed, idle } = self;
279        match item {
280            EntryIteratorItem::Existing(_) => existing,
281            EntryIteratorItem::Added(_) => added,
282            EntryIteratorItem::Changed(_) => changed,
283            EntryIteratorItem::Removed(_) => removed,
284            EntryIteratorItem::Idle(_) => idle,
285        }
286    }
287}
288
289impl<T> IntoIterator for NeighborTableEntryIteratorItemVariants<T> {
290    type Item = T;
291    type IntoIter = <[T; 5] as IntoIterator>::IntoIter;
292
293    fn into_iter(self) -> Self::IntoIter {
294        let Self { existing, added, changed, removed, idle } = self;
295        [existing, added, changed, removed, idle].into_iter()
296    }
297}
298
299pub const DISPLAYED_NEIGH_ENTRY_VARIANTS: NeighborTableEntryIteratorItemVariants<&'static str> =
300    NeighborTableEntryIteratorItemVariants {
301        existing: "EXISTING",
302        added: "ADDED",
303        changed: "CHANGED",
304        removed: "REMOVED",
305        idle: "IDLE",
306    };
307
308/// Intermediary type for serializing Entry (e.g. into JSON).
309#[derive(serde::Serialize)]
310pub struct NeighborTableEntry {
311    interface: u64,
312    neighbor: std::net::IpAddr,
313    state: &'static str,
314    mac: Option<fidl_fuchsia_net_ext::MacAddress>,
315}
316
317impl From<fidl_fuchsia_net_neighbor_ext::Entry> for NeighborTableEntry {
318    fn from(
319        fidl_fuchsia_net_neighbor_ext::Entry {
320            interface,
321            neighbor,
322            state,
323            mac,
324            // Ignored since the tabular format ignores this field.
325            updated_at: _,
326        }: fidl_fuchsia_net_neighbor_ext::Entry,
327    ) -> NeighborTableEntry {
328        let fidl_fuchsia_net_ext::IpAddress(neighbor) = neighbor.into();
329        NeighborTableEntry {
330            interface,
331            neighbor,
332            state: fidl_fuchsia_net_neighbor_ext::display_entry_state(&state),
333            mac: mac.map(|mac| mac.into()),
334        }
335    }
336}