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_icmp_error_soft: C,
53 pub rx_icmp_error_hard: C,
55 pub rx_icmp_error_hard_malformed: C,
57 pub rx_icmp_error_hard_no_socket: C,
59 pub rx: C,
62 pub rx_mapped_addr: C,
65 pub rx_unknown_dest_port: C,
68 pub rx_malformed: C,
71}
72
73pub type UdpCountersWithSocket<I> = IpMarked<I, UdpCountersWithSocketInner>;
83
84#[derive(Default, Debug)]
88#[cfg_attr(
89 any(test, feature = "testutils"),
90 derive(PartialEq, netstack3_macros::CounterCollection)
91)]
92pub struct UdpCountersWithSocketInner<C = Counter> {
93 pub rx_delivered: C,
98 pub rx_queue_full: C,
101 pub tx: C,
104 pub tx_error: C,
107 pub rx_icmp_error_hard_delivered: C,
109}
110
111pub struct CombinedUdpCounters<'a, I: Ip> {
113 pub with_socket: &'a UdpCountersWithSocket<I>,
115 pub without_socket: Option<&'a UdpCountersWithoutSocket<I>>,
121}
122
123impl<I: Ip> Inspectable for CombinedUdpCounters<'_, I> {
124 fn record<II: Inspector>(&self, inspector: &mut II) {
125 let CombinedUdpCounters { with_socket, without_socket } = self;
126 let UdpCountersWithSocketInner {
127 rx_delivered,
128 rx_queue_full,
129 tx,
130 tx_error,
131 rx_icmp_error_hard_delivered,
132 } = with_socket.as_ref();
133
134 struct WithoutSocketRx<'a> {
137 rx: &'a Counter,
138 }
139 struct WithoutSocketRxError<'a> {
140 rx_mapped_addr: &'a Counter,
141 rx_unknown_dest_port: &'a Counter,
142 rx_malformed: &'a Counter,
143 }
144 struct WithoutSocketError<'a> {
145 rx_icmp_error: &'a Counter,
146 rx_icmp_error_soft: &'a Counter,
147 rx_icmp_error_hard: &'a Counter,
148 rx_icmp_error_hard_malformed: &'a Counter,
149 rx_icmp_error_hard_no_socket: &'a Counter,
150 }
151 let (without_socket_rx, without_socket_rx_error, without_socket_error) =
152 match without_socket.map(AsRef::as_ref) {
153 None => (None, None, None),
154 Some(UdpCountersWithoutSocketInner {
155 rx_icmp_error,
156 rx_icmp_error_soft,
157 rx_icmp_error_hard,
158 rx_icmp_error_hard_malformed,
159 rx_icmp_error_hard_no_socket,
160 rx,
161 rx_mapped_addr,
162 rx_unknown_dest_port,
163 rx_malformed,
164 }) => (
165 Some(WithoutSocketRx { rx }),
166 Some(WithoutSocketRxError {
167 rx_mapped_addr,
168 rx_unknown_dest_port,
169 rx_malformed,
170 }),
171 Some(WithoutSocketError {
172 rx_icmp_error,
173 rx_icmp_error_soft,
174 rx_icmp_error_hard,
175 rx_icmp_error_hard_malformed,
176 rx_icmp_error_hard_no_socket,
177 }),
178 ),
179 };
180 inspector.record_child("Rx", |inspector| {
181 inspector.record_counter("Delivered", rx_delivered);
182 if let Some(WithoutSocketRx { rx }) = without_socket_rx {
183 inspector.record_counter("Received", rx);
184 }
185 inspector.record_child("Errors", |inspector| {
186 inspector.record_counter("DroppedQueueFull", rx_queue_full);
187 inspector.record_counter("HardIcmpErrors", rx_icmp_error_hard_delivered);
188 if let Some(WithoutSocketRxError {
189 rx_mapped_addr,
190 rx_unknown_dest_port,
191 rx_malformed,
192 }) = without_socket_rx_error
193 {
194 inspector.record_counter("MappedAddr", rx_mapped_addr);
195 inspector.record_counter("UnknownDstPort", rx_unknown_dest_port);
196 inspector.record_counter("Malformed", rx_malformed);
197 }
198 });
199 });
200 inspector.record_child("Tx", |inspector| {
201 inspector.record_counter("Sent", tx);
202 inspector.record_counter("Errors", tx_error);
203 });
204 if let Some(WithoutSocketError {
205 rx_icmp_error,
206 rx_icmp_error_soft,
207 rx_icmp_error_hard,
208 rx_icmp_error_hard_malformed,
209 rx_icmp_error_hard_no_socket,
210 }) = without_socket_error
211 {
212 inspector.record_child("IcmpErrors", |inspector| {
213 inspector.record_counter("Count", rx_icmp_error);
214 inspector.record_counter("Soft", rx_icmp_error_soft);
215 inspector.record_counter("Hard", rx_icmp_error_hard);
216 inspector.record_counter("Malformed", rx_icmp_error_hard_malformed);
217 inspector.record_counter("NoSocket", rx_icmp_error_hard_no_socket);
218 });
219 }
220 }
221}
222
223#[cfg(test)]
224pub(crate) mod testutil {
225 use super::*;
226
227 pub(crate) type CounterExpectationsWithSocket = UdpCountersWithSocketInner<u64>;
228
229 pub(crate) type CounterExpectationsWithoutSocket = UdpCountersWithoutSocketInner<u64>;
230}