netstack3_base/
inspect.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
// Copyright 2024 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.

//! Abstractions to expose netstack3 core state to human inspection in bindings.
//!
//! This module mostly exists to abstract the dependency to Fuchsia inspect via
//! a trait, so we don't have to expose all of the internal core types to
//! bindings for it to perform the inspection.

use alloc::format;
use alloc::string::String;
use core::fmt::{Debug, Display};

use net_types::ip::IpAddress;
use net_types::{AddrAndPortFormatter, ZonedAddr};

use crate::counters::Counter;

/// A trait abstracting a state inspector.
///
/// This trait follows roughly the same shape as the API provided by the
/// fuchsia_inspect crate, but we abstract it out so not to take the dependency.
///
/// Given we have the trait, we can fill it in with some helpful default
/// implementations for common types that are exposed as well, like IP addresses
/// and such.
pub trait Inspector: Sized {
    /// The type given to record contained children.
    type ChildInspector<'a>: Inspector;

    /// Records a nested inspector with `name` calling `f` with the nested child
    /// to be filled in.
    ///
    /// This is used to group and contextualize data.
    fn record_child<F: FnOnce(&mut Self::ChildInspector<'_>)>(&mut self, name: &str, f: F);

    /// Records a child without a name.
    ///
    /// The `Inpector` is expected to keep track of the number of unnamed
    /// children and allocate names appropriately from that.
    fn record_unnamed_child<F: FnOnce(&mut Self::ChildInspector<'_>)>(&mut self, f: F);

    /// Records a child whose name is the display implementation of `T`.
    fn record_display_child<T: Display, F: FnOnce(&mut Self::ChildInspector<'_>)>(
        &mut self,
        name: T,
        f: F,
    ) {
        self.record_child(&format!("{name}"), f)
    }

    /// Records a child whose name is the Debug implementation of `T`.
    fn record_debug_child<T: Debug, F: FnOnce(&mut Self::ChildInspector<'_>)>(
        &mut self,
        name: T,
        f: F,
    ) {
        self.record_child(&format!("{name:?}"), f)
    }

    /// Records anything that can be represented by a usize.
    fn record_usize<T: Into<usize>>(&mut self, name: &str, value: T);

    /// Records anything that can be represented by a u64.
    fn record_uint<T: Into<u64>>(&mut self, name: &str, value: T);

    /// Records anything that can be represented by a i64.
    fn record_int<T: Into<i64>>(&mut self, name: &str, value: T);

    /// Records anything that can be represented by a f64.
    fn record_double<T: Into<f64>>(&mut self, name: &str, value: T);

    /// Records a str value.
    fn record_str(&mut self, name: &str, value: &str);

    /// Records an owned string.
    fn record_string(&mut self, name: &str, value: String);

    /// Records a boolean.
    fn record_bool(&mut self, name: &str, value: bool);

    /// Records a counter.
    fn record_counter(&mut self, name: &str, value: &Counter) {
        self.record_uint(name, value.get())
    }

    /// Records a `value` that implements `Display` as its display string.
    fn record_display<T: Display>(&mut self, name: &str, value: T) {
        self.record_string(name, format!("{value}"))
    }

    /// Records a `value` that implements `Debug` as its debug string.
    fn record_debug<T: Debug>(&mut self, name: &str, value: T) {
        self.record_string(name, format!("{value:?}"))
    }

    /// Records an IP address.
    fn record_ip_addr<A: IpAddress>(&mut self, name: &str, value: A) {
        self.record_display(name, value)
    }

    /// Records a `ZonedAddr` and it's port, mapping the zone into an
    /// inspectable device identifier.
    fn record_zoned_addr_with_port<I: InspectorDeviceExt<D>, A: IpAddress, D, P: Display>(
        &mut self,
        name: &str,
        addr: ZonedAddr<A, D>,
        port: P,
    ) {
        self.record_display(
            name,
            AddrAndPortFormatter::<_, _, A::Version>::new(
                addr.map_zone(|device| I::device_identifier_as_address_zone(device)),
                port,
            ),
        )
    }

    /// Records the local address of a socket.
    fn record_local_socket_addr<I: InspectorDeviceExt<D>, A: IpAddress, D, P: Display>(
        &mut self,
        addr_with_port: Option<(ZonedAddr<A, D>, P)>,
    ) {
        const NAME: &str = "LocalAddress";
        if let Some((addr, port)) = addr_with_port {
            self.record_zoned_addr_with_port::<I, _, _, _>(NAME, addr, port);
        } else {
            self.record_str(NAME, "[NOT BOUND]")
        }
    }

    /// Records the remote address of a socket.
    fn record_remote_socket_addr<I: InspectorDeviceExt<D>, A: IpAddress, D, P: Display>(
        &mut self,
        addr_with_port: Option<(ZonedAddr<A, D>, P)>,
    ) {
        const NAME: &str = "RemoteAddress";
        if let Some((addr, port)) = addr_with_port {
            self.record_zoned_addr_with_port::<I, _, _, _>(NAME, addr, port);
        } else {
            self.record_str(NAME, "[NOT CONNECTED]")
        }
    }

    /// Records an implementor of [`InspectableValue`].
    fn record_inspectable_value<V: InspectableValue>(&mut self, name: &str, value: &V) {
        value.record(name, self)
    }

    /// Records an implementor of [`Inspectable`] under `name`.
    fn record_inspectable<V: Inspectable>(&mut self, name: &str, value: &V) {
        self.record_child(name, |inspector| {
            inspector.delegate_inspectable(value);
        });
    }

    /// Delegates more fields to be added by an [`Inspectable`] implementation.
    fn delegate_inspectable<V: Inspectable>(&mut self, value: &V) {
        value.record(self)
    }
}

/// A trait that allows a type to record its fields to an `inspector`.
///
/// This trait is used for types that are exposed to [`Inspector`]s many times
/// so recording them can be deduplicated.
pub trait Inspectable {
    /// Records this value into `inspector`.
    fn record<I: Inspector>(&self, inspector: &mut I);
}

impl Inspectable for () {
    fn record<I: Inspector>(&self, _inspector: &mut I) {}
}

/// A trait that marks a type as inspectable.
///
/// This trait is used for types that are exposed to [`Inspector`]s many times
/// so recording them can be deduplicated.
///
/// This type differs from [`Inspectable`] in that it receives a `name`
/// parameter. This is typically used for types that record a single entry.
pub trait InspectableValue {
    /// Records this value into `inspector`.
    fn record<I: Inspector>(&self, name: &str, inspector: &mut I);
}

/// An extension to `Inspector` that allows transforming and recording device
/// identifiers.
///
/// How to record device IDs is delegated to bindings via this trait, so we
/// don't need to propagate `InspectableValue` implementations everywhere in
/// core unnecessarily.
pub trait InspectorDeviceExt<D> {
    /// Records an entry named `name` with value `device`.
    fn record_device<I: Inspector>(inspector: &mut I, name: &str, device: &D);

    /// Returns the `Display` representation of the IPv6 scoped address zone
    /// associated with `D`.
    fn device_identifier_as_address_zone(device: D) -> impl Display;
}