1use net_types::ip::{Ip, IpMarked};
8use netstack3_base::{
9 Counter, CounterContext, Inspectable, Inspector, InspectorExt as _, ResourceCounterContext,
10 WeakDeviceIdentifier,
11};
12use netstack3_datagram::IpExt;
13
14use crate::internal::base::{UdpBindingsTypes, UdpSocketId};
15
16pub trait UdpCounterContext<I: IpExt, D: WeakDeviceIdentifier, BT: UdpBindingsTypes>:
18 ResourceCounterContext<UdpSocketId<I, D, BT>, UdpCountersWithSocket<I>>
19 + CounterContext<UdpCountersWithoutSocket<I>>
20{
21}
22
23impl<I, D, BT, CC> UdpCounterContext<I, D, BT> for CC
24where
25 I: IpExt,
26 D: WeakDeviceIdentifier,
27 BT: UdpBindingsTypes,
28 CC: ResourceCounterContext<UdpSocketId<I, D, BT>, UdpCountersWithSocket<I>>
29 + CounterContext<UdpCountersWithoutSocket<I>>,
30{
31}
32
33pub type UdpCountersWithoutSocket<I> = IpMarked<I, UdpCountersWithoutSocketInner>;
39
40#[derive(Default, Debug)]
44#[cfg_attr(
45 any(test, feature = "testutils"),
46 derive(PartialEq, netstack3_macros::CounterCollection)
47)]
48pub struct UdpCountersWithoutSocketInner<C = Counter> {
49 pub rx_icmp_error: C,
51 pub rx: C,
54 pub rx_mapped_addr: C,
57 pub rx_unknown_dest_port: C,
60 pub rx_malformed: C,
63}
64
65pub type UdpCountersWithSocket<I> = IpMarked<I, UdpCountersWithSocketInner>;
75
76#[derive(Default, Debug)]
80#[cfg_attr(
81 any(test, feature = "testutils"),
82 derive(PartialEq, netstack3_macros::CounterCollection)
83)]
84pub struct UdpCountersWithSocketInner<C = Counter> {
85 pub rx_delivered: C,
90 pub rx_queue_full: C,
93 pub tx: C,
96 pub tx_error: C,
99}
100
101pub struct CombinedUdpCounters<'a, I: Ip> {
103 pub with_socket: &'a UdpCountersWithSocket<I>,
105 pub without_socket: Option<&'a UdpCountersWithoutSocket<I>>,
111}
112
113impl<I: Ip> Inspectable for CombinedUdpCounters<'_, I> {
114 fn record<II: Inspector>(&self, inspector: &mut II) {
115 let CombinedUdpCounters { with_socket, without_socket } = self;
116 let UdpCountersWithSocketInner { rx_delivered, rx_queue_full, tx, tx_error } =
117 with_socket.as_ref();
118
119 struct WithoutSocketRx<'a> {
122 rx: &'a Counter,
123 }
124 struct WithoutSocketRxError<'a> {
125 rx_mapped_addr: &'a Counter,
126 rx_unknown_dest_port: &'a Counter,
127 rx_malformed: &'a Counter,
128 }
129 struct WithoutSocketError<'a> {
130 rx_icmp_error: &'a Counter,
131 }
132 let (without_socket_rx, without_socket_rx_error, without_socket_error) =
133 match without_socket.map(AsRef::as_ref) {
134 None => (None, None, None),
135 Some(UdpCountersWithoutSocketInner {
136 rx_icmp_error,
137 rx,
138 rx_mapped_addr,
139 rx_unknown_dest_port,
140 rx_malformed,
141 }) => (
142 Some(WithoutSocketRx { rx }),
143 Some(WithoutSocketRxError {
144 rx_mapped_addr,
145 rx_unknown_dest_port,
146 rx_malformed,
147 }),
148 Some(WithoutSocketError { rx_icmp_error }),
149 ),
150 };
151 inspector.record_child("Rx", |inspector| {
152 inspector.record_counter("Delivered", rx_delivered);
153 if let Some(WithoutSocketRx { rx }) = without_socket_rx {
154 inspector.record_counter("Received", rx);
155 }
156 inspector.record_child("Errors", |inspector| {
157 inspector.record_counter("DroppedQueueFull", rx_queue_full);
158 if let Some(WithoutSocketRxError {
159 rx_mapped_addr,
160 rx_unknown_dest_port,
161 rx_malformed,
162 }) = without_socket_rx_error
163 {
164 inspector.record_counter("MappedAddr", rx_mapped_addr);
165 inspector.record_counter("UnknownDstPort", rx_unknown_dest_port);
166 inspector.record_counter("Malformed", rx_malformed);
167 }
168 });
169 });
170 inspector.record_child("Tx", |inspector| {
171 inspector.record_counter("Sent", tx);
172 inspector.record_counter("Errors", tx_error);
173 });
174 if let Some(WithoutSocketError { rx_icmp_error }) = without_socket_error {
175 inspector.record_counter("IcmpErrors", rx_icmp_error);
176 }
177 }
178}
179
180#[cfg(test)]
181pub(crate) mod testutil {
182 use super::*;
183
184 pub(crate) type CounterExpectationsWithSocket = UdpCountersWithSocketInner<u64>;
185
186 pub(crate) type CounterExpectationsWithoutSocket = UdpCountersWithoutSocketInner<u64>;
187}