1use core::convert::Infallible as Never;
6use core::num::NonZeroU16;
7
8use net_types::ip::Ip;
9use netstack3_base::socket::SocketCookie;
10use netstack3_base::{
11 Marks, Matcher, MaybeSocketTransportProperties, SocketTransportProtocolMatcher,
12 UdpSocketProperties, UdpSocketState as UdpSocketMatcherState, WeakDeviceIdentifier,
13};
14use netstack3_datagram::{
15 DatagramSocketDiagnosticsSpec, IpExt, SocketInfo, SocketState as DatagramSocketState,
16};
17
18use crate::internal::base::{Udp, UdpBindingsTypes, UdpSocketId, UdpSocketState};
19
20#[derive(Debug)]
25#[cfg_attr(any(test, feature = "testutils"), derive(PartialEq, Eq))]
26#[allow(missing_docs)]
27pub struct UdpSocketDiagnostics<I: Ip> {
28 pub state: UdpSocketDiagnosticTuple<I>,
29 pub cookie: SocketCookie,
30 pub marks: Marks,
31}
32
33#[derive(Debug)]
35#[cfg_attr(any(test, feature = "testutils"), derive(PartialEq, Eq))]
36#[allow(missing_docs)]
37pub enum UdpSocketDiagnosticTuple<I: Ip> {
38 Bound { src_addr: Option<I::Addr>, src_port: NonZeroU16 },
39 Connected { src_addr: I::Addr, src_port: NonZeroU16, dst_addr: I::Addr, dst_port: u16 },
40}
41
42impl<I: Ip> UdpSocketDiagnosticTuple<I> {
43 pub fn src_addr(&self) -> Option<I::Addr> {
45 match self {
46 Self::Bound { src_addr, src_port: _ } => *src_addr,
47 Self::Connected { src_addr, src_port: _, dst_addr: _, dst_port: _ } => Some(*src_addr),
48 }
49 }
50
51 pub fn src_port(&self) -> Option<NonZeroU16> {
53 match self {
54 Self::Bound { src_addr: _, src_port }
55 | Self::Connected { src_addr: _, src_port, dst_addr: _, dst_port: _ } => {
56 Some(*src_port)
57 }
58 }
59 }
60
61 pub fn dst_addr(&self) -> Option<I::Addr> {
63 match self {
64 Self::Bound { src_addr: _, src_port: _ } => None,
65 Self::Connected { src_addr: _, src_port: _, dst_addr, dst_port: _ } => Some(*dst_addr),
66 }
67 }
68
69 pub fn dst_port(&self) -> Option<u16> {
71 match self {
72 Self::Bound { src_addr: _, src_port: _ } => None,
73 Self::Connected { src_addr: _, src_port: _, dst_addr: _, dst_port } => Some(*dst_port),
74 }
75 }
76}
77
78pub struct UdpTransportProtocolDiagnosticsProperties<'a, I, D, BT>(&'a UdpSocketState<I, D, BT>)
81where
82 I: IpExt,
83 D: WeakDeviceIdentifier,
84 BT: UdpBindingsTypes;
85
86impl<I, D, BT> MaybeSocketTransportProperties
87 for UdpTransportProtocolDiagnosticsProperties<'_, I, D, BT>
88where
89 I: IpExt,
90 D: WeakDeviceIdentifier,
91 BT: UdpBindingsTypes,
92{
93 type TcpProps<'a>
94 = Never
95 where
96 Self: 'a;
97
98 type UdpProps<'a>
99 = Self
100 where
101 Self: 'a;
102
103 fn tcp_socket_properties(&self) -> Option<&Self::TcpProps<'_>> {
104 None
105 }
106
107 fn udp_socket_properties(&self) -> Option<&Self::UdpProps<'_>> {
108 Some(self)
109 }
110}
111
112impl<I, D, BT> UdpSocketProperties for UdpTransportProtocolDiagnosticsProperties<'_, I, D, BT>
113where
114 I: IpExt,
115 D: WeakDeviceIdentifier,
116 BT: UdpBindingsTypes,
117{
118 fn src_port_matches(&self, matcher: &netstack3_base::PortMatcher) -> bool {
119 let Self(udp_state) = self;
120 matcher.required_matches(udp_state.local_identifier().map(|p| p.get()).as_ref())
121 }
122
123 fn dst_port_matches(&self, matcher: &netstack3_base::PortMatcher) -> bool {
124 let Self(udp_state) = self;
125 matcher.required_matches(udp_state.remote_identifier().as_ref())
126 }
127
128 fn state_matches(&self, matcher: &netstack3_base::UdpStateMatcher) -> bool {
129 let Self(udp_state) = self;
130 matcher.matches(match udp_state.to_socket_info() {
131 SocketInfo::Unbound => return false,
132 SocketInfo::Listener(_) => &UdpSocketMatcherState::Bound,
133 SocketInfo::Connected(_) => &UdpSocketMatcherState::Connected,
134 })
135 }
136}
137
138impl<BT: UdpBindingsTypes> DatagramSocketDiagnosticsSpec for Udp<BT> {
139 type DeviceClass = BT::DeviceClass;
140
141 fn transport_protocol_matches<I: IpExt, D: WeakDeviceIdentifier>(
142 state: &DatagramSocketState<I, D, Udp<BT>>,
143 matcher: &SocketTransportProtocolMatcher,
144 ) -> bool {
145 matcher.matches(&UdpTransportProtocolDiagnosticsProperties(state))
146 }
147
148 fn cookie_matches<I: IpExt, D: WeakDeviceIdentifier>(
149 id: &UdpSocketId<I, D, BT>,
150 matcher: &netstack3_base::SocketCookieMatcher,
151 ) -> bool {
152 matcher.matches(&id.socket_cookie().export_value())
153 }
154}
155
156#[cfg(test)]
157mod tests {
158 use alloc::string::ToString;
159 use alloc::vec;
160 use alloc::vec::Vec;
161 use core::num::NonZeroU16;
162
163 use ip_test_macro::ip_test;
164 use net_types::ip::Subnet;
165 use net_types::{Witness as _, ZonedAddr};
166 use netstack3_base::testutil::FakeDeviceId;
167 use netstack3_base::{
168 AddressMatcher, AddressMatcherEither, AddressMatcherType, BoundInterfaceMatcher,
169 InterfaceMatcher, IpSocketMatcher, Mark, MarkDomain, MarkMatcher, PortMatcher,
170 SocketCookieMatcher, SocketTransportProtocolMatcher, SubnetMatcher, TcpSocketMatcher,
171 UdpSocketMatcher, UdpStateMatcher,
172 };
173
174 use crate::internal::base::testutils::{FakeUdpCoreCtx, TestIpExt, UdpFakeDeviceCtx};
175 use crate::internal::base::{UdpApi, UdpRemotePort};
176
177 use super::*;
178
179 const LOCAL_PORT_1: NonZeroU16 = NonZeroU16::new(1234).unwrap();
180 const LOCAL_PORT_2: NonZeroU16 = NonZeroU16::new(5678).unwrap();
181 const LOCAL_PORT_3: NonZeroU16 = NonZeroU16::new(4321).unwrap();
182
183 const REMOTE_PORT_1: NonZeroU16 = NonZeroU16::new(100).unwrap();
184 const REMOTE_PORT_2: NonZeroU16 = NonZeroU16::new(200).unwrap();
185
186 const MARK: u32 = 0x10;
187 const MARK_MASK: u32 = !0;
188
189 #[ip_test(I)]
190 fn diagnostics_match_ip_version<I: TestIpExt>() {
191 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
192 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
193
194 let socket = api.create();
195 api.listen(&socket, None, Some(LOCAL_PORT_1)).expect("listen should succeed");
196
197 let mut results = Vec::new();
198 api.bound_sockets_diagnostics(&IpSocketMatcher::Family(I::VERSION), &mut results);
199 assert_eq!(
200 results,
201 vec![UdpSocketDiagnostics {
202 state: UdpSocketDiagnosticTuple::Bound { src_addr: None, src_port: LOCAL_PORT_1 },
203 cookie: socket.socket_cookie(),
204 marks: Marks::default(),
205 }]
206 );
207
208 results.clear();
209 api.bound_sockets_diagnostics(
210 &IpSocketMatcher::Family(
211 <<I as netstack3_base::socket::DualStackIpExt>::OtherVersion as Ip>::VERSION,
212 ),
213 &mut results,
214 );
215 assert_eq!(results, Vec::new());
216 }
217
218 #[ip_test(I)]
219 fn diagnostics_match_src_addr<I: TestIpExt>() {
220 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
221 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
222
223 let socket = api.create();
224 api.listen(&socket, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_1))
225 .expect("listen should succeed");
226
227 let mut results = Vec::new();
228 let matcher = I::map_ip_in(
229 I::TEST_ADDRS.subnet,
230 |subnet| {
231 AddressMatcherEither::V4(AddressMatcher {
232 matcher: AddressMatcherType::Subnet(SubnetMatcher(subnet)),
233 invert: false,
234 })
235 },
236 |subnet| {
237 AddressMatcherEither::V6(AddressMatcher {
238 matcher: AddressMatcherType::Subnet(SubnetMatcher(subnet)),
239 invert: false,
240 })
241 },
242 );
243 api.bound_sockets_diagnostics(&IpSocketMatcher::SrcAddr(matcher), &mut results);
244 assert_eq!(
245 results,
246 vec![UdpSocketDiagnostics {
247 state: UdpSocketDiagnosticTuple::Bound {
248 src_addr: Some(I::TEST_ADDRS.local_ip.get()),
249 src_port: LOCAL_PORT_1
250 },
251 cookie: socket.socket_cookie(),
252 marks: Marks::default(),
253 }]
254 );
255
256 results.clear();
257 let matcher = I::map_ip_in(
258 I::TEST_ADDRS.remote_ip.get(),
259 |addr| {
260 AddressMatcherEither::V4(AddressMatcher {
261 matcher: AddressMatcherType::Subnet(SubnetMatcher(
262 Subnet::new(addr, 32).unwrap(),
263 )),
264 invert: false,
265 })
266 },
267 |addr| {
268 AddressMatcherEither::V6(AddressMatcher {
269 matcher: AddressMatcherType::Subnet(SubnetMatcher(
270 Subnet::new(addr, 128).unwrap(),
271 )),
272 invert: false,
273 })
274 },
275 );
276 api.bound_sockets_diagnostics(&IpSocketMatcher::SrcAddr(matcher), &mut results);
277 assert_eq!(results, Vec::new());
278 }
279
280 #[ip_test(I)]
281 fn diagnostics_match_dst_addr<I: TestIpExt>() {
282 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
283 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
284
285 let socket = api.create();
286 api.listen(&socket, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_2))
287 .expect("listen should succeed");
288 api.connect(
289 &socket,
290 Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)),
291 UdpRemotePort::Set(LOCAL_PORT_1),
292 )
293 .expect("connect should succeed");
294
295 let mut results = Vec::new();
296 let matcher = I::map_ip_in(
297 I::TEST_ADDRS.subnet,
298 |subnet| {
299 AddressMatcherEither::V4(AddressMatcher {
300 matcher: AddressMatcherType::Subnet(SubnetMatcher(subnet)),
301 invert: false,
302 })
303 },
304 |subnet| {
305 AddressMatcherEither::V6(AddressMatcher {
306 matcher: AddressMatcherType::Subnet(SubnetMatcher(subnet)),
307 invert: false,
308 })
309 },
310 );
311 api.bound_sockets_diagnostics(&IpSocketMatcher::DstAddr(matcher), &mut results);
312 assert_eq!(
313 results,
314 vec![UdpSocketDiagnostics {
315 state: UdpSocketDiagnosticTuple::Connected {
316 src_addr: I::TEST_ADDRS.local_ip.get(),
317 src_port: LOCAL_PORT_2,
318 dst_addr: I::TEST_ADDRS.remote_ip.get(),
319 dst_port: LOCAL_PORT_1.get(),
320 },
321 cookie: socket.socket_cookie(),
322 marks: Marks::default(),
323 }]
324 );
325
326 results.clear();
327 let matcher = I::map_ip_in(
328 I::TEST_ADDRS.local_ip.get(),
329 |addr| {
330 AddressMatcherEither::V4(AddressMatcher {
331 matcher: AddressMatcherType::Subnet(SubnetMatcher(
332 Subnet::new(addr, 32).unwrap(),
333 )),
334 invert: false,
335 })
336 },
337 |addr| {
338 AddressMatcherEither::V6(AddressMatcher {
339 matcher: AddressMatcherType::Subnet(SubnetMatcher(
340 Subnet::new(addr, 128).unwrap(),
341 )),
342 invert: false,
343 })
344 },
345 );
346 api.bound_sockets_diagnostics(&IpSocketMatcher::DstAddr(matcher), &mut results);
347 assert_eq!(results, Vec::new());
348 }
349
350 #[ip_test(I)]
351 fn diagnostics_match_proto<I: TestIpExt>() {
352 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
353 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
354
355 let socket = api.create();
356 api.listen(&socket, None, Some(LOCAL_PORT_1)).expect("listen should succeed");
357
358 let mut results = Vec::new();
359 api.bound_sockets_diagnostics(
360 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Udp(UdpSocketMatcher::Empty)),
361 &mut results,
362 );
363 assert_eq!(
364 results,
365 vec![UdpSocketDiagnostics {
366 state: UdpSocketDiagnosticTuple::Bound { src_addr: None, src_port: LOCAL_PORT_1 },
367 cookie: socket.socket_cookie(),
368 marks: Marks::default(),
369 }]
370 );
371
372 results.clear();
373 api.bound_sockets_diagnostics(
374 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Tcp(TcpSocketMatcher::Empty)),
375 &mut results,
376 );
377 assert_eq!(results, Vec::new());
378 }
379
380 #[ip_test(I)]
381 fn diagnostics_match_src_port<I: TestIpExt>() {
382 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
383 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
384
385 let socket = api.create();
386 api.listen(&socket, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_1))
387 .expect("listen should succeed");
388
389 let mut results = Vec::new();
390 api.bound_sockets_diagnostics(
391 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Udp(
392 UdpSocketMatcher::SrcPort(PortMatcher {
393 range: LOCAL_PORT_1.get()..=LOCAL_PORT_1.get(),
394 invert: false,
395 }),
396 )),
397 &mut results,
398 );
399 assert_eq!(
400 results,
401 vec![UdpSocketDiagnostics {
402 state: UdpSocketDiagnosticTuple::Bound {
403 src_addr: Some(I::TEST_ADDRS.local_ip.get()),
404 src_port: LOCAL_PORT_1
405 },
406 cookie: socket.socket_cookie(),
407 marks: Marks::default(),
408 }]
409 );
410
411 results.clear();
412 api.bound_sockets_diagnostics(
413 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Udp(
414 UdpSocketMatcher::SrcPort(PortMatcher {
415 range: (LOCAL_PORT_1.get() + 1)..=(LOCAL_PORT_1.get() + 1),
416 invert: false,
417 }),
418 )),
419 &mut results,
420 );
421 assert_eq!(results, Vec::new());
422 }
423
424 #[ip_test(I)]
425 fn diagnostics_match_dst_port<I: TestIpExt>() {
426 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
427 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
428
429 let socket = api.create();
430 api.listen(&socket, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_2))
431 .expect("listen should succeed");
432 api.connect(
433 &socket,
434 Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)),
435 UdpRemotePort::Set(LOCAL_PORT_1),
436 )
437 .expect("connect should succeed");
438
439 let mut results = Vec::new();
440 api.bound_sockets_diagnostics(
441 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Udp(
442 UdpSocketMatcher::DstPort(PortMatcher {
443 range: LOCAL_PORT_1.get()..=LOCAL_PORT_1.get(),
444 invert: false,
445 }),
446 )),
447 &mut results,
448 );
449 assert_eq!(
450 results,
451 vec![UdpSocketDiagnostics {
452 state: UdpSocketDiagnosticTuple::Connected {
453 src_addr: I::TEST_ADDRS.local_ip.get(),
454 src_port: LOCAL_PORT_2,
455 dst_addr: I::TEST_ADDRS.remote_ip.get(),
456 dst_port: LOCAL_PORT_1.get(),
457 },
458 cookie: socket.socket_cookie(),
459 marks: Marks::default(),
460 }]
461 );
462
463 results.clear();
464 api.bound_sockets_diagnostics(
465 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Udp(
466 UdpSocketMatcher::DstPort(PortMatcher {
467 range: (LOCAL_PORT_1.get() + 1)..=(LOCAL_PORT_1.get() + 1),
468 invert: false,
469 }),
470 )),
471 &mut results,
472 );
473 assert_eq!(results, Vec::new());
474 }
475
476 #[ip_test(I)]
477 fn diagnostics_match_state<I: TestIpExt>() {
478 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
479 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
480
481 let socket_1 = api.create();
482 api.listen(&socket_1, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_1))
483 .expect("listen should succeed");
484
485 let socket_2 = api.create();
486 api.listen(&socket_2, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_2))
487 .expect("listen should succeed");
488 api.connect(
489 &socket_2,
490 Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)),
491 UdpRemotePort::Set(LOCAL_PORT_3),
492 )
493 .expect("connect should succeed");
494
495 let mut results = Vec::new();
496 api.bound_sockets_diagnostics(
497 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Udp(UdpSocketMatcher::State(
498 UdpStateMatcher::BOUND,
499 ))),
500 &mut results,
501 );
502 assert_eq!(
503 results,
504 vec![UdpSocketDiagnostics {
505 state: UdpSocketDiagnosticTuple::Bound {
506 src_addr: Some(I::TEST_ADDRS.local_ip.get()),
507 src_port: LOCAL_PORT_1
508 },
509 cookie: socket_1.socket_cookie(),
510 marks: Marks::default(),
511 }]
512 );
513
514 results.clear();
515 api.bound_sockets_diagnostics(
516 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Udp(UdpSocketMatcher::State(
517 UdpStateMatcher::CONNECTED,
518 ))),
519 &mut results,
520 );
521 assert_eq!(
522 results,
523 vec![UdpSocketDiagnostics {
524 state: UdpSocketDiagnosticTuple::Connected {
525 src_addr: I::TEST_ADDRS.local_ip.get(),
526 src_port: LOCAL_PORT_2,
527 dst_addr: I::TEST_ADDRS.remote_ip.get(),
528 dst_port: LOCAL_PORT_3.get(),
529 },
530 cookie: socket_2.socket_cookie(),
531 marks: Marks::default(),
532 }]
533 );
534 }
535
536 #[ip_test(I)]
537 fn diagnostics_match_device<I: TestIpExt>() {
538 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
539 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
540
541 let socket = api.create();
542 api.set_device(&socket, Some(&FakeDeviceId)).expect("set device should succeed");
543 api.listen(&socket, None, Some(LOCAL_PORT_1)).expect("listen should succeed");
544
545 let mut results = Vec::new();
546 api.bound_sockets_diagnostics(
547 &IpSocketMatcher::BoundInterface(BoundInterfaceMatcher::Bound(InterfaceMatcher::Name(
548 FakeDeviceId::FAKE_NAME.to_string(),
549 ))),
550 &mut results,
551 );
552 assert_eq!(
553 results,
554 vec![UdpSocketDiagnostics {
555 state: UdpSocketDiagnosticTuple::Bound { src_addr: None, src_port: LOCAL_PORT_1 },
556 cookie: socket.socket_cookie(),
557 marks: Marks::default(),
558 }]
559 );
560
561 results.clear();
562 api.bound_sockets_diagnostics(
563 &IpSocketMatcher::BoundInterface(BoundInterfaceMatcher::Unbound),
564 &mut results,
565 );
566 assert_eq!(results, Vec::new());
567 }
568
569 #[ip_test(I)]
570 fn diagnostics_match_cookie<I: TestIpExt>() {
571 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
572 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
573
574 let socket = api.create();
575 api.listen(&socket, None, Some(LOCAL_PORT_1)).expect("listen should succeed");
576
577 let mut results = Vec::new();
578 api.bound_sockets_diagnostics(
579 &IpSocketMatcher::Cookie(SocketCookieMatcher {
580 cookie: socket.socket_cookie().export_value(),
581 invert: false,
582 }),
583 &mut results,
584 );
585 assert_eq!(
586 results,
587 vec![UdpSocketDiagnostics {
588 state: UdpSocketDiagnosticTuple::Bound { src_addr: None, src_port: LOCAL_PORT_1 },
589 cookie: socket.socket_cookie(),
590 marks: Marks::default(),
591 }]
592 );
593
594 results.clear();
595 api.bound_sockets_diagnostics(
596 &IpSocketMatcher::Cookie(SocketCookieMatcher {
597 cookie: socket.socket_cookie().export_value() + 1,
598 invert: false,
599 }),
600 &mut results,
601 );
602 assert_eq!(results, Vec::new());
603 }
604
605 #[ip_test(I)]
606 #[test_case::test_case(MarkDomain::Mark1; "mark_1")]
607 #[test_case::test_case(MarkDomain::Mark2; "mark_2")]
608 fn diagnostics_match_mark<I: TestIpExt>(domain: MarkDomain) {
609 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
610 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
611
612 let socket = api.create();
613 api.listen(&socket, None, Some(LOCAL_PORT_1)).expect("listen should succeed");
614
615 api.set_mark(&socket, domain, Mark(Some(MARK)));
616
617 let mut results = Vec::new();
618 let matcher = |query_mark| {
619 IpSocketMatcher::Mark(netstack3_base::MarkInDomainMatcher {
620 domain,
621 matcher: MarkMatcher::Marked {
622 mask: MARK_MASK,
623 start: query_mark,
624 end: query_mark,
625 invert: false,
626 },
627 })
628 };
629 api.bound_sockets_diagnostics(&matcher(MARK), &mut results);
630 assert_eq!(
631 results,
632 vec![UdpSocketDiagnostics {
633 state: UdpSocketDiagnosticTuple::Bound { src_addr: None, src_port: LOCAL_PORT_1 },
634 cookie: socket.socket_cookie(),
635 marks: netstack3_base::MarkStorage::new([(domain, MARK)]),
636 }]
637 );
638
639 results.clear();
640 api.bound_sockets_diagnostics(&matcher(MARK + 1), &mut results);
641 assert_eq!(results, Vec::new());
642 }
643
644 #[ip_test(I)]
647 fn diagnostics_match_multiple<I: TestIpExt>() {
648 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
649 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
650
651 let socket_1 = api.create();
652 api.listen(&socket_1, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_1))
653 .expect("listen should succeed");
654 api.connect(
655 &socket_1,
656 Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)),
657 UdpRemotePort::Set(REMOTE_PORT_1),
658 )
659 .expect("connect should succeed");
660
661 let socket_2 = api.create();
662 api.listen(&socket_2, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_2))
663 .expect("listen should succeed");
664 api.connect(
665 &socket_2,
666 Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)),
667 UdpRemotePort::Set(REMOTE_PORT_1),
668 )
669 .expect("connect should succeed");
670
671 let socket_3 = api.create();
672 api.listen(&socket_3, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_3))
673 .expect("listen should succeed");
674 api.connect(
675 &socket_3,
676 Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)),
677 UdpRemotePort::Set(REMOTE_PORT_2),
678 )
679 .expect("connect should succeed");
680
681 let mut results = Vec::new();
682 api.bound_sockets_diagnostics(
683 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Udp(
684 UdpSocketMatcher::DstPort(PortMatcher {
685 range: REMOTE_PORT_1.get()..=REMOTE_PORT_1.get(),
686 invert: false,
687 }),
688 )),
689 &mut results,
690 );
691
692 results.sort_by(|a, b| a.cookie.cmp(&b.cookie));
693 let mut expected = vec![
694 UdpSocketDiagnostics {
695 state: UdpSocketDiagnosticTuple::Connected {
696 src_addr: I::TEST_ADDRS.local_ip.get(),
697 src_port: LOCAL_PORT_1,
698 dst_addr: I::TEST_ADDRS.remote_ip.get(),
699 dst_port: REMOTE_PORT_1.get(),
700 },
701 cookie: socket_1.socket_cookie(),
702 marks: Marks::default(),
703 },
704 UdpSocketDiagnostics {
705 state: UdpSocketDiagnosticTuple::Connected {
706 src_addr: I::TEST_ADDRS.local_ip.get(),
707 src_port: LOCAL_PORT_2,
708 dst_addr: I::TEST_ADDRS.remote_ip.get(),
709 dst_port: REMOTE_PORT_1.get(),
710 },
711 cookie: socket_2.socket_cookie(),
712 marks: Marks::default(),
713 },
714 ];
715 expected.sort_by(|a, b| a.cookie.cmp(&b.cookie));
716 assert_eq!(results, expected);
717
718 results.clear();
719 api.bound_sockets_diagnostics(
720 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Udp(
721 UdpSocketMatcher::DstPort(PortMatcher {
722 range: REMOTE_PORT_2.get()..=REMOTE_PORT_2.get(),
723 invert: false,
724 }),
725 )),
726 &mut results,
727 );
728 assert_eq!(
729 results,
730 vec![UdpSocketDiagnostics {
731 state: UdpSocketDiagnosticTuple::Connected {
732 src_addr: I::TEST_ADDRS.local_ip.get(),
733 src_port: LOCAL_PORT_3,
734 dst_addr: I::TEST_ADDRS.remote_ip.get(),
735 dst_port: REMOTE_PORT_2.get(),
736 },
737 cookie: socket_3.socket_cookie(),
738 marks: Marks::default(),
739 }]
740 );
741 }
742}