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