1use core::num::NonZeroU16;
6
7use net_types::Witness as _;
8use net_types::ip::Ip;
9use netstack3_base::socket::SocketCookie;
10use netstack3_base::{
11 Marks, Matcher, MaybeSocketTransportProperties, SocketDiagnosticsSeed,
12 SocketTransportProtocolMatcher, UdpSocketProperties, UdpSocketState as UdpSocketMatcherState,
13 WeakDeviceIdentifier,
14};
15use netstack3_datagram::{
16 ConnInfo, DatagramSocketDiagnosticsSpec, IpExt, ListenerInfo, SocketInfo,
17 SocketState as DatagramSocketState,
18};
19
20use crate::internal::base::{Udp, UdpBindingsTypes, UdpSocketId, UdpSocketState};
21
22#[derive(Debug)]
27#[cfg_attr(any(test, feature = "testutils"), derive(PartialEq, Eq))]
28#[allow(missing_docs)]
29pub struct UdpSocketDiagnostics<I: Ip> {
30 pub state: UdpSocketDiagnosticTuple<I>,
31 pub cookie: SocketCookie,
32 pub marks: Marks,
33}
34
35impl<I: IpExt> UdpSocketDiagnostics<I> {
36 pub(crate) fn from_parts<D: WeakDeviceIdentifier, BT: UdpBindingsTypes>(
37 socket_state: &UdpSocketState<I, D, BT>,
38 cookie: SocketCookie,
39 ) -> Option<Self> {
40 let state = UdpSocketDiagnosticTuple::from_socket_info(socket_state.to_socket_info())?;
41 let marks = *socket_state.options().marks();
42
43 Some(Self { state, cookie, marks })
44 }
45}
46
47pub struct UdpSocketDiagnosticsSeed<I, D, BT>
50where
51 I: IpExt,
52 D: WeakDeviceIdentifier,
53 BT: UdpBindingsTypes,
54{
55 pub(crate) state: UdpSocketState<I, D, BT>,
56 pub(crate) cookie: SocketCookie,
57}
58
59impl<I, D, BT> SocketDiagnosticsSeed for UdpSocketDiagnosticsSeed<I, D, BT>
60where
61 I: IpExt,
62 D: WeakDeviceIdentifier,
63 BT: UdpBindingsTypes,
64{
65 type Output = UdpSocketDiagnostics<I>;
66
67 fn resolve(self) -> Option<UdpSocketDiagnostics<I>> {
68 let Self { state, cookie } = self;
69 UdpSocketDiagnostics::from_parts(&state, cookie)
70 }
71}
72
73#[derive(Debug)]
75#[cfg_attr(any(test, feature = "testutils"), derive(PartialEq, Eq))]
76#[allow(missing_docs)]
77pub enum UdpSocketDiagnosticTuple<I: Ip> {
78 Bound { src_addr: Option<I::Addr>, src_port: NonZeroU16 },
79 Connected { src_addr: I::Addr, src_port: NonZeroU16, dst_addr: I::Addr, dst_port: u16 },
80}
81
82impl<I: Ip> UdpSocketDiagnosticTuple<I> {
83 fn from_socket_info<D: WeakDeviceIdentifier>(info: SocketInfo<I::Addr, D>) -> Option<Self> {
84 match info {
85 SocketInfo::Unbound => None,
88 SocketInfo::Listener(ListenerInfo { local_ip, local_identifier }) => {
89 Some(Self::Bound {
90 src_addr: local_ip.map(|ip| ip.into_inner().addr().get()),
91 src_port: local_identifier,
92 })
93 }
94 SocketInfo::Connected(ConnInfo {
95 local_ip,
96 local_identifier,
97 remote_ip,
98 remote_identifier,
99 }) => Some(Self::Connected {
100 src_addr: local_ip.into_inner().addr().get(),
101 src_port: local_identifier,
102 dst_addr: remote_ip.into_inner().addr().get(),
103 dst_port: remote_identifier,
104 }),
105 }
106 }
107
108 pub fn src_addr(&self) -> Option<I::Addr> {
110 match self {
111 Self::Bound { src_addr, src_port: _ } => *src_addr,
112 Self::Connected { src_addr, src_port: _, dst_addr: _, dst_port: _ } => Some(*src_addr),
113 }
114 }
115
116 pub fn src_port(&self) -> Option<NonZeroU16> {
118 match self {
119 Self::Bound { src_addr: _, src_port }
120 | Self::Connected { src_addr: _, src_port, dst_addr: _, dst_port: _ } => {
121 Some(*src_port)
122 }
123 }
124 }
125
126 pub fn dst_addr(&self) -> Option<I::Addr> {
128 match self {
129 Self::Bound { src_addr: _, src_port: _ } => None,
130 Self::Connected { src_addr: _, src_port: _, dst_addr, dst_port: _ } => Some(*dst_addr),
131 }
132 }
133
134 pub fn dst_port(&self) -> Option<u16> {
136 match self {
137 Self::Bound { src_addr: _, src_port: _ } => None,
138 Self::Connected { src_addr: _, src_port: _, dst_addr: _, dst_port } => Some(*dst_port),
139 }
140 }
141}
142
143pub struct UdpTransportProtocolDiagnosticsProperties<'a, I, D, BT>(&'a UdpSocketState<I, D, BT>)
146where
147 I: IpExt,
148 D: WeakDeviceIdentifier,
149 BT: UdpBindingsTypes;
150
151impl<I, D, BT> MaybeSocketTransportProperties
152 for UdpTransportProtocolDiagnosticsProperties<'_, I, D, BT>
153where
154 I: IpExt,
155 D: WeakDeviceIdentifier,
156 BT: UdpBindingsTypes,
157{
158 type TcpProps<'a>
159 = !
160 where
161 Self: 'a;
162
163 type UdpProps<'a>
164 = Self
165 where
166 Self: 'a;
167
168 fn tcp_socket_properties(&self) -> Option<&Self::TcpProps<'_>> {
169 None
170 }
171
172 fn udp_socket_properties(&self) -> Option<&Self::UdpProps<'_>> {
173 Some(self)
174 }
175}
176
177impl<I, D, BT> UdpSocketProperties for UdpTransportProtocolDiagnosticsProperties<'_, I, D, BT>
178where
179 I: IpExt,
180 D: WeakDeviceIdentifier,
181 BT: UdpBindingsTypes,
182{
183 fn src_port_matches(&self, matcher: &netstack3_base::BoundPortMatcher) -> bool {
184 let Self(udp_state) = self;
185 matcher.matches(&udp_state.local_identifier().map(|p| p.get()))
186 }
187
188 fn dst_port_matches(&self, matcher: &netstack3_base::BoundPortMatcher) -> bool {
189 let Self(udp_state) = self;
190 matcher.matches(&udp_state.remote_identifier())
191 }
192
193 fn state_matches(&self, matcher: &netstack3_base::UdpStateMatcher) -> bool {
194 let Self(udp_state) = self;
195 matcher.matches(match udp_state.to_socket_info() {
196 SocketInfo::Unbound => return false,
197 SocketInfo::Listener(_) => &UdpSocketMatcherState::Bound,
198 SocketInfo::Connected(_) => &UdpSocketMatcherState::Connected,
199 })
200 }
201}
202
203impl<BT: UdpBindingsTypes> DatagramSocketDiagnosticsSpec for Udp<BT> {
204 type DeviceClass = BT::DeviceClass;
205
206 fn transport_protocol_matches<I: IpExt, D: WeakDeviceIdentifier>(
207 state: &DatagramSocketState<I, D, Udp<BT>>,
208 matcher: &SocketTransportProtocolMatcher,
209 ) -> bool {
210 matcher.matches(&UdpTransportProtocolDiagnosticsProperties(state))
211 }
212
213 fn cookie_matches<I: IpExt, D: WeakDeviceIdentifier>(
214 id: &UdpSocketId<I, D, BT>,
215 matcher: &netstack3_base::SocketCookieMatcher,
216 ) -> bool {
217 matcher.matches(&id.socket_cookie().export_value())
218 }
219}
220
221#[cfg(test)]
222mod tests {
223 use alloc::string::ToString;
224 use alloc::vec;
225 use alloc::vec::Vec;
226 use assert_matches::assert_matches;
227 use core::num::NonZeroU16;
228
229 use ip_test_macro::ip_test;
230 use net_types::ZonedAddr;
231 use net_types::ip::Subnet;
232 use netstack3_base::testutil::{FakeDeviceId, FakeSendToken, set_logger_for_test};
233 use netstack3_base::{
234 AddressMatcher, AddressMatcherEither, AddressMatcherType, BoundAddressMatcherEither,
235 BoundInterfaceMatcher, BoundPortMatcher, InterfaceMatcher, IpSocketMatcher, Mark,
236 MarkDomain, MarkMatcher, PortMatcher, SocketCookieMatcher, SocketTransportProtocolMatcher,
237 SubnetMatcher, TcpSocketMatcher, UdpSocketMatcher, UdpStateMatcher,
238 };
239
240 use crate::internal::base::testutils::{FakeUdpCoreCtx, TestIpExt, UdpFakeDeviceCtx};
241 use crate::internal::base::{UdpApi, UdpRemotePort};
242 use netstack3_datagram::{ConnInfo, ListenerInfo};
243
244 use super::*;
245
246 const LOCAL_PORT_1: NonZeroU16 = NonZeroU16::new(1234).unwrap();
247 const LOCAL_PORT_2: NonZeroU16 = NonZeroU16::new(5678).unwrap();
248 const LOCAL_PORT_3: NonZeroU16 = NonZeroU16::new(4321).unwrap();
249
250 const REMOTE_PORT_1: NonZeroU16 = NonZeroU16::new(100).unwrap();
251 const REMOTE_PORT_2: NonZeroU16 = NonZeroU16::new(200).unwrap();
252
253 const MARK: u32 = 0x10;
254 const MARK_MASK: u32 = !0;
255
256 #[ip_test(I)]
257 fn diagnostics_match_ip_version<I: TestIpExt>() {
258 set_logger_for_test();
259
260 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
261 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
262
263 let socket = api.create();
264 api.listen(&socket, None, Some(LOCAL_PORT_1)).expect("listen should succeed");
265
266 let mut results = Vec::new();
267 api.bound_sockets_diagnostics(&IpSocketMatcher::Family(I::VERSION), &mut results);
268 assert_eq!(
269 results,
270 vec![UdpSocketDiagnostics {
271 state: UdpSocketDiagnosticTuple::Bound { src_addr: None, src_port: LOCAL_PORT_1 },
272 cookie: socket.socket_cookie(),
273 marks: Marks::default(),
274 }]
275 );
276
277 results.clear();
278 api.bound_sockets_diagnostics(
279 &IpSocketMatcher::Family(
280 <<I as netstack3_base::socket::DualStackIpExt>::OtherVersion as Ip>::VERSION,
281 ),
282 &mut results,
283 );
284 assert_eq!(results, Vec::new());
285 }
286
287 #[ip_test(I)]
288 fn diagnostics_match_src_addr<I: TestIpExt>() {
289 set_logger_for_test();
290
291 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
292 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
293
294 let socket = api.create();
295 api.listen(&socket, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_1))
296 .expect("listen should succeed");
297
298 let mut results = Vec::new();
299 let matcher = I::map_ip_in(
300 I::TEST_ADDRS.subnet,
301 |subnet| {
302 BoundAddressMatcherEither::Bound(AddressMatcherEither::V4(AddressMatcher {
303 matcher: AddressMatcherType::Subnet(SubnetMatcher(subnet)),
304 invert: false,
305 }))
306 },
307 |subnet| {
308 BoundAddressMatcherEither::Bound(AddressMatcherEither::V6(AddressMatcher {
309 matcher: AddressMatcherType::Subnet(SubnetMatcher(subnet)),
310 invert: false,
311 }))
312 },
313 );
314 api.bound_sockets_diagnostics(&IpSocketMatcher::SrcAddr(matcher), &mut results);
315 assert_eq!(
316 results,
317 vec![UdpSocketDiagnostics {
318 state: UdpSocketDiagnosticTuple::Bound {
319 src_addr: Some(I::TEST_ADDRS.local_ip.get()),
320 src_port: LOCAL_PORT_1
321 },
322 cookie: socket.socket_cookie(),
323 marks: Marks::default(),
324 }]
325 );
326
327 results.clear();
328 let matcher = I::map_ip_in(
329 I::TEST_ADDRS.remote_ip.get(),
330 |addr| {
331 BoundAddressMatcherEither::Bound(AddressMatcherEither::V4(AddressMatcher {
332 matcher: AddressMatcherType::Subnet(SubnetMatcher(
333 Subnet::new(addr, 32).unwrap(),
334 )),
335 invert: false,
336 }))
337 },
338 |addr| {
339 BoundAddressMatcherEither::Bound(AddressMatcherEither::V6(AddressMatcher {
340 matcher: AddressMatcherType::Subnet(SubnetMatcher(
341 Subnet::new(addr, 128).unwrap(),
342 )),
343 invert: false,
344 }))
345 },
346 );
347 api.bound_sockets_diagnostics(&IpSocketMatcher::SrcAddr(matcher), &mut results);
348 assert_eq!(results, Vec::new());
349 }
350
351 #[ip_test(I)]
352 fn diagnostics_match_src_addr_unbound<I: TestIpExt>() {
353 set_logger_for_test();
354
355 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
356 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
357
358 let socket1 = api.create();
359 api.listen(&socket1, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_1))
360 .expect("listen should succeed");
361
362 let socket2 = api.create();
363 api.listen(&socket2, None, Some(LOCAL_PORT_2)).expect("listen should succeed");
364
365 let mut results = Vec::new();
366 let matcher = match I::VERSION {
367 net_types::ip::IpVersion::V4 => BoundAddressMatcherEither::Unbound,
368 net_types::ip::IpVersion::V6 => BoundAddressMatcherEither::Unbound,
369 };
370 api.bound_sockets_diagnostics(&IpSocketMatcher::SrcAddr(matcher), &mut results);
371 assert_eq!(
372 results,
373 vec![UdpSocketDiagnostics {
374 state: UdpSocketDiagnosticTuple::Bound { src_addr: None, src_port: LOCAL_PORT_2 },
375 cookie: socket2.socket_cookie(),
376 marks: Marks::default(),
377 }]
378 );
379
380 results.clear();
381 let matcher = I::map_ip_in(
382 I::TEST_ADDRS.subnet,
383 |subnet| {
384 BoundAddressMatcherEither::Bound(AddressMatcherEither::V4(AddressMatcher {
385 matcher: AddressMatcherType::Subnet(SubnetMatcher(subnet)),
386 invert: false,
387 }))
388 },
389 |subnet| {
390 BoundAddressMatcherEither::Bound(AddressMatcherEither::V6(AddressMatcher {
391 matcher: AddressMatcherType::Subnet(SubnetMatcher(subnet)),
392 invert: false,
393 }))
394 },
395 );
396 api.bound_sockets_diagnostics(&IpSocketMatcher::SrcAddr(matcher), &mut results);
397 assert_eq!(
398 results,
399 vec![UdpSocketDiagnostics {
400 state: UdpSocketDiagnosticTuple::Bound {
401 src_addr: Some(I::TEST_ADDRS.local_ip.get()),
402 src_port: LOCAL_PORT_1
403 },
404 cookie: socket1.socket_cookie(),
405 marks: Marks::default(),
406 }]
407 );
408 }
409
410 #[ip_test(I)]
411 fn diagnostics_match_dst_addr<I: TestIpExt>() {
412 set_logger_for_test();
413
414 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
415 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
416
417 let socket = api.create();
418 api.listen(&socket, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_2))
419 .expect("listen should succeed");
420 api.connect(
421 &socket,
422 Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)),
423 UdpRemotePort::Set(LOCAL_PORT_1),
424 )
425 .expect("connect should succeed");
426
427 let mut results = Vec::new();
428 let matcher = I::map_ip_in(
429 I::TEST_ADDRS.subnet,
430 |subnet| {
431 BoundAddressMatcherEither::Bound(AddressMatcherEither::V4(AddressMatcher {
432 matcher: AddressMatcherType::Subnet(SubnetMatcher(subnet)),
433 invert: false,
434 }))
435 },
436 |subnet| {
437 BoundAddressMatcherEither::Bound(AddressMatcherEither::V6(AddressMatcher {
438 matcher: AddressMatcherType::Subnet(SubnetMatcher(subnet)),
439 invert: false,
440 }))
441 },
442 );
443 api.bound_sockets_diagnostics(&IpSocketMatcher::DstAddr(matcher), &mut results);
444 assert_eq!(
445 results,
446 vec![UdpSocketDiagnostics {
447 state: UdpSocketDiagnosticTuple::Connected {
448 src_addr: I::TEST_ADDRS.local_ip.get(),
449 src_port: LOCAL_PORT_2,
450 dst_addr: I::TEST_ADDRS.remote_ip.get(),
451 dst_port: LOCAL_PORT_1.get(),
452 },
453 cookie: socket.socket_cookie(),
454 marks: Marks::default(),
455 }]
456 );
457
458 results.clear();
459 let matcher = I::map_ip_in(
460 I::TEST_ADDRS.local_ip.get(),
461 |addr| {
462 BoundAddressMatcherEither::Bound(AddressMatcherEither::V4(AddressMatcher {
463 matcher: AddressMatcherType::Subnet(SubnetMatcher(
464 Subnet::new(addr, 32).unwrap(),
465 )),
466 invert: false,
467 }))
468 },
469 |addr| {
470 BoundAddressMatcherEither::Bound(AddressMatcherEither::V6(AddressMatcher {
471 matcher: AddressMatcherType::Subnet(SubnetMatcher(
472 Subnet::new(addr, 128).unwrap(),
473 )),
474 invert: false,
475 }))
476 },
477 );
478 api.bound_sockets_diagnostics(&IpSocketMatcher::DstAddr(matcher), &mut results);
479 assert_eq!(results, Vec::new());
480 }
481
482 #[ip_test(I)]
483 fn diagnostics_match_dst_addr_unbound<I: TestIpExt>() {
484 set_logger_for_test();
485
486 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
487 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
488
489 let socket1 = api.create();
490 api.listen(&socket1, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_1))
491 .expect("listen should succeed");
492 api.connect(
493 &socket1,
494 Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)),
495 UdpRemotePort::Set(REMOTE_PORT_1),
496 )
497 .expect("connect should succeed");
498
499 let socket2 = api.create();
500 api.listen(&socket2, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_2))
501 .expect("listen should succeed");
502
503 let mut results = Vec::new();
504 let matcher = match I::VERSION {
505 net_types::ip::IpVersion::V4 => BoundAddressMatcherEither::Unbound,
506 net_types::ip::IpVersion::V6 => BoundAddressMatcherEither::Unbound,
507 };
508 api.bound_sockets_diagnostics(&IpSocketMatcher::DstAddr(matcher), &mut results);
509 assert_eq!(
510 results,
511 vec![UdpSocketDiagnostics {
512 state: UdpSocketDiagnosticTuple::Bound {
513 src_addr: Some(I::TEST_ADDRS.local_ip.get()),
514 src_port: LOCAL_PORT_2
515 },
516 cookie: socket2.socket_cookie(),
517 marks: Marks::default(),
518 }]
519 );
520
521 results.clear();
522 let matcher = I::map_ip_in(
523 I::TEST_ADDRS.remote_ip.get(),
524 |addr| {
525 BoundAddressMatcherEither::Bound(AddressMatcherEither::V4(AddressMatcher {
526 matcher: AddressMatcherType::Subnet(SubnetMatcher(
527 Subnet::new(addr, 32).unwrap(),
528 )),
529 invert: false,
530 }))
531 },
532 |addr| {
533 BoundAddressMatcherEither::Bound(AddressMatcherEither::V6(AddressMatcher {
534 matcher: AddressMatcherType::Subnet(SubnetMatcher(
535 Subnet::new(addr, 128).unwrap(),
536 )),
537 invert: false,
538 }))
539 },
540 );
541 api.bound_sockets_diagnostics(&IpSocketMatcher::DstAddr(matcher), &mut results);
542 assert_eq!(
543 results,
544 vec![UdpSocketDiagnostics {
545 state: UdpSocketDiagnosticTuple::Connected {
546 src_addr: I::TEST_ADDRS.local_ip.get(),
547 src_port: LOCAL_PORT_1,
548 dst_addr: I::TEST_ADDRS.remote_ip.get(),
549 dst_port: REMOTE_PORT_1.get(),
550 },
551 cookie: socket1.socket_cookie(),
552 marks: Marks::default(),
553 }]
554 );
555 }
556
557 #[ip_test(I)]
558 fn diagnostics_match_proto<I: TestIpExt>() {
559 set_logger_for_test();
560
561 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
562 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
563
564 let socket = api.create();
565 api.listen(&socket, None, Some(LOCAL_PORT_1)).expect("listen should succeed");
566
567 let mut results = Vec::new();
568 api.bound_sockets_diagnostics(
569 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Udp(UdpSocketMatcher::Empty)),
570 &mut results,
571 );
572 assert_eq!(
573 results,
574 vec![UdpSocketDiagnostics {
575 state: UdpSocketDiagnosticTuple::Bound { src_addr: None, src_port: LOCAL_PORT_1 },
576 cookie: socket.socket_cookie(),
577 marks: Marks::default(),
578 }]
579 );
580
581 results.clear();
582 api.bound_sockets_diagnostics(
583 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Tcp(TcpSocketMatcher::Empty)),
584 &mut results,
585 );
586 assert_eq!(results, Vec::new());
587 }
588
589 #[ip_test(I)]
590 fn diagnostics_match_src_port<I: TestIpExt>() {
591 set_logger_for_test();
592
593 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
594 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
595
596 let socket = api.create();
597 api.listen(&socket, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_1))
598 .expect("listen should succeed");
599
600 let mut results = Vec::new();
601 api.bound_sockets_diagnostics(
602 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Udp(
603 UdpSocketMatcher::SrcPort(BoundPortMatcher::Bound(PortMatcher {
604 range: LOCAL_PORT_1.get()..=LOCAL_PORT_1.get(),
605 invert: false,
606 })),
607 )),
608 &mut results,
609 );
610 assert_eq!(
611 results,
612 vec![UdpSocketDiagnostics {
613 state: UdpSocketDiagnosticTuple::Bound {
614 src_addr: Some(I::TEST_ADDRS.local_ip.get()),
615 src_port: LOCAL_PORT_1
616 },
617 cookie: socket.socket_cookie(),
618 marks: Marks::default(),
619 }]
620 );
621
622 results.clear();
623 api.bound_sockets_diagnostics(
624 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Udp(
625 UdpSocketMatcher::SrcPort(BoundPortMatcher::Bound(PortMatcher {
626 range: (LOCAL_PORT_1.get() + 1)..=(LOCAL_PORT_1.get() + 1),
627 invert: false,
628 })),
629 )),
630 &mut results,
631 );
632 assert_eq!(results, Vec::new());
633 }
634
635 #[ip_test(I)]
636 fn diagnostics_match_dst_port<I: TestIpExt>() {
637 set_logger_for_test();
638
639 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
640 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
641
642 let socket = api.create();
643 api.listen(&socket, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_2))
644 .expect("listen should succeed");
645 api.connect(
646 &socket,
647 Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)),
648 UdpRemotePort::Set(LOCAL_PORT_1),
649 )
650 .expect("connect should succeed");
651
652 let mut results = Vec::new();
653 api.bound_sockets_diagnostics(
654 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Udp(
655 UdpSocketMatcher::DstPort(BoundPortMatcher::Bound(PortMatcher {
656 range: LOCAL_PORT_1.get()..=LOCAL_PORT_1.get(),
657 invert: false,
658 })),
659 )),
660 &mut results,
661 );
662 assert_eq!(
663 results,
664 vec![UdpSocketDiagnostics {
665 state: UdpSocketDiagnosticTuple::Connected {
666 src_addr: I::TEST_ADDRS.local_ip.get(),
667 src_port: LOCAL_PORT_2,
668 dst_addr: I::TEST_ADDRS.remote_ip.get(),
669 dst_port: LOCAL_PORT_1.get(),
670 },
671 cookie: socket.socket_cookie(),
672 marks: Marks::default(),
673 }]
674 );
675
676 results.clear();
677 api.bound_sockets_diagnostics(
678 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Udp(
679 UdpSocketMatcher::DstPort(BoundPortMatcher::Bound(PortMatcher {
680 range: (LOCAL_PORT_1.get() + 1)..=(LOCAL_PORT_1.get() + 1),
681 invert: false,
682 })),
683 )),
684 &mut results,
685 );
686 assert_eq!(results, Vec::new());
687 }
688
689 #[ip_test(I)]
690 fn diagnostics_match_src_port_unbound<I: TestIpExt>() {
691 set_logger_for_test();
692
693 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
694 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
695
696 let socket = api.create();
697 api.listen(&socket, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_1))
698 .expect("listen should succeed");
699
700 let mut results = Vec::new();
701 api.bound_sockets_diagnostics(
703 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Udp(
704 UdpSocketMatcher::SrcPort(BoundPortMatcher::Unbound),
705 )),
706 &mut results,
707 );
708 assert_eq!(results, Vec::new());
709 }
710
711 #[ip_test(I)]
712 fn diagnostics_match_dst_port_unbound<I: TestIpExt>() {
713 set_logger_for_test();
714
715 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
716 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
717
718 let socket1 = api.create();
720 api.listen(&socket1, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_1))
721 .expect("listen should succeed");
722
723 let socket2 = api.create();
725 api.listen(&socket2, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_2))
726 .expect("listen should succeed");
727 api.connect(
728 &socket2,
729 Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)),
730 UdpRemotePort::Set(REMOTE_PORT_2),
731 )
732 .expect("connect should succeed");
733
734 let mut results = Vec::new();
735 api.bound_sockets_diagnostics(
736 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Udp(
737 UdpSocketMatcher::DstPort(BoundPortMatcher::Unbound),
738 )),
739 &mut results,
740 );
741 assert_eq!(
742 results,
743 vec![UdpSocketDiagnostics {
744 state: UdpSocketDiagnosticTuple::Bound {
745 src_addr: Some(I::TEST_ADDRS.local_ip.get()),
746 src_port: LOCAL_PORT_1
747 },
748 cookie: socket1.socket_cookie(),
749 marks: Marks::default(),
750 }]
751 );
752
753 results.clear();
754 api.bound_sockets_diagnostics(
755 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Udp(
756 UdpSocketMatcher::DstPort(BoundPortMatcher::Unbound),
757 )),
758 &mut results,
759 );
760 assert_eq!(
761 results,
762 vec![UdpSocketDiagnostics {
763 state: UdpSocketDiagnosticTuple::Bound {
764 src_addr: Some(I::TEST_ADDRS.local_ip.get()),
765 src_port: LOCAL_PORT_1,
766 },
767 cookie: socket1.socket_cookie(),
768 marks: Marks::default(),
769 }]
770 );
771 }
772
773 #[ip_test(I)]
774 fn diagnostics_match_state<I: TestIpExt>() {
775 set_logger_for_test();
776
777 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
778 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
779
780 let socket_1 = api.create();
781 api.listen(&socket_1, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_1))
782 .expect("listen should succeed");
783
784 let socket_2 = api.create();
785 api.listen(&socket_2, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_2))
786 .expect("listen should succeed");
787 api.connect(
788 &socket_2,
789 Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)),
790 UdpRemotePort::Set(LOCAL_PORT_3),
791 )
792 .expect("connect should succeed");
793
794 let mut results = Vec::new();
795 api.bound_sockets_diagnostics(
796 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Udp(UdpSocketMatcher::State(
797 UdpStateMatcher::BOUND,
798 ))),
799 &mut results,
800 );
801 assert_eq!(
802 results,
803 vec![UdpSocketDiagnostics {
804 state: UdpSocketDiagnosticTuple::Bound {
805 src_addr: Some(I::TEST_ADDRS.local_ip.get()),
806 src_port: LOCAL_PORT_1
807 },
808 cookie: socket_1.socket_cookie(),
809 marks: Marks::default(),
810 }]
811 );
812
813 results.clear();
814 api.bound_sockets_diagnostics(
815 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Udp(UdpSocketMatcher::State(
816 UdpStateMatcher::CONNECTED,
817 ))),
818 &mut results,
819 );
820 assert_eq!(
821 results,
822 vec![UdpSocketDiagnostics {
823 state: UdpSocketDiagnosticTuple::Connected {
824 src_addr: I::TEST_ADDRS.local_ip.get(),
825 src_port: LOCAL_PORT_2,
826 dst_addr: I::TEST_ADDRS.remote_ip.get(),
827 dst_port: LOCAL_PORT_3.get(),
828 },
829 cookie: socket_2.socket_cookie(),
830 marks: Marks::default(),
831 }]
832 );
833 }
834
835 #[ip_test(I)]
836 fn diagnostics_match_device<I: TestIpExt>() {
837 set_logger_for_test();
838
839 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
840 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
841
842 let socket = api.create();
843 api.set_device(&socket, Some(&FakeDeviceId)).expect("set device should succeed");
844 api.listen(&socket, None, Some(LOCAL_PORT_1)).expect("listen should succeed");
845
846 let mut results = Vec::new();
847 api.bound_sockets_diagnostics(
848 &IpSocketMatcher::BoundInterface(BoundInterfaceMatcher::Bound(InterfaceMatcher::Name(
849 FakeDeviceId::FAKE_NAME.to_string(),
850 ))),
851 &mut results,
852 );
853 assert_eq!(
854 results,
855 vec![UdpSocketDiagnostics {
856 state: UdpSocketDiagnosticTuple::Bound { src_addr: None, src_port: LOCAL_PORT_1 },
857 cookie: socket.socket_cookie(),
858 marks: Marks::default(),
859 }]
860 );
861
862 results.clear();
863 api.bound_sockets_diagnostics(
864 &IpSocketMatcher::BoundInterface(BoundInterfaceMatcher::Unbound),
865 &mut results,
866 );
867 assert_eq!(results, Vec::new());
868 }
869
870 #[ip_test(I)]
871 fn diagnostics_match_cookie<I: TestIpExt>() {
872 set_logger_for_test();
873
874 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
875 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
876
877 let socket = api.create();
878 api.listen(&socket, None, Some(LOCAL_PORT_1)).expect("listen should succeed");
879
880 let mut results = Vec::new();
881 api.bound_sockets_diagnostics(
882 &IpSocketMatcher::Cookie(SocketCookieMatcher {
883 cookie: socket.socket_cookie().export_value(),
884 invert: false,
885 }),
886 &mut results,
887 );
888 assert_eq!(
889 results,
890 vec![UdpSocketDiagnostics {
891 state: UdpSocketDiagnosticTuple::Bound { src_addr: None, src_port: LOCAL_PORT_1 },
892 cookie: socket.socket_cookie(),
893 marks: Marks::default(),
894 }]
895 );
896
897 results.clear();
898 api.bound_sockets_diagnostics(
899 &IpSocketMatcher::Cookie(SocketCookieMatcher {
900 cookie: socket.socket_cookie().export_value() + 1,
901 invert: false,
902 }),
903 &mut results,
904 );
905 assert_eq!(results, Vec::new());
906 }
907
908 #[ip_test(I, test = false)]
909 #[test_case::test_case(MarkDomain::Mark1; "mark_1")]
910 #[test_case::test_case(MarkDomain::Mark2; "mark_2")]
911 fn diagnostics_match_mark<I: TestIpExt>(domain: MarkDomain) {
912 set_logger_for_test();
913
914 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
915 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
916
917 let socket = api.create();
918 api.listen(&socket, None, Some(LOCAL_PORT_1)).expect("listen should succeed");
919
920 api.set_mark(&socket, domain, Mark(Some(MARK)));
921
922 let mut results = Vec::new();
923 let matcher = |query_mark| {
924 IpSocketMatcher::Mark(netstack3_base::MarkInDomainMatcher {
925 domain,
926 matcher: MarkMatcher::Marked {
927 mask: MARK_MASK,
928 start: query_mark,
929 end: query_mark,
930 invert: false,
931 },
932 })
933 };
934 api.bound_sockets_diagnostics(&matcher(MARK), &mut results);
935 assert_eq!(
936 results,
937 vec![UdpSocketDiagnostics {
938 state: UdpSocketDiagnosticTuple::Bound { src_addr: None, src_port: LOCAL_PORT_1 },
939 cookie: socket.socket_cookie(),
940 marks: netstack3_base::MarkStorage::new([(domain, MARK)]),
941 }]
942 );
943
944 results.clear();
945 api.bound_sockets_diagnostics(&matcher(MARK + 1), &mut results);
946 assert_eq!(results, Vec::new());
947 }
948
949 #[ip_test(I)]
952 fn diagnostics_match_multiple<I: TestIpExt>() {
953 set_logger_for_test();
954
955 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
956 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
957
958 let socket_1 = api.create();
959 api.listen(&socket_1, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_1))
960 .expect("listen should succeed");
961 api.connect(
962 &socket_1,
963 Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)),
964 UdpRemotePort::Set(REMOTE_PORT_1),
965 )
966 .expect("connect should succeed");
967
968 let socket_2 = api.create();
969 api.listen(&socket_2, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_2))
970 .expect("listen should succeed");
971 api.connect(
972 &socket_2,
973 Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)),
974 UdpRemotePort::Set(REMOTE_PORT_1),
975 )
976 .expect("connect should succeed");
977
978 let socket_3 = api.create();
979 api.listen(&socket_3, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_3))
980 .expect("listen should succeed");
981 api.connect(
982 &socket_3,
983 Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)),
984 UdpRemotePort::Set(REMOTE_PORT_2),
985 )
986 .expect("connect should succeed");
987
988 let mut results = Vec::new();
989 api.bound_sockets_diagnostics(
990 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Udp(
991 UdpSocketMatcher::DstPort(BoundPortMatcher::Bound(PortMatcher {
992 range: REMOTE_PORT_1.get()..=REMOTE_PORT_1.get(),
993 invert: false,
994 })),
995 )),
996 &mut results,
997 );
998
999 results.sort_by(|a, b| a.cookie.cmp(&b.cookie));
1000 let mut expected = vec![
1001 UdpSocketDiagnostics {
1002 state: UdpSocketDiagnosticTuple::Connected {
1003 src_addr: I::TEST_ADDRS.local_ip.get(),
1004 src_port: LOCAL_PORT_1,
1005 dst_addr: I::TEST_ADDRS.remote_ip.get(),
1006 dst_port: REMOTE_PORT_1.get(),
1007 },
1008 cookie: socket_1.socket_cookie(),
1009 marks: Marks::default(),
1010 },
1011 UdpSocketDiagnostics {
1012 state: UdpSocketDiagnosticTuple::Connected {
1013 src_addr: I::TEST_ADDRS.local_ip.get(),
1014 src_port: LOCAL_PORT_2,
1015 dst_addr: I::TEST_ADDRS.remote_ip.get(),
1016 dst_port: REMOTE_PORT_1.get(),
1017 },
1018 cookie: socket_2.socket_cookie(),
1019 marks: Marks::default(),
1020 },
1021 ];
1022 expected.sort_by(|a, b| a.cookie.cmp(&b.cookie));
1023 assert_eq!(results, expected);
1024
1025 results.clear();
1026 api.bound_sockets_diagnostics(
1027 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Udp(
1028 UdpSocketMatcher::DstPort(BoundPortMatcher::Bound(PortMatcher {
1029 range: REMOTE_PORT_2.get()..=REMOTE_PORT_2.get(),
1030 invert: false,
1031 })),
1032 )),
1033 &mut results,
1034 );
1035 assert_eq!(
1036 results,
1037 vec![UdpSocketDiagnostics {
1038 state: UdpSocketDiagnosticTuple::Connected {
1039 src_addr: I::TEST_ADDRS.local_ip.get(),
1040 src_port: LOCAL_PORT_3,
1041 dst_addr: I::TEST_ADDRS.remote_ip.get(),
1042 dst_port: REMOTE_PORT_2.get(),
1043 },
1044 cookie: socket_3.socket_cookie(),
1045 marks: Marks::default(),
1046 }]
1047 );
1048 }
1049
1050 #[ip_test(I)]
1051 fn disconnect_listener<I: TestIpExt>() {
1052 set_logger_for_test();
1053
1054 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::new_fake_device::<I>());
1055 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
1056 let socket = api.create();
1057 api.listen(&socket, None, Some(LOCAL_PORT_1)).unwrap();
1058 assert_matches!(api.get_info(&socket), SocketInfo::Listener(ListenerInfo { .. }));
1059
1060 let count = api.disconnect_bound(&IpSocketMatcher::Cookie(SocketCookieMatcher {
1061 cookie: socket.socket_cookie().export_value(),
1062 invert: false,
1063 }));
1064 assert_eq!(count, 1);
1065 assert_matches!(api.get_info(&socket), SocketInfo::Unbound);
1066 }
1067
1068 #[ip_test(I)]
1069 fn disconnect_implicitly_bound<I: TestIpExt>() {
1070 set_logger_for_test();
1071
1072 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::with_local_remote_ip_addrs(
1073 vec![I::TEST_ADDRS.local_ip],
1074 vec![I::TEST_ADDRS.remote_ip],
1075 ));
1076 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
1077 let socket = api.create();
1078
1079 api.send_to(
1080 &socket,
1081 Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)),
1082 REMOTE_PORT_1.into(),
1083 packet::Buf::new(vec![], ..),
1084 FakeSendToken::default(),
1085 )
1086 .expect("send failed");
1087 assert_matches!(api.get_info(&socket), SocketInfo::Listener(ListenerInfo { .. }));
1088
1089 let count = api.disconnect_bound(&IpSocketMatcher::Cookie(SocketCookieMatcher {
1090 cookie: socket.socket_cookie().export_value(),
1091 invert: false,
1092 }));
1093 assert_eq!(count, 1);
1094 assert_matches!(api.get_info(&socket), SocketInfo::Unbound);
1095 }
1096
1097 #[ip_test(I)]
1098 fn disconnect_connected_and_reuse<I: TestIpExt>() {
1099 set_logger_for_test();
1100
1101 let mut ctx = UdpFakeDeviceCtx::with_core_ctx(FakeUdpCoreCtx::with_local_remote_ip_addrs(
1102 vec![I::TEST_ADDRS.local_ip],
1103 vec![I::TEST_ADDRS.remote_ip],
1104 ));
1105 let mut api = UdpApi::<I, _>::new(ctx.as_mut());
1106 let socket = api.create();
1107
1108 api.listen(&socket, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_1))
1109 .unwrap();
1110 api.connect(
1111 &socket,
1112 Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)),
1113 REMOTE_PORT_1.into(),
1114 )
1115 .unwrap();
1116
1117 assert_matches!(api.get_info(&socket), SocketInfo::Connected(ConnInfo { .. }));
1118
1119 let count = api.disconnect_bound(&IpSocketMatcher::Cookie(SocketCookieMatcher {
1120 cookie: socket.socket_cookie().export_value(),
1121 invert: false,
1122 }));
1123 assert_eq!(count, 1);
1124 assert_matches!(api.get_info(&socket), SocketInfo::Unbound);
1125
1126 let new_remote_port = NonZeroU16::new(9999).unwrap();
1129 api.connect(
1130 &socket,
1131 Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)),
1132 new_remote_port.into(),
1133 )
1134 .unwrap();
1135
1136 let info = api.get_info(&socket);
1137 assert_matches!(info, SocketInfo::Connected(ConnInfo { .. }));
1138 if let SocketInfo::Connected(conn_info) = info {
1139 assert_eq!(conn_info.remote_identifier, new_remote_port.get());
1141 }
1142 }
1143}