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}
234
235/// Errors returned when converting from [`froutes_ext::InstalledRoute`]
236/// to [`ForwardingEntry`].
237#[derive(Debug, Error)]
238pub enum ForwardingEntryConversionError {
239    #[error("the route's action was unknown")]
240    UnknownRouteAction,
241}
242
243impl<I: net_types::ip::Ip> TryFrom<froutes_ext::InstalledRoute<I>> for ForwardingEntry {
244    type Error = ForwardingEntryConversionError;
245    fn try_from(route: froutes_ext::InstalledRoute<I>) -> Result<Self, Self::Error> {
246        let froutes_ext::InstalledRoute {
247            route: froutes_ext::Route { destination, action, properties: _ },
248            effective_properties: froutes_ext::EffectiveRouteProperties { metric },
249            table_id: _,
250        } = route;
251        let (device_id, next_hop) = match action {
252            froutes_ext::RouteAction::Forward(froutes_ext::RouteTarget {
253                outbound_interface,
254                next_hop,
255            }) => (outbound_interface, next_hop),
256            froutes_ext::RouteAction::Unknown => {
257                return Err(ForwardingEntryConversionError::UnknownRouteAction)
258            }
259        };
260        let subnet = destination.into();
261        let next_hop = next_hop.map(|next_hop| next_hop.get().to_ip_addr().into());
262        Ok(Self { subnet, device_id, next_hop, metric })
263    }
264}
265
266pub struct NeighborTableEntryIteratorItemVariants<T> {
267    existing: T,
268    added: T,
269    changed: T,
270    removed: T,
271    idle: T,
272}
273
274impl<T> NeighborTableEntryIteratorItemVariants<T> {
275    pub fn select(self, item: &fidl_fuchsia_net_neighbor::EntryIteratorItem) -> T {
276        use fidl_fuchsia_net_neighbor::EntryIteratorItem;
277        let Self { existing, added, changed, removed, idle } = self;
278        match item {
279            EntryIteratorItem::Existing(_) => existing,
280            EntryIteratorItem::Added(_) => added,
281            EntryIteratorItem::Changed(_) => changed,
282            EntryIteratorItem::Removed(_) => removed,
283            EntryIteratorItem::Idle(_) => idle,
284        }
285    }
286}
287
288impl<T> IntoIterator for NeighborTableEntryIteratorItemVariants<T> {
289    type Item = T;
290    type IntoIter = <[T; 5] as IntoIterator>::IntoIter;
291
292    fn into_iter(self) -> Self::IntoIter {
293        let Self { existing, added, changed, removed, idle } = self;
294        [existing, added, changed, removed, idle].into_iter()
295    }
296}
297
298pub const DISPLAYED_NEIGH_ENTRY_VARIANTS: NeighborTableEntryIteratorItemVariants<&'static str> =
299    NeighborTableEntryIteratorItemVariants {
300        existing: "EXISTING",
301        added: "ADDED",
302        changed: "CHANGED",
303        removed: "REMOVED",
304        idle: "IDLE",
305    };
306
307/// Intermediary type for serializing Entry (e.g. into JSON).
308#[derive(serde::Serialize)]
309pub struct NeighborTableEntry {
310    interface: u64,
311    neighbor: std::net::IpAddr,
312    state: &'static str,
313    mac: Option<fidl_fuchsia_net_ext::MacAddress>,
314}
315
316impl From<fidl_fuchsia_net_neighbor_ext::Entry> for NeighborTableEntry {
317    fn from(
318        fidl_fuchsia_net_neighbor_ext::Entry {
319            interface,
320            neighbor,
321            state,
322            mac,
323            // Ignored since the tabular format ignores this field.
324            updated_at: _,
325        }: fidl_fuchsia_net_neighbor_ext::Entry,
326    ) -> NeighborTableEntry {
327        let fidl_fuchsia_net_ext::IpAddress(neighbor) = neighbor.into();
328        NeighborTableEntry {
329            interface,
330            neighbor,
331            state: fidl_fuchsia_net_neighbor_ext::display_entry_state(&state),
332            mac: mac.map(|mac| mac.into()),
333        }
334    }
335}