blob: 27ad644c1bcebab5a6f14b3af966ef21124fcb42 [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! Rather than reuse existing _ext types, we define intermediary types for
//! JSON serialization to avoid coupling too closely to particular FIDL
//! protocols.
use fidl_fuchsia_net_routes_ext as froutes_ext;
use net_types::{ip::IpAddress as _, Witness as _};
use thiserror::Error;
#[derive(serde::Serialize, Ord, PartialOrd, Eq, PartialEq)]
pub(crate) struct Subnet<T> {
pub(crate) addr: T,
pub(crate) prefix_len: u8,
}
impl From<fidl_fuchsia_net_ext::Subnet> for Subnet<std::net::IpAddr> {
fn from(ext: fidl_fuchsia_net_ext::Subnet) -> Subnet<std::net::IpAddr> {
let fidl_fuchsia_net_ext::Subnet {
addr: fidl_fuchsia_net_ext::IpAddress(addr),
prefix_len,
} = ext;
Subnet { addr, prefix_len }
}
}
impl<A: net_types::ip::IpAddress> From<net_types::ip::Subnet<A>> for Subnet<std::net::IpAddr> {
fn from(sub: net_types::ip::Subnet<A>) -> Subnet<std::net::IpAddr> {
let addr = sub.network().to_ip_addr().into();
let prefix_len = sub.prefix();
Subnet { addr, prefix_len }
}
}
#[derive(serde::Serialize, Ord, PartialOrd, Eq, PartialEq)]
pub(crate) enum AddressAssignmentState {
Tentative,
Assigned,
Unavailable,
}
impl From<fidl_fuchsia_net_interfaces::AddressAssignmentState> for AddressAssignmentState {
fn from(value: fidl_fuchsia_net_interfaces::AddressAssignmentState) -> Self {
match value {
fidl_fuchsia_net_interfaces::AddressAssignmentState::Tentative => Self::Tentative,
fidl_fuchsia_net_interfaces::AddressAssignmentState::Assigned => Self::Assigned,
fidl_fuchsia_net_interfaces::AddressAssignmentState::Unavailable => Self::Unavailable,
}
}
}
#[derive(serde::Serialize, Ord, PartialOrd, Eq, PartialEq)]
pub(crate) struct Address<I> {
#[serde(flatten)]
pub(crate) subnet: Subnet<I>,
pub(crate) valid_until: Option<i64>,
pub(crate) assignment_state: AddressAssignmentState,
}
impl<I> Address<I> {
fn map<I2, F: Fn(I) -> I2>(self, f: F) -> Address<I2> {
let Self { subnet: Subnet { addr, prefix_len }, valid_until, assignment_state } = self;
Address { subnet: Subnet { addr: f(addr), prefix_len }, valid_until, assignment_state }
}
}
#[derive(serde::Serialize)]
pub(crate) struct Addresses {
pub(crate) ipv4: Vec<Address<std::net::Ipv4Addr>>,
pub(crate) ipv6: Vec<Address<std::net::Ipv6Addr>>,
}
impl Addresses {
pub(crate) fn all_addresses(self) -> impl Iterator<Item = Address<std::net::IpAddr>> {
let Self { ipv4, ipv6 } = self;
ipv4.into_iter()
.map(|a| a.map(Into::into))
.chain(ipv6.into_iter().map(|a| a.map(Into::into)))
}
}
impl<I: Iterator<Item = fidl_fuchsia_net_interfaces_ext::Address>> From<I> for Addresses {
fn from(addresses: I) -> Addresses {
use itertools::Itertools as _;
let (mut ipv4, mut ipv6): (Vec<_>, Vec<_>) = addresses.into_iter().partition_map(
|fidl_fuchsia_net_interfaces_ext::Address { addr, valid_until, assignment_state }| {
let fidl_fuchsia_net_ext::Subnet {
addr: fidl_fuchsia_net_ext::IpAddress(addr),
prefix_len,
} = addr.into();
let assignment_state = assignment_state.into();
fn new_address<I>(
addr: I,
prefix_len: u8,
valid_until: i64,
assignment_state: AddressAssignmentState,
) -> Address<I> {
let valid_until = (valid_until != i64::MAX).then_some(valid_until);
Address { subnet: Subnet { addr, prefix_len }, valid_until, assignment_state }
}
match addr {
std::net::IpAddr::V4(addr) => itertools::Either::Left(new_address(
addr,
prefix_len,
valid_until,
assignment_state,
)),
std::net::IpAddr::V6(addr) => itertools::Either::Right(new_address(
addr,
prefix_len,
valid_until,
assignment_state,
)),
}
},
);
ipv4.sort();
ipv6.sort();
Addresses { ipv4, ipv6 }
}
}
#[derive(serde::Serialize)]
pub(crate) enum DeviceClass {
Loopback,
Virtual,
Ethernet,
Wlan,
Ppp,
Bridge,
WlanAp,
}
impl From<fidl_fuchsia_net_interfaces::DeviceClass> for DeviceClass {
fn from(device_class: fidl_fuchsia_net_interfaces::DeviceClass) -> Self {
match device_class {
fidl_fuchsia_net_interfaces::DeviceClass::Loopback(
fidl_fuchsia_net_interfaces::Empty,
) => Self::Loopback,
fidl_fuchsia_net_interfaces::DeviceClass::Device(device_class) => match device_class {
fidl_fuchsia_hardware_network::DeviceClass::Virtual => Self::Virtual,
fidl_fuchsia_hardware_network::DeviceClass::Ethernet => Self::Ethernet,
fidl_fuchsia_hardware_network::DeviceClass::Wlan => Self::Wlan,
fidl_fuchsia_hardware_network::DeviceClass::Ppp => Self::Ppp,
fidl_fuchsia_hardware_network::DeviceClass::Bridge => Self::Bridge,
fidl_fuchsia_hardware_network::DeviceClass::WlanAp => Self::WlanAp,
},
}
}
}
#[derive(serde::Serialize)]
/// Intermediary struct for serializing interface properties into JSON.
pub(crate) struct InterfaceView {
pub(crate) nicid: u64,
pub(crate) name: String,
pub(crate) device_class: DeviceClass,
pub(crate) online: bool,
pub(crate) addresses: Addresses,
pub(crate) has_default_ipv4_route: bool,
pub(crate) has_default_ipv6_route: bool,
pub(crate) mac: Option<fidl_fuchsia_net_ext::MacAddress>,
}
impl From<(fidl_fuchsia_net_interfaces_ext::Properties, Option<fidl_fuchsia_net::MacAddress>)>
for InterfaceView
{
fn from(
t: (fidl_fuchsia_net_interfaces_ext::Properties, Option<fidl_fuchsia_net::MacAddress>),
) -> InterfaceView {
let (
fidl_fuchsia_net_interfaces_ext::Properties {
id,
name,
device_class,
online,
addresses,
has_default_ipv4_route,
has_default_ipv6_route,
},
mac,
) = t;
InterfaceView {
nicid: id.get(),
name,
device_class: device_class.into(),
online,
addresses: addresses.into_iter().into(),
has_default_ipv4_route,
has_default_ipv6_route,
mac: mac.map(Into::into),
}
}
}
#[derive(serde::Serialize, Ord, PartialOrd, Eq, PartialEq)]
/// Intermediary struct for serializing IP forwarding table entries into JSON.
pub struct ForwardingEntry {
#[serde(rename = "destination")]
subnet: Subnet<std::net::IpAddr>,
#[serde(rename = "nicid")]
device_id: u64,
#[serde(rename = "gateway")]
next_hop: Option<std::net::IpAddr>,
metric: u32,
}
/// Errors returned when converting from [`froutes_ext::InstalledRoute`]
/// to [`ForwardingEntry`].
#[derive(Debug, Error)]
pub enum ForwardingEntryConversionError {
#[error("the route's action was unknown")]
UnknownRouteAction,
}
impl<I: net_types::ip::Ip> TryFrom<froutes_ext::InstalledRoute<I>> for ForwardingEntry {
type Error = ForwardingEntryConversionError;
fn try_from(route: froutes_ext::InstalledRoute<I>) -> Result<Self, Self::Error> {
let froutes_ext::InstalledRoute {
route: froutes_ext::Route { destination, action, properties: _ },
effective_properties: froutes_ext::EffectiveRouteProperties { metric },
} = route;
let (device_id, next_hop) = match action {
froutes_ext::RouteAction::Forward(froutes_ext::RouteTarget {
outbound_interface,
next_hop,
}) => (outbound_interface, next_hop),
froutes_ext::RouteAction::Unknown => {
return Err(ForwardingEntryConversionError::UnknownRouteAction)
}
};
let subnet = destination.into();
let next_hop = next_hop.map(|next_hop| next_hop.get().to_ip_addr().into());
Ok(Self { subnet, device_id, next_hop, metric })
}
}
pub struct NeighborTableEntryIteratorItemVariants<T> {
existing: T,
added: T,
changed: T,
removed: T,
idle: T,
}
impl<T> NeighborTableEntryIteratorItemVariants<T> {
pub fn select(self, item: &fidl_fuchsia_net_neighbor::EntryIteratorItem) -> T {
use fidl_fuchsia_net_neighbor::EntryIteratorItem;
let Self { existing, added, changed, removed, idle } = self;
match item {
EntryIteratorItem::Existing(_) => existing,
EntryIteratorItem::Added(_) => added,
EntryIteratorItem::Changed(_) => changed,
EntryIteratorItem::Removed(_) => removed,
EntryIteratorItem::Idle(_) => idle,
}
}
}
impl<T> IntoIterator for NeighborTableEntryIteratorItemVariants<T> {
type Item = T;
type IntoIter = <[T; 5] as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
let Self { existing, added, changed, removed, idle } = self;
[existing, added, changed, removed, idle].into_iter()
}
}
pub const DISPLAYED_NEIGH_ENTRY_VARIANTS: NeighborTableEntryIteratorItemVariants<&'static str> =
NeighborTableEntryIteratorItemVariants {
existing: "EXISTING",
added: "ADDED",
changed: "CHANGED",
removed: "REMOVED",
idle: "IDLE",
};
/// Intermediary type for serializing Entry (e.g. into JSON).
#[derive(serde::Serialize)]
pub struct NeighborTableEntry {
interface: u64,
neighbor: std::net::IpAddr,
state: &'static str,
mac: Option<fidl_fuchsia_net_ext::MacAddress>,
}
impl From<fidl_fuchsia_net_neighbor_ext::Entry> for NeighborTableEntry {
fn from(
fidl_fuchsia_net_neighbor_ext::Entry {
interface,
neighbor,
state,
mac,
// Ignored since the tabular format ignores this field.
updated_at: _,
}: fidl_fuchsia_net_neighbor_ext::Entry,
) -> NeighborTableEntry {
let fidl_fuchsia_net_ext::IpAddress(neighbor) = neighbor.into();
NeighborTableEntry {
interface,
neighbor,
state: fidl_fuchsia_net_neighbor_ext::display_entry_state(&state),
mac: mac.map(|mac| mac.into()),
}
}
}