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