blob: e16541a5f58e8e8aa748ba507bb63d22e4217c0e [file] [log] [blame]
// Copyright 2023 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.
//! Inspect utilities.
//!
//! This module provides utilities for publishing netstack3 diagnostics data to
//! Inspect.
use std::string::ToString as _;
use fuchsia_inspect::Node;
use net_types::{
ethernet::Mac,
ip::{Ipv4, Ipv6},
UnicastAddr, Witness as _,
};
use netstack3_core::device::{DeviceId, EthernetLinkDevice, WeakDeviceId};
use netstack3_fuchsia::{FuchsiaInspector, InspectorDeviceIdProvider};
use crate::bindings::{
devices::{
DeviceIdAndName, DeviceSpecificInfo, DynamicCommonInfo, DynamicNetdeviceInfo, EthernetInfo,
},
BindingsCtx, Ctx, DeviceIdExt as _,
};
impl InspectorDeviceIdProvider<DeviceId<BindingsCtx>> for BindingsCtx {
fn device_id(id: &DeviceId<BindingsCtx>) -> u64 {
id.bindings_id().id.into()
}
}
impl InspectorDeviceIdProvider<WeakDeviceId<BindingsCtx>> for BindingsCtx {
fn device_id(id: &WeakDeviceId<BindingsCtx>) -> u64 {
id.bindings_id().id.into()
}
}
/// Publishes netstack3 socket diagnostics data to Inspect.
pub(crate) fn sockets(ctx: &mut Ctx) -> fuchsia_inspect::Inspector {
let inspector = fuchsia_inspect::Inspector::new(Default::default());
let mut bindings_inspector = FuchsiaInspector::<BindingsCtx>::new(inspector.root());
ctx.api().tcp::<Ipv4>().inspect(&mut bindings_inspector);
ctx.api().tcp::<Ipv6>().inspect(&mut bindings_inspector);
ctx.api().udp::<Ipv4>().inspect(&mut bindings_inspector);
ctx.api().udp::<Ipv6>().inspect(&mut bindings_inspector);
ctx.api().icmp_echo::<Ipv4>().inspect(&mut bindings_inspector);
ctx.api().icmp_echo::<Ipv6>().inspect(&mut bindings_inspector);
inspector
}
/// Publishes netstack3 routing table diagnostics data to Inspect.
pub(crate) fn routes(ctx: &mut Ctx) -> fuchsia_inspect::Inspector {
let inspector = fuchsia_inspect::Inspector::new(Default::default());
let mut bindings_inspector = FuchsiaInspector::<BindingsCtx>::new(inspector.root());
ctx.api().routes::<Ipv4>().inspect(&mut bindings_inspector);
ctx.api().routes::<Ipv6>().inspect(&mut bindings_inspector);
inspector
}
pub(crate) fn devices(ctx: &mut Ctx) -> fuchsia_inspect::Inspector {
// Snapshot devices out so we're not holding onto the devices lock for too
// long.
let devices =
ctx.bindings_ctx().devices.with_devices(|devices| devices.cloned().collect::<Vec<_>>());
let inspector = fuchsia_inspect::Inspector::new(Default::default());
let node = inspector.root();
for device_id in devices {
let external_state = device_id.external_state();
let DeviceIdAndName { id: binding_id, name } = device_id.bindings_id();
node.record_child(format!("{binding_id}"), |node| {
node.record_string("Name", &name);
node.record_uint("InterfaceId", (*binding_id).into());
node.record_child("IPv4", |node| {
ctx.api()
.device_ip::<Ipv4>()
.inspect(&device_id, &mut FuchsiaInspector::<BindingsCtx>::new(node))
});
node.record_child("IPv6", |node| {
ctx.api()
.device_ip::<Ipv6>()
.inspect(&device_id, &mut FuchsiaInspector::<BindingsCtx>::new(node))
});
external_state.with_common_info(
|DynamicCommonInfo {
admin_enabled,
mtu,
addresses: _,
control_hook: _,
events: _,
}| {
node.record_bool("AdminEnabled", *admin_enabled);
node.record_uint("MTU", mtu.get().into());
},
);
match external_state {
DeviceSpecificInfo::Ethernet(info @ EthernetInfo { mac, .. }) => {
node.record_bool("Loopback", false);
info.with_dynamic_info(|dynamic| {
record_network_device(node, &dynamic.netdevice, Some(mac))
});
}
DeviceSpecificInfo::Loopback(_info) => {
node.record_bool("Loopback", true);
}
DeviceSpecificInfo::PureIp(info) => {
node.record_bool("Loopback", false);
info.with_dynamic_info(|dynamic| record_network_device(node, dynamic, None));
}
}
ctx.api()
.device_any()
.inspect(&device_id, &mut FuchsiaInspector::<BindingsCtx>::new(node));
})
}
inspector
}
/// Record information about the Netdevice as a child of the given inspect node.
fn record_network_device(
node: &Node,
DynamicNetdeviceInfo { phy_up, common_info: _ }: &DynamicNetdeviceInfo,
mac: Option<&UnicastAddr<Mac>>,
) {
node.record_child("NetworkDevice", |node| {
if let Some(mac) = mac {
node.record_string("MacAddress", mac.get().to_string());
}
node.record_bool("PhyUp", *phy_up);
})
}
pub(crate) fn neighbors(mut ctx: Ctx) -> fuchsia_inspect::Inspector {
let inspector = fuchsia_inspect::Inspector::new(Default::default());
// Get a snapshot of all supported devices. Ethernet is the only device type
// that supports neighbors.
let ethernet_devices = ctx.bindings_ctx().devices.with_devices(|devices| {
devices
.filter_map(|d| match d {
DeviceId::Ethernet(d) => Some(d.clone()),
// NUD is not supported on Loopback or pure IP devices.
DeviceId::Loopback(_) | DeviceId::PureIp(_) => None,
})
.collect::<Vec<_>>()
});
for device in ethernet_devices {
inspector.root().record_child(&device.bindings_id().name, |node| {
let mut inspector = FuchsiaInspector::<BindingsCtx>::new(node);
ctx.api()
.neighbor::<Ipv4, EthernetLinkDevice>()
.inspect_neighbors(&device, &mut inspector);
ctx.api()
.neighbor::<Ipv6, EthernetLinkDevice>()
.inspect_neighbors(&device, &mut inspector);
});
}
inspector
}
pub(crate) fn counters(ctx: &mut Ctx) -> fuchsia_inspect::Inspector {
let inspector = fuchsia_inspect::Inspector::new(Default::default());
ctx.api()
.counters()
.inspect_stack_counters(&mut FuchsiaInspector::<BindingsCtx>::new(inspector.root()));
inspector
}
pub(crate) fn filtering_state(ctx: &mut Ctx) -> fuchsia_inspect::Inspector {
let inspector = fuchsia_inspect::Inspector::new(Default::default());
ctx.api().filter().inspect_state(&mut FuchsiaInspector::<BindingsCtx>::new(inspector.root()));
inspector
}