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