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::BoundPortMatcher) -> bool {
119 let Self(udp_state) = self;
120 matcher.matches(&udp_state.local_identifier().map(|p| p.get()))
121 }
122
123 fn dst_port_matches(&self, matcher: &netstack3_base::BoundPortMatcher) -> bool {
124 let Self(udp_state) = self;
125 matcher.matches(&udp_state.remote_identifier())
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, set_logger_for_test};
167 use netstack3_base::{
168 AddressMatcher, AddressMatcherEither, AddressMatcherType, BoundAddressMatcherEither,
169 BoundInterfaceMatcher, BoundPortMatcher, InterfaceMatcher, IpSocketMatcher, Mark,
170 MarkDomain, MarkMatcher, PortMatcher, SocketCookieMatcher, SocketTransportProtocolMatcher,
171 SubnetMatcher, TcpSocketMatcher, 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 set_logger_for_test();
192
193 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
194 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
195
196 let socket = api.create();
197 api.listen(&socket, None, Some(LOCAL_PORT_1)).expect("listen should succeed");
198
199 let mut results = Vec::new();
200 api.bound_sockets_diagnostics(&IpSocketMatcher::Family(I::VERSION), &mut results);
201 assert_eq!(
202 results,
203 vec![UdpSocketDiagnostics {
204 state: UdpSocketDiagnosticTuple::Bound { src_addr: None, src_port: LOCAL_PORT_1 },
205 cookie: socket.socket_cookie(),
206 marks: Marks::default(),
207 }]
208 );
209
210 results.clear();
211 api.bound_sockets_diagnostics(
212 &IpSocketMatcher::Family(
213 <<I as netstack3_base::socket::DualStackIpExt>::OtherVersion as Ip>::VERSION,
214 ),
215 &mut results,
216 );
217 assert_eq!(results, Vec::new());
218 }
219
220 #[ip_test(I)]
221 fn diagnostics_match_src_addr<I: TestIpExt>() {
222 set_logger_for_test();
223
224 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
225 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
226
227 let socket = api.create();
228 api.listen(&socket, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_1))
229 .expect("listen should succeed");
230
231 let mut results = Vec::new();
232 let matcher = I::map_ip_in(
233 I::TEST_ADDRS.subnet,
234 |subnet| {
235 BoundAddressMatcherEither::Bound(AddressMatcherEither::V4(AddressMatcher {
236 matcher: AddressMatcherType::Subnet(SubnetMatcher(subnet)),
237 invert: false,
238 }))
239 },
240 |subnet| {
241 BoundAddressMatcherEither::Bound(AddressMatcherEither::V6(AddressMatcher {
242 matcher: AddressMatcherType::Subnet(SubnetMatcher(subnet)),
243 invert: false,
244 }))
245 },
246 );
247 api.bound_sockets_diagnostics(&IpSocketMatcher::SrcAddr(matcher), &mut results);
248 assert_eq!(
249 results,
250 vec![UdpSocketDiagnostics {
251 state: UdpSocketDiagnosticTuple::Bound {
252 src_addr: Some(I::TEST_ADDRS.local_ip.get()),
253 src_port: LOCAL_PORT_1
254 },
255 cookie: socket.socket_cookie(),
256 marks: Marks::default(),
257 }]
258 );
259
260 results.clear();
261 let matcher = I::map_ip_in(
262 I::TEST_ADDRS.remote_ip.get(),
263 |addr| {
264 BoundAddressMatcherEither::Bound(AddressMatcherEither::V4(AddressMatcher {
265 matcher: AddressMatcherType::Subnet(SubnetMatcher(
266 Subnet::new(addr, 32).unwrap(),
267 )),
268 invert: false,
269 }))
270 },
271 |addr| {
272 BoundAddressMatcherEither::Bound(AddressMatcherEither::V6(AddressMatcher {
273 matcher: AddressMatcherType::Subnet(SubnetMatcher(
274 Subnet::new(addr, 128).unwrap(),
275 )),
276 invert: false,
277 }))
278 },
279 );
280 api.bound_sockets_diagnostics(&IpSocketMatcher::SrcAddr(matcher), &mut results);
281 assert_eq!(results, Vec::new());
282 }
283
284 #[ip_test(I)]
285 fn diagnostics_match_src_addr_unbound<I: TestIpExt>() {
286 set_logger_for_test();
287
288 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
289 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
290
291 let socket1 = api.create();
292 api.listen(&socket1, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_1))
293 .expect("listen should succeed");
294
295 let socket2 = api.create();
296 api.listen(&socket2, None, Some(LOCAL_PORT_2)).expect("listen should succeed");
297
298 let mut results = Vec::new();
299 let matcher = match I::VERSION {
300 net_types::ip::IpVersion::V4 => BoundAddressMatcherEither::Unbound,
301 net_types::ip::IpVersion::V6 => BoundAddressMatcherEither::Unbound,
302 };
303 api.bound_sockets_diagnostics(&IpSocketMatcher::SrcAddr(matcher), &mut results);
304 assert_eq!(
305 results,
306 vec![UdpSocketDiagnostics {
307 state: UdpSocketDiagnosticTuple::Bound { src_addr: None, src_port: LOCAL_PORT_2 },
308 cookie: socket2.socket_cookie(),
309 marks: Marks::default(),
310 }]
311 );
312
313 results.clear();
314 let matcher = I::map_ip_in(
315 I::TEST_ADDRS.subnet,
316 |subnet| {
317 BoundAddressMatcherEither::Bound(AddressMatcherEither::V4(AddressMatcher {
318 matcher: AddressMatcherType::Subnet(SubnetMatcher(subnet)),
319 invert: false,
320 }))
321 },
322 |subnet| {
323 BoundAddressMatcherEither::Bound(AddressMatcherEither::V6(AddressMatcher {
324 matcher: AddressMatcherType::Subnet(SubnetMatcher(subnet)),
325 invert: false,
326 }))
327 },
328 );
329 api.bound_sockets_diagnostics(&IpSocketMatcher::SrcAddr(matcher), &mut results);
330 assert_eq!(
331 results,
332 vec![UdpSocketDiagnostics {
333 state: UdpSocketDiagnosticTuple::Bound {
334 src_addr: Some(I::TEST_ADDRS.local_ip.get()),
335 src_port: LOCAL_PORT_1
336 },
337 cookie: socket1.socket_cookie(),
338 marks: Marks::default(),
339 }]
340 );
341 }
342
343 #[ip_test(I)]
344 fn diagnostics_match_dst_addr<I: TestIpExt>() {
345 set_logger_for_test();
346
347 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
348 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
349
350 let socket = api.create();
351 api.listen(&socket, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_2))
352 .expect("listen should succeed");
353 api.connect(
354 &socket,
355 Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)),
356 UdpRemotePort::Set(LOCAL_PORT_1),
357 )
358 .expect("connect should succeed");
359
360 let mut results = Vec::new();
361 let matcher = I::map_ip_in(
362 I::TEST_ADDRS.subnet,
363 |subnet| {
364 BoundAddressMatcherEither::Bound(AddressMatcherEither::V4(AddressMatcher {
365 matcher: AddressMatcherType::Subnet(SubnetMatcher(subnet)),
366 invert: false,
367 }))
368 },
369 |subnet| {
370 BoundAddressMatcherEither::Bound(AddressMatcherEither::V6(AddressMatcher {
371 matcher: AddressMatcherType::Subnet(SubnetMatcher(subnet)),
372 invert: false,
373 }))
374 },
375 );
376 api.bound_sockets_diagnostics(&IpSocketMatcher::DstAddr(matcher), &mut results);
377 assert_eq!(
378 results,
379 vec![UdpSocketDiagnostics {
380 state: UdpSocketDiagnosticTuple::Connected {
381 src_addr: I::TEST_ADDRS.local_ip.get(),
382 src_port: LOCAL_PORT_2,
383 dst_addr: I::TEST_ADDRS.remote_ip.get(),
384 dst_port: LOCAL_PORT_1.get(),
385 },
386 cookie: socket.socket_cookie(),
387 marks: Marks::default(),
388 }]
389 );
390
391 results.clear();
392 let matcher = I::map_ip_in(
393 I::TEST_ADDRS.local_ip.get(),
394 |addr| {
395 BoundAddressMatcherEither::Bound(AddressMatcherEither::V4(AddressMatcher {
396 matcher: AddressMatcherType::Subnet(SubnetMatcher(
397 Subnet::new(addr, 32).unwrap(),
398 )),
399 invert: false,
400 }))
401 },
402 |addr| {
403 BoundAddressMatcherEither::Bound(AddressMatcherEither::V6(AddressMatcher {
404 matcher: AddressMatcherType::Subnet(SubnetMatcher(
405 Subnet::new(addr, 128).unwrap(),
406 )),
407 invert: false,
408 }))
409 },
410 );
411 api.bound_sockets_diagnostics(&IpSocketMatcher::DstAddr(matcher), &mut results);
412 assert_eq!(results, Vec::new());
413 }
414
415 #[ip_test(I)]
416 fn diagnostics_match_dst_addr_unbound<I: TestIpExt>() {
417 set_logger_for_test();
418
419 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
420 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
421
422 let socket1 = api.create();
423 api.listen(&socket1, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_1))
424 .expect("listen should succeed");
425 api.connect(
426 &socket1,
427 Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)),
428 UdpRemotePort::Set(REMOTE_PORT_1),
429 )
430 .expect("connect should succeed");
431
432 let socket2 = api.create();
433 api.listen(&socket2, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_2))
434 .expect("listen should succeed");
435
436 let mut results = Vec::new();
437 let matcher = match I::VERSION {
438 net_types::ip::IpVersion::V4 => BoundAddressMatcherEither::Unbound,
439 net_types::ip::IpVersion::V6 => BoundAddressMatcherEither::Unbound,
440 };
441 api.bound_sockets_diagnostics(&IpSocketMatcher::DstAddr(matcher), &mut results);
442 assert_eq!(
443 results,
444 vec![UdpSocketDiagnostics {
445 state: UdpSocketDiagnosticTuple::Bound {
446 src_addr: Some(I::TEST_ADDRS.local_ip.get()),
447 src_port: LOCAL_PORT_2
448 },
449 cookie: socket2.socket_cookie(),
450 marks: Marks::default(),
451 }]
452 );
453
454 results.clear();
455 let matcher = I::map_ip_in(
456 I::TEST_ADDRS.remote_ip.get(),
457 |addr| {
458 BoundAddressMatcherEither::Bound(AddressMatcherEither::V4(AddressMatcher {
459 matcher: AddressMatcherType::Subnet(SubnetMatcher(
460 Subnet::new(addr, 32).unwrap(),
461 )),
462 invert: false,
463 }))
464 },
465 |addr| {
466 BoundAddressMatcherEither::Bound(AddressMatcherEither::V6(AddressMatcher {
467 matcher: AddressMatcherType::Subnet(SubnetMatcher(
468 Subnet::new(addr, 128).unwrap(),
469 )),
470 invert: false,
471 }))
472 },
473 );
474 api.bound_sockets_diagnostics(&IpSocketMatcher::DstAddr(matcher), &mut results);
475 assert_eq!(
476 results,
477 vec![UdpSocketDiagnostics {
478 state: UdpSocketDiagnosticTuple::Connected {
479 src_addr: I::TEST_ADDRS.local_ip.get(),
480 src_port: LOCAL_PORT_1,
481 dst_addr: I::TEST_ADDRS.remote_ip.get(),
482 dst_port: REMOTE_PORT_1.get(),
483 },
484 cookie: socket1.socket_cookie(),
485 marks: Marks::default(),
486 }]
487 );
488 }
489
490 #[ip_test(I)]
491 fn diagnostics_match_proto<I: TestIpExt>() {
492 set_logger_for_test();
493
494 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
495 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
496
497 let socket = api.create();
498 api.listen(&socket, None, Some(LOCAL_PORT_1)).expect("listen should succeed");
499
500 let mut results = Vec::new();
501 api.bound_sockets_diagnostics(
502 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Udp(UdpSocketMatcher::Empty)),
503 &mut results,
504 );
505 assert_eq!(
506 results,
507 vec![UdpSocketDiagnostics {
508 state: UdpSocketDiagnosticTuple::Bound { src_addr: None, src_port: LOCAL_PORT_1 },
509 cookie: socket.socket_cookie(),
510 marks: Marks::default(),
511 }]
512 );
513
514 results.clear();
515 api.bound_sockets_diagnostics(
516 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Tcp(TcpSocketMatcher::Empty)),
517 &mut results,
518 );
519 assert_eq!(results, Vec::new());
520 }
521
522 #[ip_test(I)]
523 fn diagnostics_match_src_port<I: TestIpExt>() {
524 set_logger_for_test();
525
526 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
527 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
528
529 let socket = api.create();
530 api.listen(&socket, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_1))
531 .expect("listen should succeed");
532
533 let mut results = Vec::new();
534 api.bound_sockets_diagnostics(
535 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Udp(
536 UdpSocketMatcher::SrcPort(BoundPortMatcher::Bound(PortMatcher {
537 range: LOCAL_PORT_1.get()..=LOCAL_PORT_1.get(),
538 invert: false,
539 })),
540 )),
541 &mut results,
542 );
543 assert_eq!(
544 results,
545 vec![UdpSocketDiagnostics {
546 state: UdpSocketDiagnosticTuple::Bound {
547 src_addr: Some(I::TEST_ADDRS.local_ip.get()),
548 src_port: LOCAL_PORT_1
549 },
550 cookie: socket.socket_cookie(),
551 marks: Marks::default(),
552 }]
553 );
554
555 results.clear();
556 api.bound_sockets_diagnostics(
557 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Udp(
558 UdpSocketMatcher::SrcPort(BoundPortMatcher::Bound(PortMatcher {
559 range: (LOCAL_PORT_1.get() + 1)..=(LOCAL_PORT_1.get() + 1),
560 invert: false,
561 })),
562 )),
563 &mut results,
564 );
565 assert_eq!(results, Vec::new());
566 }
567
568 #[ip_test(I)]
569 fn diagnostics_match_dst_port<I: TestIpExt>() {
570 set_logger_for_test();
571
572 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
573 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
574
575 let socket = api.create();
576 api.listen(&socket, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_2))
577 .expect("listen should succeed");
578 api.connect(
579 &socket,
580 Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)),
581 UdpRemotePort::Set(LOCAL_PORT_1),
582 )
583 .expect("connect should succeed");
584
585 let mut results = Vec::new();
586 api.bound_sockets_diagnostics(
587 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Udp(
588 UdpSocketMatcher::DstPort(BoundPortMatcher::Bound(PortMatcher {
589 range: LOCAL_PORT_1.get()..=LOCAL_PORT_1.get(),
590 invert: false,
591 })),
592 )),
593 &mut results,
594 );
595 assert_eq!(
596 results,
597 vec![UdpSocketDiagnostics {
598 state: UdpSocketDiagnosticTuple::Connected {
599 src_addr: I::TEST_ADDRS.local_ip.get(),
600 src_port: LOCAL_PORT_2,
601 dst_addr: I::TEST_ADDRS.remote_ip.get(),
602 dst_port: LOCAL_PORT_1.get(),
603 },
604 cookie: socket.socket_cookie(),
605 marks: Marks::default(),
606 }]
607 );
608
609 results.clear();
610 api.bound_sockets_diagnostics(
611 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Udp(
612 UdpSocketMatcher::DstPort(BoundPortMatcher::Bound(PortMatcher {
613 range: (LOCAL_PORT_1.get() + 1)..=(LOCAL_PORT_1.get() + 1),
614 invert: false,
615 })),
616 )),
617 &mut results,
618 );
619 assert_eq!(results, Vec::new());
620 }
621
622 #[ip_test(I)]
623 fn diagnostics_match_src_port_unbound<I: TestIpExt>() {
624 set_logger_for_test();
625
626 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
627 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
628
629 let socket = api.create();
630 api.listen(&socket, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_1))
631 .expect("listen should succeed");
632
633 let mut results = Vec::new();
634 api.bound_sockets_diagnostics(
636 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Udp(
637 UdpSocketMatcher::SrcPort(BoundPortMatcher::Unbound),
638 )),
639 &mut results,
640 );
641 assert_eq!(results, Vec::new());
642 }
643
644 #[ip_test(I)]
645 fn diagnostics_match_dst_port_unbound<I: TestIpExt>() {
646 set_logger_for_test();
647
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 socket1 = api.create();
653 api.listen(&socket1, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_1))
654 .expect("listen should succeed");
655
656 let socket2 = api.create();
658 api.listen(&socket2, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_2))
659 .expect("listen should succeed");
660 api.connect(
661 &socket2,
662 Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)),
663 UdpRemotePort::Set(REMOTE_PORT_2),
664 )
665 .expect("connect should succeed");
666
667 let mut results = Vec::new();
668 api.bound_sockets_diagnostics(
669 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Udp(
670 UdpSocketMatcher::DstPort(BoundPortMatcher::Unbound),
671 )),
672 &mut results,
673 );
674 assert_eq!(
675 results,
676 vec![UdpSocketDiagnostics {
677 state: UdpSocketDiagnosticTuple::Bound {
678 src_addr: Some(I::TEST_ADDRS.local_ip.get()),
679 src_port: LOCAL_PORT_1
680 },
681 cookie: socket1.socket_cookie(),
682 marks: Marks::default(),
683 }]
684 );
685
686 results.clear();
687 api.bound_sockets_diagnostics(
688 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Udp(
689 UdpSocketMatcher::DstPort(BoundPortMatcher::Unbound),
690 )),
691 &mut results,
692 );
693 assert_eq!(
694 results,
695 vec![UdpSocketDiagnostics {
696 state: UdpSocketDiagnosticTuple::Bound {
697 src_addr: Some(I::TEST_ADDRS.local_ip.get()),
698 src_port: LOCAL_PORT_1,
699 },
700 cookie: socket1.socket_cookie(),
701 marks: Marks::default(),
702 }]
703 );
704 }
705
706 #[ip_test(I)]
707 fn diagnostics_match_state<I: TestIpExt>() {
708 set_logger_for_test();
709
710 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
711 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
712
713 let socket_1 = api.create();
714 api.listen(&socket_1, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_1))
715 .expect("listen should succeed");
716
717 let socket_2 = api.create();
718 api.listen(&socket_2, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_2))
719 .expect("listen should succeed");
720 api.connect(
721 &socket_2,
722 Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)),
723 UdpRemotePort::Set(LOCAL_PORT_3),
724 )
725 .expect("connect should succeed");
726
727 let mut results = Vec::new();
728 api.bound_sockets_diagnostics(
729 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Udp(UdpSocketMatcher::State(
730 UdpStateMatcher::BOUND,
731 ))),
732 &mut results,
733 );
734 assert_eq!(
735 results,
736 vec![UdpSocketDiagnostics {
737 state: UdpSocketDiagnosticTuple::Bound {
738 src_addr: Some(I::TEST_ADDRS.local_ip.get()),
739 src_port: LOCAL_PORT_1
740 },
741 cookie: socket_1.socket_cookie(),
742 marks: Marks::default(),
743 }]
744 );
745
746 results.clear();
747 api.bound_sockets_diagnostics(
748 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Udp(UdpSocketMatcher::State(
749 UdpStateMatcher::CONNECTED,
750 ))),
751 &mut results,
752 );
753 assert_eq!(
754 results,
755 vec![UdpSocketDiagnostics {
756 state: UdpSocketDiagnosticTuple::Connected {
757 src_addr: I::TEST_ADDRS.local_ip.get(),
758 src_port: LOCAL_PORT_2,
759 dst_addr: I::TEST_ADDRS.remote_ip.get(),
760 dst_port: LOCAL_PORT_3.get(),
761 },
762 cookie: socket_2.socket_cookie(),
763 marks: Marks::default(),
764 }]
765 );
766 }
767
768 #[ip_test(I)]
769 fn diagnostics_match_device<I: TestIpExt>() {
770 set_logger_for_test();
771
772 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
773 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
774
775 let socket = api.create();
776 api.set_device(&socket, Some(&FakeDeviceId)).expect("set device should succeed");
777 api.listen(&socket, None, Some(LOCAL_PORT_1)).expect("listen should succeed");
778
779 let mut results = Vec::new();
780 api.bound_sockets_diagnostics(
781 &IpSocketMatcher::BoundInterface(BoundInterfaceMatcher::Bound(InterfaceMatcher::Name(
782 FakeDeviceId::FAKE_NAME.to_string(),
783 ))),
784 &mut results,
785 );
786 assert_eq!(
787 results,
788 vec![UdpSocketDiagnostics {
789 state: UdpSocketDiagnosticTuple::Bound { src_addr: None, src_port: LOCAL_PORT_1 },
790 cookie: socket.socket_cookie(),
791 marks: Marks::default(),
792 }]
793 );
794
795 results.clear();
796 api.bound_sockets_diagnostics(
797 &IpSocketMatcher::BoundInterface(BoundInterfaceMatcher::Unbound),
798 &mut results,
799 );
800 assert_eq!(results, Vec::new());
801 }
802
803 #[ip_test(I)]
804 fn diagnostics_match_cookie<I: TestIpExt>() {
805 set_logger_for_test();
806
807 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
808 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
809
810 let socket = api.create();
811 api.listen(&socket, None, Some(LOCAL_PORT_1)).expect("listen should succeed");
812
813 let mut results = Vec::new();
814 api.bound_sockets_diagnostics(
815 &IpSocketMatcher::Cookie(SocketCookieMatcher {
816 cookie: socket.socket_cookie().export_value(),
817 invert: false,
818 }),
819 &mut results,
820 );
821 assert_eq!(
822 results,
823 vec![UdpSocketDiagnostics {
824 state: UdpSocketDiagnosticTuple::Bound { src_addr: None, src_port: LOCAL_PORT_1 },
825 cookie: socket.socket_cookie(),
826 marks: Marks::default(),
827 }]
828 );
829
830 results.clear();
831 api.bound_sockets_diagnostics(
832 &IpSocketMatcher::Cookie(SocketCookieMatcher {
833 cookie: socket.socket_cookie().export_value() + 1,
834 invert: false,
835 }),
836 &mut results,
837 );
838 assert_eq!(results, Vec::new());
839 }
840
841 #[ip_test(I)]
842 #[test_case::test_case(MarkDomain::Mark1; "mark_1")]
843 #[test_case::test_case(MarkDomain::Mark2; "mark_2")]
844 fn diagnostics_match_mark<I: TestIpExt>(domain: MarkDomain) {
845 set_logger_for_test();
846
847 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
848 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
849
850 let socket = api.create();
851 api.listen(&socket, None, Some(LOCAL_PORT_1)).expect("listen should succeed");
852
853 api.set_mark(&socket, domain, Mark(Some(MARK)));
854
855 let mut results = Vec::new();
856 let matcher = |query_mark| {
857 IpSocketMatcher::Mark(netstack3_base::MarkInDomainMatcher {
858 domain,
859 matcher: MarkMatcher::Marked {
860 mask: MARK_MASK,
861 start: query_mark,
862 end: query_mark,
863 invert: false,
864 },
865 })
866 };
867 api.bound_sockets_diagnostics(&matcher(MARK), &mut results);
868 assert_eq!(
869 results,
870 vec![UdpSocketDiagnostics {
871 state: UdpSocketDiagnosticTuple::Bound { src_addr: None, src_port: LOCAL_PORT_1 },
872 cookie: socket.socket_cookie(),
873 marks: netstack3_base::MarkStorage::new([(domain, MARK)]),
874 }]
875 );
876
877 results.clear();
878 api.bound_sockets_diagnostics(&matcher(MARK + 1), &mut results);
879 assert_eq!(results, Vec::new());
880 }
881
882 #[ip_test(I)]
885 fn diagnostics_match_multiple<I: TestIpExt>() {
886 set_logger_for_test();
887
888 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
889 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
890
891 let socket_1 = api.create();
892 api.listen(&socket_1, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_1))
893 .expect("listen should succeed");
894 api.connect(
895 &socket_1,
896 Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)),
897 UdpRemotePort::Set(REMOTE_PORT_1),
898 )
899 .expect("connect should succeed");
900
901 let socket_2 = api.create();
902 api.listen(&socket_2, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_2))
903 .expect("listen should succeed");
904 api.connect(
905 &socket_2,
906 Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)),
907 UdpRemotePort::Set(REMOTE_PORT_1),
908 )
909 .expect("connect should succeed");
910
911 let socket_3 = api.create();
912 api.listen(&socket_3, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_3))
913 .expect("listen should succeed");
914 api.connect(
915 &socket_3,
916 Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)),
917 UdpRemotePort::Set(REMOTE_PORT_2),
918 )
919 .expect("connect should succeed");
920
921 let mut results = Vec::new();
922 api.bound_sockets_diagnostics(
923 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Udp(
924 UdpSocketMatcher::DstPort(BoundPortMatcher::Bound(PortMatcher {
925 range: REMOTE_PORT_1.get()..=REMOTE_PORT_1.get(),
926 invert: false,
927 })),
928 )),
929 &mut results,
930 );
931
932 results.sort_by(|a, b| a.cookie.cmp(&b.cookie));
933 let mut expected = vec![
934 UdpSocketDiagnostics {
935 state: UdpSocketDiagnosticTuple::Connected {
936 src_addr: I::TEST_ADDRS.local_ip.get(),
937 src_port: LOCAL_PORT_1,
938 dst_addr: I::TEST_ADDRS.remote_ip.get(),
939 dst_port: REMOTE_PORT_1.get(),
940 },
941 cookie: socket_1.socket_cookie(),
942 marks: Marks::default(),
943 },
944 UdpSocketDiagnostics {
945 state: UdpSocketDiagnosticTuple::Connected {
946 src_addr: I::TEST_ADDRS.local_ip.get(),
947 src_port: LOCAL_PORT_2,
948 dst_addr: I::TEST_ADDRS.remote_ip.get(),
949 dst_port: REMOTE_PORT_1.get(),
950 },
951 cookie: socket_2.socket_cookie(),
952 marks: Marks::default(),
953 },
954 ];
955 expected.sort_by(|a, b| a.cookie.cmp(&b.cookie));
956 assert_eq!(results, expected);
957
958 results.clear();
959 api.bound_sockets_diagnostics(
960 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Udp(
961 UdpSocketMatcher::DstPort(BoundPortMatcher::Bound(PortMatcher {
962 range: REMOTE_PORT_2.get()..=REMOTE_PORT_2.get(),
963 invert: false,
964 })),
965 )),
966 &mut results,
967 );
968 assert_eq!(
969 results,
970 vec![UdpSocketDiagnostics {
971 state: UdpSocketDiagnosticTuple::Connected {
972 src_addr: I::TEST_ADDRS.local_ip.get(),
973 src_port: LOCAL_PORT_3,
974 dst_addr: I::TEST_ADDRS.remote_ip.get(),
975 dst_port: REMOTE_PORT_2.get(),
976 },
977 cookie: socket_3.socket_cookie(),
978 marks: Marks::default(),
979 }]
980 );
981 }
982}