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 BoundState, DualStackIpExt, Listener, SocketCookie, TcpBindingsTypes, TcpSocketId,
14 TcpSocketState, TcpSocketStateInner, Unbound,
15};
16use crate::internal::state::State;
17use crate::internal::state::info::TcpSocketInfo;
18
19pub(crate) struct TcpSocketStateForMatching<
22 'a,
23 I: DualStackIpExt,
24 D: netstack3_base::WeakDeviceIdentifier,
25 BT: TcpBindingsTypes,
26> {
27 pub(crate) state: &'a TcpSocketState<I, D, BT>,
28 pub(crate) id: &'a TcpSocketId<I, D, BT>,
29}
30
31impl<'a, I: DualStackIpExt, D: netstack3_base::WeakDeviceIdentifier, BT: TcpBindingsTypes>
32 netstack3_base::MaybeSocketTransportProperties for TcpSocketStateForMatching<'a, I, D, BT>
33{
34 type TcpProps<'b>
35 = Self
36 where
37 Self: 'b;
38
39 type UdpProps<'b>
40 = Never
41 where
42 Self: 'b;
43
44 fn tcp_socket_properties(&self) -> Option<&Self::TcpProps<'_>> {
45 Some(self)
46 }
47
48 fn udp_socket_properties(&self) -> Option<&Self::UdpProps<'_>> {
49 None
50 }
51}
52
53impl<'a, I: DualStackIpExt, D: netstack3_base::WeakDeviceIdentifier, BT: TcpBindingsTypes>
54 netstack3_base::TcpSocketProperties for TcpSocketStateForMatching<'a, I, D, BT>
55{
56 fn src_port_matches(&self, matcher: &netstack3_base::BoundPortMatcher) -> bool {
57 let src_port = match &self.state.socket_state {
58 TcpSocketStateInner::Unbound(_) => None,
59 TcpSocketStateInner::Bound(BoundState { addr, .. })
60 | TcpSocketStateInner::Listener(Listener { addr, .. }) => {
61 Some(I::get_bound_info(addr).port.get())
62 }
63 TcpSocketStateInner::Connected { conn, .. } => {
64 Some(I::get_conn_info(conn).local_addr.port.get())
65 }
66 };
67
68 matcher.matches(&src_port)
69 }
70
71 fn dst_port_matches(&self, matcher: &netstack3_base::BoundPortMatcher) -> bool {
72 let dst_port = match &self.state.socket_state {
73 TcpSocketStateInner::Unbound(_)
74 | TcpSocketStateInner::Bound(_)
75 | TcpSocketStateInner::Listener(_) => None,
76 TcpSocketStateInner::Connected { conn, .. } => {
77 Some(I::get_conn_info(conn).remote_addr.port.get())
78 }
79 };
80
81 matcher.matches(&dst_port)
82 }
83
84 fn state_matches(&self, matcher: &netstack3_base::TcpStateMatcher) -> bool {
85 matcher.matches(&self.state.base_state())
86 }
87}
88
89impl<'a, I: DualStackIpExt, D: netstack3_base::WeakDeviceIdentifier, BT: TcpBindingsTypes>
90 IpSocketProperties<BT::DeviceClass> for TcpSocketStateForMatching<'a, I, D, BT>
91where
92 D::Strong: netstack3_base::InterfaceProperties<BT::DeviceClass>,
93{
94 fn family_matches(&self, family: &net_types::ip::IpVersion) -> bool {
95 I::VERSION == *family
96 }
97
98 fn src_addr_matches(&self, addr: &netstack3_base::BoundAddressMatcherEither) -> bool {
99 let src_addr = match &self.state.socket_state {
100 TcpSocketStateInner::Unbound(_) => None,
101 TcpSocketStateInner::Bound(BoundState { addr, .. })
102 | TcpSocketStateInner::Listener(Listener { addr, .. }) => {
103 I::get_bound_info(addr).addr.map(|a| a.addr().get())
104 }
105 TcpSocketStateInner::Connected { conn, .. } => {
106 Some(I::get_conn_info(conn).local_addr.ip.addr().get())
107 }
108 };
109
110 addr.matches(&src_addr.map(|a| a.to_ip_addr()))
111 }
112
113 fn dst_addr_matches(&self, addr: &netstack3_base::BoundAddressMatcherEither) -> bool {
114 let dst_addr = match &self.state.socket_state {
115 TcpSocketStateInner::Unbound(_)
116 | TcpSocketStateInner::Bound(_)
117 | TcpSocketStateInner::Listener(_) => None,
118 TcpSocketStateInner::Connected { conn, .. } => {
119 Some(I::get_conn_info(conn).remote_addr.ip.addr().get())
120 }
121 };
122
123 addr.matches(&dst_addr.map(|a| a.to_ip_addr()))
124 }
125
126 fn transport_protocol_matches(
127 &self,
128 proto: &netstack3_base::SocketTransportProtocolMatcher,
129 ) -> bool {
130 proto.matches(self)
131 }
132
133 fn bound_interface_matches(
134 &self,
135 iface: &netstack3_base::BoundInterfaceMatcher<BT::DeviceClass>,
136 ) -> bool {
137 let device = match &self.state.socket_state {
138 TcpSocketStateInner::Unbound(_) => None,
139 TcpSocketStateInner::Bound(BoundState { addr, .. })
140 | TcpSocketStateInner::Listener(Listener { addr, .. }) => {
141 I::get_bound_info(addr).device
142 }
143 TcpSocketStateInner::Connected { conn, .. } => I::get_conn_info(conn).device,
144 };
145 iface.matches(&device.and_then(|d| d.upgrade()).as_ref())
146 }
147
148 fn cookie_matches(&self, cookie: &netstack3_base::SocketCookieMatcher) -> bool {
149 cookie.matches(&self.id.socket_cookie().export_value())
150 }
151
152 fn mark_matches(&self, matcher: &netstack3_base::MarkInDomainMatcher) -> bool {
153 matcher.matcher.matches(self.state.socket_options.ip_options.marks.get(matcher.domain))
154 }
155}
156
157#[cfg_attr(any(test, feature = "testutils"), derive(Debug, PartialEq, Eq))]
159pub struct TcpSocketDiagnostics<I: Ip, Instant> {
160 pub state_machine: netstack3_base::TcpSocketState,
162 pub tuple: TcpSocketDiagnosticTuple<I>,
164 pub cookie: SocketCookie,
166 pub marks: Marks,
168 pub tcp_info: Option<TcpSocketInfo<Instant>>,
170}
171
172#[derive(Debug, PartialEq, Eq)]
178#[allow(missing_docs)]
179pub enum TcpSocketDiagnosticTuple<I: Ip> {
180 Bound { src_addr: Option<I::Addr>, src_port: NonZeroU16 },
184 Connected { src_addr: I::Addr, src_port: NonZeroU16, dst_addr: I::Addr, dst_port: NonZeroU16 },
186}
187
188impl<I: Ip> TcpSocketDiagnosticTuple<I> {
189 pub fn src_addr(&self) -> Option<I::Addr> {
191 match self {
192 Self::Bound { src_addr, src_port: _ } => *src_addr,
193 Self::Connected { src_addr, src_port: _, dst_addr: _, dst_port: _ } => Some(*src_addr),
194 }
195 }
196
197 pub fn src_port(&self) -> Option<NonZeroU16> {
199 match self {
200 Self::Bound { src_addr: _, src_port }
201 | Self::Connected { src_addr: _, src_port, dst_addr: _, dst_port: _ } => {
202 Some(*src_port)
203 }
204 }
205 }
206
207 pub fn dst_addr(&self) -> Option<I::Addr> {
209 match self {
210 Self::Bound { src_addr: _, src_port: _ } => None,
211 Self::Connected { src_addr: _, src_port: _, dst_addr, dst_port: _ } => Some(*dst_addr),
212 }
213 }
214
215 pub fn dst_port(&self) -> Option<NonZeroU16> {
217 match self {
218 Self::Bound { src_addr: _, src_port: _ } => None,
219 Self::Connected { src_addr: _, src_port: _, dst_addr: _, dst_port } => Some(*dst_port),
220 }
221 }
222}
223
224impl<I, D, BT> TcpSocketState<I, D, BT>
225where
226 I: DualStackIpExt,
227 D: WeakDeviceIdentifier,
228 BT: TcpBindingsTypes,
229{
230 pub(crate) fn tcp_info(
231 &self,
232 counters: &crate::internal::counters::TcpCountersWithSocket<I>,
233 ) -> TcpSocketInfo<BT::Instant> {
234 match &self.socket_state {
235 TcpSocketStateInner::Unbound(unbound) => {
236 TcpSocketInfo::from_partial_state(unbound.base_state(), counters.as_ref())
237 }
238 TcpSocketStateInner::Bound(bound) => {
239 TcpSocketInfo::from_partial_state(bound.base_state(), counters.as_ref())
240 }
241 TcpSocketStateInner::Listener(listener) => {
242 TcpSocketInfo::from_partial_state(listener.base_state(), counters.as_ref())
243 }
244 TcpSocketStateInner::Connected { conn, timer: _ } => {
245 TcpSocketInfo::from_full_state(I::get_state(conn), counters.as_ref())
246 }
247 }
248 }
249
250 pub(crate) fn get_diagnostics(
251 &self,
252 counters: &crate::internal::counters::TcpCountersWithSocket<I>,
253 extended_info: bool,
254 ) -> Option<(
255 TcpSocketDiagnosticTuple<I>,
256 netstack3_base::TcpSocketState,
257 Marks,
258 Option<TcpSocketInfo<BT::Instant>>,
259 )> {
260 let tuple = match &self.socket_state {
261 TcpSocketStateInner::Unbound(_) => None,
262 TcpSocketStateInner::Bound(BoundState { addr, .. })
263 | TcpSocketStateInner::Listener(Listener { addr, .. }) => {
264 let addr_info = I::get_bound_info(addr);
265 Some(TcpSocketDiagnosticTuple::Bound {
266 src_addr: addr_info.addr.map(|ip| ip.addr().get()),
267 src_port: addr_info.port,
268 })
269 }
270 TcpSocketStateInner::Connected { conn, .. } => {
271 let info = I::get_conn_info(conn);
272 Some(TcpSocketDiagnosticTuple::Connected {
273 src_addr: info.local_addr.ip.addr().get(),
274 dst_addr: info.remote_addr.ip.addr().get(),
275 src_port: info.local_addr.port,
276 dst_port: info.remote_addr.port,
277 })
278 }
279 }?;
280
281 Some((
282 tuple,
283 self.base_state(),
284 self.socket_options.ip_options.marks,
285 extended_info.then(|| self.tcp_info(counters)),
286 ))
287 }
288
289 pub(crate) fn base_state(&self) -> netstack3_base::TcpSocketState {
290 match &self.socket_state {
291 TcpSocketStateInner::Unbound(unbound) => unbound.base_state(),
292 TcpSocketStateInner::Bound(bound) => bound.base_state(),
293 TcpSocketStateInner::Listener(listener) => listener.base_state(),
294 TcpSocketStateInner::Connected { conn, .. } => match I::get_state(conn) {
295 State::Closed(_) => netstack3_base::TcpSocketState::Close,
296 State::Listen(_) => netstack3_base::TcpSocketState::Listen,
297 State::SynRcvd(_) => netstack3_base::TcpSocketState::SynRecv,
298 State::SynSent(_) => netstack3_base::TcpSocketState::SynSent,
299 State::Established(_) => netstack3_base::TcpSocketState::Established,
300 State::CloseWait(_) => netstack3_base::TcpSocketState::CloseWait,
301 State::LastAck(_) => netstack3_base::TcpSocketState::LastAck,
302 State::FinWait1(_) => netstack3_base::TcpSocketState::FinWait1,
303 State::FinWait2(_) => netstack3_base::TcpSocketState::FinWait2,
304 State::Closing(_) => netstack3_base::TcpSocketState::Closing,
305 State::TimeWait(_) => netstack3_base::TcpSocketState::TimeWait,
306 },
307 }
308 }
309}
310
311impl<D, Extra> Unbound<D, Extra> {
312 fn base_state(&self) -> netstack3_base::TcpSocketState {
313 netstack3_base::TcpSocketState::Close
314 }
315}
316
317impl<I, D, BT> BoundState<I, D, BT>
318where
319 I: DualStackIpExt,
320 D: WeakDeviceIdentifier,
321 BT: TcpBindingsTypes,
322{
323 fn base_state(&self) -> netstack3_base::TcpSocketState {
324 netstack3_base::TcpSocketState::Close
325 }
326}
327
328impl<I, D, BT> Listener<I, D, BT>
329where
330 I: DualStackIpExt,
331 D: WeakDeviceIdentifier,
332 BT: TcpBindingsTypes,
333{
334 fn base_state(&self) -> netstack3_base::TcpSocketState {
335 netstack3_base::TcpSocketState::Listen
336 }
337}
338
339#[cfg(test)]
340mod tests {
341 use alloc::string::ToString;
342 use alloc::vec;
343 use alloc::vec::Vec;
344 use assert_matches::assert_matches;
345 use core::num::NonZeroUsize;
346 use core::time::Duration;
347
348 use ip_test_macro::ip_test;
349 use net_types::ZonedAddr;
350 use net_types::ip::{Ip, Subnet};
351 use netstack3_base::testutil::{
352 FakeDeviceId, FakeInstant, FakeNetworkSpec as _, set_logger_for_test,
353 };
354 use netstack3_base::{
355 AddressMatcher, AddressMatcherEither, AddressMatcherType, BoundAddressMatcherEither,
356 BoundInterfaceMatcher, BoundPortMatcher, InterfaceMatcher, IpSocketMatcher, Mark,
357 MarkDomain, MarkMatcher, PortMatcher, SocketCookieMatcher, SocketTransportProtocolMatcher,
358 StrongDeviceIdentifier, SubnetMatcher, TcpSocketMatcher, TcpStateMatcher, UdpSocketMatcher,
359 };
360 use test_case::test_case;
361 use test_util::assert_gt;
362
363 use super::*;
364 use crate::AcceptError;
365 use crate::internal::base::ConnectionError;
366 use crate::internal::socket::TcpContext;
367 use crate::internal::socket::tests::{
368 FakeTcpNetworkSpec, TcpApiExt, TcpBindingsCtx, TcpCoreCtx, TcpCtx, TcpTestIpExt,
369 };
370 use crate::internal::state::info::CongestionControlState;
371
372 const LOCAL_PORT_1: NonZeroU16 = NonZeroU16::new(1234).unwrap();
373 const LOCAL_PORT_2: NonZeroU16 = NonZeroU16::new(5678).unwrap();
374 const LOCAL_PORT_3: NonZeroU16 = NonZeroU16::new(4321).unwrap();
375
376 const REMOTE_PORT_1: NonZeroU16 = NonZeroU16::new(100).unwrap();
377 const REMOTE_PORT_2: NonZeroU16 = NonZeroU16::new(200).unwrap();
378
379 const MARK: u32 = 0x10;
380 const MARK_MASK: u32 = !0;
381
382 #[ip_test(I)]
383 fn diagnostics_match_ip_version<I: TcpTestIpExt>()
384 where
385 TcpCoreCtx<FakeDeviceId, TcpBindingsCtx<FakeDeviceId>>:
386 TcpContext<I, TcpBindingsCtx<FakeDeviceId>>,
387 {
388 set_logger_for_test();
389
390 let mut ctx = TcpCtx::with_core_ctx(TcpCoreCtx::new::<I>(
391 I::TEST_ADDRS.local_ip,
392 I::TEST_ADDRS.remote_ip,
393 ));
394 let mut api = ctx.tcp_api::<I>();
395 let s = api.create(Default::default());
396 api.bind(&s, None, Some(LOCAL_PORT_1)).expect("bind should succeed");
397 api.listen(&s, NonZeroUsize::new(1).unwrap()).expect("listen should succeed");
398
399 let mut results = Vec::new();
400 api.bound_sockets_diagnostics(&IpSocketMatcher::Family(I::VERSION), &mut results, false);
401 assert_eq!(
402 results,
403 vec![TcpSocketDiagnostics {
404 state_machine: netstack3_base::TcpSocketState::Listen,
405 tuple: TcpSocketDiagnosticTuple::Bound { src_addr: None, src_port: LOCAL_PORT_1 },
406 cookie: s.socket_cookie(),
407 marks: Marks::default(),
408 tcp_info: None,
409 }]
410 );
411
412 results.clear();
413 api.bound_sockets_diagnostics(
414 &IpSocketMatcher::Family(
415 <<I as netstack3_base::socket::DualStackIpExt>::OtherVersion as Ip>::VERSION,
416 ),
417 &mut results,
418 false,
419 );
420 assert_eq!(results, Vec::new());
421 }
422
423 #[ip_test(I)]
424 fn diagnostics_match_src_addr<I: TcpTestIpExt>()
425 where
426 TcpCoreCtx<FakeDeviceId, TcpBindingsCtx<FakeDeviceId>>:
427 TcpContext<I, TcpBindingsCtx<FakeDeviceId>>,
428 {
429 set_logger_for_test();
430
431 let mut ctx = TcpCtx::with_core_ctx(TcpCoreCtx::new::<I>(
432 I::TEST_ADDRS.local_ip,
433 I::TEST_ADDRS.remote_ip,
434 ));
435 let mut api = ctx.tcp_api::<I>();
436 let s = api.create(Default::default());
437 api.bind(&s, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_1))
438 .expect("bind should succeed");
439 api.listen(&s, NonZeroUsize::new(1).unwrap()).expect("listen should succeed");
440
441 let mut results = Vec::new();
442 let matcher = I::map_ip_in(
443 I::TEST_ADDRS.local_ip.get(),
444 |addr| {
445 BoundAddressMatcherEither::Bound(AddressMatcherEither::V4(AddressMatcher {
446 matcher: AddressMatcherType::Subnet(SubnetMatcher(
447 Subnet::new(addr, 32).unwrap(),
448 )),
449 invert: false,
450 }))
451 },
452 |addr| {
453 BoundAddressMatcherEither::Bound(AddressMatcherEither::V6(AddressMatcher {
454 matcher: AddressMatcherType::Subnet(SubnetMatcher(
455 Subnet::new(addr, 128).unwrap(),
456 )),
457 invert: false,
458 }))
459 },
460 );
461 api.bound_sockets_diagnostics(&IpSocketMatcher::SrcAddr(matcher), &mut results, false);
462 assert_eq!(
463 results,
464 vec![TcpSocketDiagnostics {
465 state_machine: netstack3_base::TcpSocketState::Listen,
466 tuple: TcpSocketDiagnosticTuple::Bound {
467 src_addr: Some(I::TEST_ADDRS.local_ip.get()),
468 src_port: LOCAL_PORT_1,
469 },
470 cookie: s.socket_cookie(),
471 marks: Marks::default(),
472 tcp_info: None,
473 }]
474 );
475
476 results.clear();
477 let matcher = I::map_ip_in(
478 I::TEST_ADDRS.remote_ip.get(),
479 |addr| {
480 BoundAddressMatcherEither::Bound(AddressMatcherEither::V4(AddressMatcher {
481 matcher: AddressMatcherType::Subnet(SubnetMatcher(
482 Subnet::new(addr, 32).unwrap(),
483 )),
484 invert: false,
485 }))
486 },
487 |addr| {
488 BoundAddressMatcherEither::Bound(AddressMatcherEither::V6(AddressMatcher {
489 matcher: AddressMatcherType::Subnet(SubnetMatcher(
490 Subnet::new(addr, 128).unwrap(),
491 )),
492 invert: false,
493 }))
494 },
495 );
496 api.bound_sockets_diagnostics(&IpSocketMatcher::SrcAddr(matcher), &mut results, false);
497 assert_eq!(results, Vec::new());
498 }
499
500 #[ip_test(I)]
501 fn diagnostics_match_dst_addr<I: TcpTestIpExt>()
502 where
503 TcpCoreCtx<FakeDeviceId, TcpBindingsCtx<FakeDeviceId>>:
504 TcpContext<I, TcpBindingsCtx<FakeDeviceId>>,
505 {
506 set_logger_for_test();
507
508 let mut ctx = TcpCtx::with_core_ctx(TcpCoreCtx::new::<I>(
509 I::TEST_ADDRS.local_ip,
510 I::TEST_ADDRS.remote_ip,
511 ));
512 let mut api = ctx.tcp_api::<I>();
513 let s = api.create(Default::default());
514 api.bind(&s, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_1))
515 .expect("bind should succeed");
516 api.connect(&s, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)), LOCAL_PORT_2)
517 .expect("connect should succeed");
518
519 let mut results = Vec::new();
520 let matcher = I::map_ip_in(
521 I::TEST_ADDRS.remote_ip.get(),
522 |addr| {
523 BoundAddressMatcherEither::Bound(AddressMatcherEither::V4(AddressMatcher {
524 matcher: AddressMatcherType::Subnet(SubnetMatcher(
525 Subnet::new(addr, 32).unwrap(),
526 )),
527 invert: false,
528 }))
529 },
530 |addr| {
531 BoundAddressMatcherEither::Bound(AddressMatcherEither::V6(AddressMatcher {
532 matcher: AddressMatcherType::Subnet(SubnetMatcher(
533 Subnet::new(addr, 128).unwrap(),
534 )),
535 invert: false,
536 }))
537 },
538 );
539 api.bound_sockets_diagnostics(&IpSocketMatcher::DstAddr(matcher), &mut results, false);
540 assert_eq!(
541 results,
542 vec![TcpSocketDiagnostics {
543 state_machine: netstack3_base::TcpSocketState::SynSent,
544 tuple: TcpSocketDiagnosticTuple::Connected {
545 src_addr: I::TEST_ADDRS.local_ip.get(),
546 src_port: LOCAL_PORT_1,
547 dst_addr: I::TEST_ADDRS.remote_ip.get(),
548 dst_port: LOCAL_PORT_2,
549 },
550 cookie: s.socket_cookie(),
551 marks: Marks::default(),
552 tcp_info: None,
553 }]
554 );
555
556 results.clear();
557 let matcher = I::map_ip_in(
558 I::TEST_ADDRS.local_ip.get(),
559 |addr| {
560 BoundAddressMatcherEither::Bound(AddressMatcherEither::V4(AddressMatcher {
561 matcher: AddressMatcherType::Subnet(SubnetMatcher(
562 Subnet::new(addr, 32).unwrap(),
563 )),
564 invert: false,
565 }))
566 },
567 |addr| {
568 BoundAddressMatcherEither::Bound(AddressMatcherEither::V6(AddressMatcher {
569 matcher: AddressMatcherType::Subnet(SubnetMatcher(
570 Subnet::new(addr, 128).unwrap(),
571 )),
572 invert: false,
573 }))
574 },
575 );
576 api.bound_sockets_diagnostics(&IpSocketMatcher::DstAddr(matcher), &mut results, false);
577 assert_eq!(results, Vec::new());
578 }
579
580 #[ip_test(I)]
581 fn diagnostics_match_proto<I: TcpTestIpExt>()
582 where
583 TcpCoreCtx<FakeDeviceId, TcpBindingsCtx<FakeDeviceId>>:
584 TcpContext<I, TcpBindingsCtx<FakeDeviceId>>,
585 {
586 set_logger_for_test();
587
588 let mut ctx = TcpCtx::with_core_ctx(TcpCoreCtx::new::<I>(
589 I::TEST_ADDRS.local_ip,
590 I::TEST_ADDRS.remote_ip,
591 ));
592 let mut api = ctx.tcp_api::<I>();
593 let s = api.create(Default::default());
594 api.bind(&s, None, Some(LOCAL_PORT_1)).expect("bind should succeed");
595 api.listen(&s, NonZeroUsize::new(1).unwrap()).expect("listen should succeed");
596
597 let mut results = Vec::new();
598 api.bound_sockets_diagnostics(
599 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Tcp(TcpSocketMatcher::Empty)),
600 &mut results,
601 false,
602 );
603 assert_eq!(
604 results,
605 vec![TcpSocketDiagnostics {
606 state_machine: netstack3_base::TcpSocketState::Listen,
607 tuple: TcpSocketDiagnosticTuple::Bound { src_addr: None, src_port: LOCAL_PORT_1 },
608 cookie: s.socket_cookie(),
609 marks: Marks::default(),
610 tcp_info: None,
611 }]
612 );
613
614 results.clear();
615 api.bound_sockets_diagnostics(
616 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Udp(UdpSocketMatcher::Empty)),
617 &mut results,
618 false,
619 );
620 assert_eq!(results, Vec::new());
621 }
622
623 #[ip_test(I)]
624 fn diagnostics_match_device<I: TcpTestIpExt>()
625 where
626 TcpCoreCtx<FakeDeviceId, TcpBindingsCtx<FakeDeviceId>>:
627 TcpContext<I, TcpBindingsCtx<FakeDeviceId>>,
628 {
629 set_logger_for_test();
630
631 let mut ctx = TcpCtx::with_core_ctx(TcpCoreCtx::new::<I>(
632 I::TEST_ADDRS.local_ip,
633 I::TEST_ADDRS.remote_ip,
634 ));
635 let mut api = ctx.tcp_api::<I>();
636 let s = api.create(Default::default());
637 api.set_device(&s, Some(FakeDeviceId)).expect("set device should succeed");
638 api.bind(&s, None, Some(LOCAL_PORT_1)).expect("bind should succeed");
639 api.listen(&s, NonZeroUsize::new(1).unwrap()).expect("listen should succeed");
640
641 let mut results = Vec::new();
642 api.bound_sockets_diagnostics(
643 &IpSocketMatcher::BoundInterface(BoundInterfaceMatcher::Bound(InterfaceMatcher::Name(
644 FakeDeviceId::FAKE_NAME.to_string(),
645 ))),
646 &mut results,
647 false,
648 );
649 assert_eq!(
650 results,
651 vec![TcpSocketDiagnostics {
652 state_machine: netstack3_base::TcpSocketState::Listen,
653 tuple: TcpSocketDiagnosticTuple::Bound { src_addr: None, src_port: LOCAL_PORT_1 },
654 cookie: s.socket_cookie(),
655 marks: Marks::default(),
656 tcp_info: None,
657 }]
658 );
659
660 results.clear();
661 api.bound_sockets_diagnostics(
662 &IpSocketMatcher::BoundInterface(BoundInterfaceMatcher::Unbound),
663 &mut results,
664 false,
665 );
666 assert_eq!(results, Vec::new());
667 }
668
669 #[ip_test(I)]
670 fn diagnostics_match_cookie<I: TcpTestIpExt>()
671 where
672 TcpCoreCtx<FakeDeviceId, TcpBindingsCtx<FakeDeviceId>>:
673 TcpContext<I, TcpBindingsCtx<FakeDeviceId>>,
674 {
675 set_logger_for_test();
676
677 let mut ctx = TcpCtx::with_core_ctx(TcpCoreCtx::new::<I>(
678 I::TEST_ADDRS.local_ip,
679 I::TEST_ADDRS.remote_ip,
680 ));
681 let mut api = ctx.tcp_api::<I>();
682
683 let socket_1 = api.create(Default::default());
684 api.bind(&socket_1, None, Some(LOCAL_PORT_1)).expect("bind should succeed");
685 api.listen(&socket_1, NonZeroUsize::new(1).unwrap()).expect("listen should succeed");
686 let socket_2 = api.create(Default::default());
687 api.bind(&socket_2, None, Some(LOCAL_PORT_2)).expect("bind should succeed");
688 api.listen(&socket_2, NonZeroUsize::new(1).unwrap()).expect("listen should succeed");
689
690 let mut results = Vec::new();
691 api.bound_sockets_diagnostics(
692 &IpSocketMatcher::Cookie(SocketCookieMatcher {
693 cookie: socket_1.socket_cookie().export_value(),
694 invert: false,
695 }),
696 &mut results,
697 false,
698 );
699 assert_eq!(
700 results,
701 vec![TcpSocketDiagnostics {
702 state_machine: netstack3_base::TcpSocketState::Listen,
703 tuple: TcpSocketDiagnosticTuple::Bound { src_addr: None, src_port: LOCAL_PORT_1 },
704 cookie: socket_1.socket_cookie(),
705 marks: Marks::default(),
706 tcp_info: None,
707 }]
708 );
709
710 results.clear();
711 api.bound_sockets_diagnostics(
712 &IpSocketMatcher::Cookie(SocketCookieMatcher {
713 cookie: socket_2.socket_cookie().export_value(),
714 invert: false,
715 }),
716 &mut results,
717 false,
718 );
719 assert_eq!(
720 results,
721 vec![TcpSocketDiagnostics {
722 state_machine: netstack3_base::TcpSocketState::Listen,
723 tuple: TcpSocketDiagnosticTuple::Bound { src_addr: None, src_port: LOCAL_PORT_2 },
724 cookie: socket_2.socket_cookie(),
725 marks: Marks::default(),
726 tcp_info: None,
727 }]
728 );
729 }
730
731 #[ip_test(I)]
732 #[test_case::test_case(MarkDomain::Mark1; "mark_1")]
733 #[test_case::test_case(MarkDomain::Mark2; "mark_2")]
734 fn diagnostics_match_mark<I: TcpTestIpExt>(domain: MarkDomain)
735 where
736 TcpCoreCtx<FakeDeviceId, TcpBindingsCtx<FakeDeviceId>>:
737 TcpContext<I, TcpBindingsCtx<FakeDeviceId>>,
738 {
739 set_logger_for_test();
740
741 let mut ctx = TcpCtx::with_core_ctx(TcpCoreCtx::new::<I>(
742 I::TEST_ADDRS.local_ip,
743 I::TEST_ADDRS.remote_ip,
744 ));
745 let mut api = ctx.tcp_api::<I>();
746
747 let s = api.create(Default::default());
748 api.bind(&s, None, Some(LOCAL_PORT_1)).expect("bind should succeed");
749 api.listen(&s, NonZeroUsize::new(1).unwrap()).expect("listen should succeed");
750 api.set_mark(&s, domain, Mark(Some(MARK)));
751
752 let mut results = Vec::new();
753 let matcher = |query_mark| {
754 IpSocketMatcher::Mark(netstack3_base::MarkInDomainMatcher {
755 domain,
756 matcher: MarkMatcher::Marked {
757 mask: MARK_MASK,
758 start: query_mark,
759 end: query_mark,
760 invert: false,
761 },
762 })
763 };
764 api.bound_sockets_diagnostics(&matcher(MARK), &mut results, false);
765 assert_eq!(
766 results,
767 vec![TcpSocketDiagnostics {
768 state_machine: netstack3_base::TcpSocketState::Listen,
769 tuple: TcpSocketDiagnosticTuple::Bound { src_addr: None, src_port: LOCAL_PORT_1 },
770 cookie: s.socket_cookie(),
771 marks: netstack3_base::MarkStorage::new([(domain, MARK)]),
772 tcp_info: None,
773 }]
774 );
775
776 results.clear();
777 api.bound_sockets_diagnostics(&matcher(MARK + 1), &mut results, false);
778 assert_eq!(results, Vec::new());
779 }
780
781 #[ip_test(I)]
782 fn diagnostics_match_multiple<I: TcpTestIpExt>()
783 where
784 TcpCoreCtx<FakeDeviceId, TcpBindingsCtx<FakeDeviceId>>:
785 TcpContext<I, TcpBindingsCtx<FakeDeviceId>>,
786 {
787 set_logger_for_test();
788
789 let mut ctx = TcpCtx::with_core_ctx(TcpCoreCtx::new::<I>(
790 I::TEST_ADDRS.local_ip,
791 I::TEST_ADDRS.remote_ip,
792 ));
793 let mut api = ctx.tcp_api::<I>();
794
795 let socket_1 = api.create(Default::default());
796 api.bind(&socket_1, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_1))
797 .expect("bind should succeed");
798 api.connect(&socket_1, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)), REMOTE_PORT_1)
799 .expect("connect socket 1");
800 let socket_2 = api.create(Default::default());
801 api.bind(&socket_2, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_2))
802 .expect("bind should succeed");
803 api.connect(&socket_2, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)), REMOTE_PORT_1)
804 .expect("connect socket 2");
805 let socket_3 = api.create(Default::default());
806 api.bind(&socket_3, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_3))
807 .expect("bind should succeed");
808 api.connect(&socket_3, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)), REMOTE_PORT_2)
809 .expect("connect socket 3");
810
811 let mut results = Vec::new();
812
813 api.bound_sockets_diagnostics(
814 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Tcp(
815 TcpSocketMatcher::DstPort(BoundPortMatcher::Bound(PortMatcher {
816 range: REMOTE_PORT_1.get()..=REMOTE_PORT_1.get(),
817 invert: false,
818 })),
819 )),
820 &mut results,
821 false,
822 );
823
824 results.sort_by(|a, b| a.cookie.cmp(&b.cookie));
825 let mut expected = vec![
826 TcpSocketDiagnostics {
827 state_machine: netstack3_base::TcpSocketState::SynSent,
828 tuple: TcpSocketDiagnosticTuple::Connected {
829 src_addr: I::TEST_ADDRS.local_ip.get(),
830 src_port: LOCAL_PORT_1,
831 dst_addr: I::TEST_ADDRS.remote_ip.get(),
832 dst_port: REMOTE_PORT_1,
833 },
834 cookie: socket_1.socket_cookie(),
835 marks: Marks::default(),
836 tcp_info: None,
837 },
838 TcpSocketDiagnostics {
839 state_machine: netstack3_base::TcpSocketState::SynSent,
840 tuple: TcpSocketDiagnosticTuple::Connected {
841 src_addr: I::TEST_ADDRS.local_ip.get(),
842 src_port: LOCAL_PORT_2,
843 dst_addr: I::TEST_ADDRS.remote_ip.get(),
844 dst_port: REMOTE_PORT_1,
845 },
846 cookie: socket_2.socket_cookie(),
847 marks: Marks::default(),
848 tcp_info: None,
849 },
850 ];
851 expected.sort_by(|a, b| a.cookie.cmp(&b.cookie));
852 assert_eq!(results, expected);
853
854 results.clear();
855 api.bound_sockets_diagnostics(
856 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Tcp(
857 TcpSocketMatcher::DstPort(BoundPortMatcher::Bound(PortMatcher {
858 range: REMOTE_PORT_2.get()..=REMOTE_PORT_2.get(),
859 invert: false,
860 })),
861 )),
862 &mut results,
863 false,
864 );
865 assert_eq!(
866 results,
867 vec![TcpSocketDiagnostics {
868 state_machine: netstack3_base::TcpSocketState::SynSent,
869 tuple: TcpSocketDiagnosticTuple::Connected {
870 src_addr: I::TEST_ADDRS.local_ip.get(),
871 src_port: LOCAL_PORT_3,
872 dst_addr: I::TEST_ADDRS.remote_ip.get(),
873 dst_port: REMOTE_PORT_2,
874 },
875 cookie: socket_3.socket_cookie(),
876 marks: Marks::default(),
877 tcp_info: None,
878 }]
879 );
880 }
881
882 #[ip_test(I)]
883 fn diagnostics_match_src_port<I: TcpTestIpExt>()
884 where
885 TcpCoreCtx<FakeDeviceId, TcpBindingsCtx<FakeDeviceId>>: TcpContext<
886 I,
887 TcpBindingsCtx<FakeDeviceId>,
888 SingleStackConverter = I::SingleStackConverter,
889 DualStackConverter = I::DualStackConverter,
890 >,
891 {
892 set_logger_for_test();
893
894 let mut ctx = TcpCtx::with_core_ctx(TcpCoreCtx::new::<I>(
895 I::TEST_ADDRS.local_ip,
896 I::TEST_ADDRS.remote_ip,
897 ));
898 let mut api = ctx.tcp_api::<I>();
899 let s = api.create(Default::default());
900 api.bind(&s, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_1))
901 .expect("bind should succeed");
902 api.listen(&s, NonZeroUsize::new(1).unwrap()).expect("listen should succeed");
903
904 let mut results = Vec::new();
905 api.bound_sockets_diagnostics(
906 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Tcp(
907 TcpSocketMatcher::SrcPort(BoundPortMatcher::Bound(PortMatcher {
908 range: LOCAL_PORT_1.get()..=LOCAL_PORT_1.get(),
909 invert: false,
910 })),
911 )),
912 &mut results,
913 false,
914 );
915 assert_eq!(
916 results,
917 vec![TcpSocketDiagnostics {
918 state_machine: netstack3_base::TcpSocketState::Listen,
919 tuple: TcpSocketDiagnosticTuple::Bound {
920 src_addr: Some(I::TEST_ADDRS.local_ip.get()),
921 src_port: LOCAL_PORT_1,
922 },
923 cookie: s.socket_cookie(),
924 marks: Marks::default(),
925 tcp_info: None,
926 }]
927 );
928
929 results.clear();
930 api.bound_sockets_diagnostics(
931 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Tcp(
932 TcpSocketMatcher::SrcPort(BoundPortMatcher::Bound(PortMatcher {
933 range: (LOCAL_PORT_1.get() + 1)..=(LOCAL_PORT_1.get() + 1),
934 invert: false,
935 })),
936 )),
937 &mut results,
938 false,
939 );
940 assert_eq!(results, Vec::new());
941 }
942
943 #[ip_test(I)]
944 fn diagnostics_match_dst_port<I: TcpTestIpExt>()
945 where
946 TcpCoreCtx<FakeDeviceId, TcpBindingsCtx<FakeDeviceId>>: TcpContext<
947 I,
948 TcpBindingsCtx<FakeDeviceId>,
949 SingleStackConverter = I::SingleStackConverter,
950 DualStackConverter = I::DualStackConverter,
951 >,
952 {
953 set_logger_for_test();
954
955 let mut ctx = TcpCtx::with_core_ctx(TcpCoreCtx::new::<I>(
956 I::TEST_ADDRS.local_ip,
957 I::TEST_ADDRS.remote_ip,
958 ));
959 let mut api = ctx.tcp_api::<I>();
960 let s = api.create(Default::default());
961 api.bind(&s, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_1))
962 .expect("bind should succeed");
963 api.connect(&s, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)), LOCAL_PORT_2)
964 .expect("connect should succeed");
965
966 let mut results = Vec::new();
967 api.bound_sockets_diagnostics(
968 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Tcp(
969 TcpSocketMatcher::DstPort(BoundPortMatcher::Bound(PortMatcher {
970 range: LOCAL_PORT_2.get()..=LOCAL_PORT_2.get(),
971 invert: false,
972 })),
973 )),
974 &mut results,
975 false,
976 );
977 assert_eq!(
978 results,
979 vec![TcpSocketDiagnostics {
980 state_machine: netstack3_base::TcpSocketState::SynSent,
981 tuple: TcpSocketDiagnosticTuple::Connected {
982 src_addr: I::TEST_ADDRS.local_ip.get(),
983 src_port: LOCAL_PORT_1,
984 dst_addr: I::TEST_ADDRS.remote_ip.get(),
985 dst_port: LOCAL_PORT_2,
986 },
987 cookie: s.socket_cookie(),
988 marks: Marks::default(),
989 tcp_info: None,
990 }]
991 );
992
993 results.clear();
994 api.bound_sockets_diagnostics(
995 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Tcp(
996 TcpSocketMatcher::DstPort(BoundPortMatcher::Bound(PortMatcher {
997 range: (LOCAL_PORT_2.get() + 1)..=(LOCAL_PORT_2.get() + 1),
998 invert: false,
999 })),
1000 )),
1001 &mut results,
1002 false,
1003 );
1004 assert_eq!(results, Vec::new());
1005 }
1006
1007 #[ip_test(I)]
1008 fn diagnostics_match_state<I: TcpTestIpExt>()
1009 where
1010 TcpCoreCtx<FakeDeviceId, TcpBindingsCtx<FakeDeviceId>>: TcpContext<
1011 I,
1012 TcpBindingsCtx<FakeDeviceId>,
1013 SingleStackConverter = I::SingleStackConverter,
1014 DualStackConverter = I::DualStackConverter,
1015 >,
1016 {
1017 set_logger_for_test();
1018
1019 let mut ctx = TcpCtx::with_core_ctx(TcpCoreCtx::new::<I>(
1020 I::TEST_ADDRS.local_ip,
1021 I::TEST_ADDRS.remote_ip,
1022 ));
1023 let mut api = ctx.tcp_api::<I>();
1024
1025 let listen_socket = api.create(Default::default());
1027 api.bind(
1028 &listen_socket,
1029 Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)),
1030 Some(LOCAL_PORT_1),
1031 )
1032 .expect("bind");
1033 api.listen(&listen_socket, NonZeroUsize::new(1).unwrap()).expect("listen");
1034
1035 let syn_sent_socket = api.create(Default::default());
1037 api.bind(
1038 &syn_sent_socket,
1039 Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)),
1040 Some(LOCAL_PORT_2),
1041 )
1042 .expect("bind");
1043 api.connect(
1046 &syn_sent_socket,
1047 Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)),
1048 LOCAL_PORT_3,
1049 )
1050 .expect("connect");
1051
1052 let mut results = Vec::new();
1053
1054 api.bound_sockets_diagnostics(
1055 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Tcp(TcpSocketMatcher::State(
1056 TcpStateMatcher::LISTEN,
1057 ))),
1058 &mut results,
1059 false,
1060 );
1061 assert_eq!(
1062 results,
1063 vec![TcpSocketDiagnostics {
1064 state_machine: netstack3_base::TcpSocketState::Listen,
1065 tuple: TcpSocketDiagnosticTuple::Bound {
1066 src_addr: Some(I::TEST_ADDRS.local_ip.get()),
1067 src_port: LOCAL_PORT_1,
1068 },
1069 cookie: listen_socket.socket_cookie(),
1070 marks: Marks::default(),
1071 tcp_info: None,
1072 }]
1073 );
1074
1075 results.clear();
1076 api.bound_sockets_diagnostics(
1077 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Tcp(TcpSocketMatcher::State(
1078 TcpStateMatcher::SYN_SENT,
1079 ))),
1080 &mut results,
1081 false,
1082 );
1083 assert_eq!(
1084 results,
1085 vec![TcpSocketDiagnostics {
1086 state_machine: netstack3_base::TcpSocketState::SynSent,
1087 tuple: TcpSocketDiagnosticTuple::Connected {
1088 src_addr: I::TEST_ADDRS.local_ip.get(),
1089 src_port: LOCAL_PORT_2,
1090 dst_addr: I::TEST_ADDRS.remote_ip.get(),
1091 dst_port: LOCAL_PORT_3,
1092 },
1093 cookie: syn_sent_socket.socket_cookie(),
1094 marks: Marks::default(),
1095 tcp_info: None,
1096 }]
1097 );
1098
1099 results.clear();
1100 api.bound_sockets_diagnostics(
1101 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Tcp(TcpSocketMatcher::State(
1102 TcpStateMatcher::ESTABLISHED,
1103 ))),
1104 &mut results,
1105 false,
1106 );
1107 assert_eq!(results, Vec::new());
1108 }
1109
1110 #[ip_test(I)]
1111 fn diagnostics_match_src_addr_unbound<I: TcpTestIpExt>()
1112 where
1113 TcpCoreCtx<FakeDeviceId, TcpBindingsCtx<FakeDeviceId>>:
1114 TcpContext<I, TcpBindingsCtx<FakeDeviceId>>,
1115 {
1116 set_logger_for_test();
1117
1118 let mut ctx = TcpCtx::with_core_ctx(TcpCoreCtx::new::<I>(
1119 I::TEST_ADDRS.local_ip,
1120 I::TEST_ADDRS.remote_ip,
1121 ));
1122 let mut api = ctx.tcp_api::<I>();
1123
1124 let s1 = api.create(Default::default());
1126 api.bind(&s1, None, Some(LOCAL_PORT_1)).expect("bind should succeed");
1127 api.listen(&s1, NonZeroUsize::new(1).unwrap()).expect("listen should succeed");
1128
1129 let s2 = api.create(Default::default());
1131 api.bind(&s2, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_2))
1132 .expect("bind should succeed");
1133 api.listen(&s2, NonZeroUsize::new(1).unwrap()).expect("listen should succeed");
1134
1135 let mut results = Vec::new();
1136 let matcher = match I::VERSION {
1137 net_types::ip::IpVersion::V4 => BoundAddressMatcherEither::Unbound,
1138 net_types::ip::IpVersion::V6 => BoundAddressMatcherEither::Unbound,
1139 };
1140 api.bound_sockets_diagnostics(&IpSocketMatcher::SrcAddr(matcher), &mut results, false);
1141 assert_eq!(
1142 results,
1143 vec![TcpSocketDiagnostics {
1144 state_machine: netstack3_base::TcpSocketState::Listen,
1145 tuple: TcpSocketDiagnosticTuple::Bound { src_addr: None, src_port: LOCAL_PORT_1 },
1146 cookie: s1.socket_cookie(),
1147 marks: Marks::default(),
1148 tcp_info: None,
1149 }]
1150 );
1151
1152 results.clear();
1153 let matcher = I::map_ip_in(
1154 I::TEST_ADDRS.local_ip.get(),
1155 |addr| {
1156 BoundAddressMatcherEither::Bound(AddressMatcherEither::V4(AddressMatcher {
1157 matcher: AddressMatcherType::Subnet(SubnetMatcher(
1158 Subnet::new(addr, 32).unwrap(),
1159 )),
1160 invert: false,
1161 }))
1162 },
1163 |addr| {
1164 BoundAddressMatcherEither::Bound(AddressMatcherEither::V6(AddressMatcher {
1165 matcher: AddressMatcherType::Subnet(SubnetMatcher(
1166 Subnet::new(addr, 128).unwrap(),
1167 )),
1168 invert: false,
1169 }))
1170 },
1171 );
1172 api.bound_sockets_diagnostics(&IpSocketMatcher::SrcAddr(matcher), &mut results, false);
1173 assert_eq!(
1174 results,
1175 vec![TcpSocketDiagnostics {
1176 state_machine: netstack3_base::TcpSocketState::Listen,
1177 tuple: TcpSocketDiagnosticTuple::Bound {
1178 src_addr: Some(I::TEST_ADDRS.local_ip.get()),
1179 src_port: LOCAL_PORT_2,
1180 },
1181 cookie: s2.socket_cookie(),
1182 marks: Marks::default(),
1183 tcp_info: None,
1184 }]
1185 );
1186 }
1187
1188 #[ip_test(I)]
1189 fn diagnostics_match_dst_addr_unbound<I: TcpTestIpExt>()
1190 where
1191 TcpCoreCtx<FakeDeviceId, TcpBindingsCtx<FakeDeviceId>>:
1192 TcpContext<I, TcpBindingsCtx<FakeDeviceId>>,
1193 {
1194 set_logger_for_test();
1195
1196 let mut ctx = TcpCtx::with_core_ctx(TcpCoreCtx::new::<I>(
1197 I::TEST_ADDRS.local_ip,
1198 I::TEST_ADDRS.remote_ip,
1199 ));
1200 let mut api = ctx.tcp_api::<I>();
1201
1202 let s1 = api.create(Default::default());
1204 api.bind(&s1, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_1))
1205 .expect("bind should succeed");
1206 api.listen(&s1, NonZeroUsize::new(1).unwrap()).expect("listen should succeed");
1207
1208 let s2 = api.create(Default::default());
1210 api.bind(&s2, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_2))
1211 .expect("bind should succeed");
1212 api.connect(&s2, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)), REMOTE_PORT_1)
1213 .expect("connect should succeed");
1214
1215 let mut results = Vec::new();
1216 let matcher = match I::VERSION {
1217 net_types::ip::IpVersion::V4 => BoundAddressMatcherEither::Unbound,
1218 net_types::ip::IpVersion::V6 => BoundAddressMatcherEither::Unbound,
1219 };
1220 api.bound_sockets_diagnostics(&IpSocketMatcher::DstAddr(matcher), &mut results, false);
1221 assert_eq!(
1222 results,
1223 vec![TcpSocketDiagnostics {
1224 state_machine: netstack3_base::TcpSocketState::Listen,
1225 tuple: TcpSocketDiagnosticTuple::Bound {
1226 src_addr: Some(I::TEST_ADDRS.local_ip.get()),
1227 src_port: LOCAL_PORT_1
1228 },
1229 cookie: s1.socket_cookie(),
1230 marks: Marks::default(),
1231 tcp_info: None,
1232 }]
1233 );
1234
1235 results.clear();
1236 let matcher = I::map_ip_in(
1237 I::TEST_ADDRS.remote_ip.get(),
1238 |addr| {
1239 BoundAddressMatcherEither::Bound(AddressMatcherEither::V4(AddressMatcher {
1240 matcher: AddressMatcherType::Subnet(SubnetMatcher(
1241 Subnet::new(addr, 32).unwrap(),
1242 )),
1243 invert: false,
1244 }))
1245 },
1246 |addr| {
1247 BoundAddressMatcherEither::Bound(AddressMatcherEither::V6(AddressMatcher {
1248 matcher: AddressMatcherType::Subnet(SubnetMatcher(
1249 Subnet::new(addr, 128).unwrap(),
1250 )),
1251 invert: false,
1252 }))
1253 },
1254 );
1255 api.bound_sockets_diagnostics(&IpSocketMatcher::DstAddr(matcher), &mut results, false);
1256 assert_eq!(
1257 results,
1258 vec![TcpSocketDiagnostics {
1259 state_machine: netstack3_base::TcpSocketState::SynSent,
1260 tuple: TcpSocketDiagnosticTuple::Connected {
1261 src_addr: I::TEST_ADDRS.local_ip.get(),
1262 src_port: LOCAL_PORT_2,
1263 dst_addr: I::TEST_ADDRS.remote_ip.get(),
1264 dst_port: REMOTE_PORT_1,
1265 },
1266 cookie: s2.socket_cookie(),
1267 marks: Marks::default(),
1268 tcp_info: None,
1269 }]
1270 );
1271 }
1272
1273 #[ip_test(I)]
1274 fn diagnostics_match_src_port_unbound<I: TcpTestIpExt>()
1275 where
1276 TcpCoreCtx<FakeDeviceId, TcpBindingsCtx<FakeDeviceId>>:
1277 TcpContext<I, TcpBindingsCtx<FakeDeviceId>>,
1278 {
1279 set_logger_for_test();
1280
1281 let mut ctx = TcpCtx::with_core_ctx(TcpCoreCtx::new::<I>(
1282 I::TEST_ADDRS.local_ip,
1283 I::TEST_ADDRS.remote_ip,
1284 ));
1285 let mut api = ctx.tcp_api::<I>();
1286
1287 let s1 = api.create(Default::default());
1288 api.bind(&s1, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_1))
1289 .expect("bind should succeed");
1290 api.listen(&s1, NonZeroUsize::new(1).unwrap()).expect("listen should succeed");
1291
1292 let mut results = Vec::new();
1293 api.bound_sockets_diagnostics(
1294 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Tcp(
1295 TcpSocketMatcher::SrcPort(BoundPortMatcher::Unbound),
1296 )),
1297 &mut results,
1298 false,
1299 );
1300 assert_eq!(results, Vec::new());
1302 }
1303
1304 #[ip_test(I)]
1305 fn diagnostics_match_dst_port_unbound<I: TcpTestIpExt>()
1306 where
1307 TcpCoreCtx<FakeDeviceId, TcpBindingsCtx<FakeDeviceId>>:
1308 TcpContext<I, TcpBindingsCtx<FakeDeviceId>>,
1309 {
1310 set_logger_for_test();
1311
1312 let mut ctx = TcpCtx::with_core_ctx(TcpCoreCtx::new::<I>(
1313 I::TEST_ADDRS.local_ip,
1314 I::TEST_ADDRS.remote_ip,
1315 ));
1316 let mut api = ctx.tcp_api::<I>();
1317
1318 let s1 = api.create(Default::default());
1320 api.bind(&s1, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_1))
1321 .expect("bind should succeed");
1322 api.listen(&s1, NonZeroUsize::new(1).unwrap()).expect("listen should succeed");
1323
1324 let s2 = api.create(Default::default());
1326 api.bind(&s2, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_2))
1327 .expect("bind should succeed");
1328 api.connect(&s2, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)), REMOTE_PORT_1)
1329 .expect("connect should succeed");
1330
1331 let mut results = Vec::new();
1332 api.bound_sockets_diagnostics(
1333 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Tcp(
1334 TcpSocketMatcher::DstPort(BoundPortMatcher::Unbound),
1335 )),
1336 &mut results,
1337 false,
1338 );
1339 assert_eq!(
1340 results,
1341 vec![TcpSocketDiagnostics {
1342 state_machine: netstack3_base::TcpSocketState::Listen,
1343 tuple: TcpSocketDiagnosticTuple::Bound {
1344 src_addr: Some(I::TEST_ADDRS.local_ip.get()),
1345 src_port: LOCAL_PORT_1
1346 },
1347 cookie: s1.socket_cookie(),
1348 marks: Marks::default(),
1349 tcp_info: None,
1350 }]
1351 );
1352
1353 results.clear();
1354 api.bound_sockets_diagnostics(
1355 &IpSocketMatcher::Proto(SocketTransportProtocolMatcher::Tcp(
1356 TcpSocketMatcher::DstPort(BoundPortMatcher::Bound(PortMatcher {
1357 range: REMOTE_PORT_1.get()..=REMOTE_PORT_1.get(),
1358 invert: false,
1359 })),
1360 )),
1361 &mut results,
1362 false,
1363 );
1364 assert_eq!(
1365 results,
1366 vec![TcpSocketDiagnostics {
1367 state_machine: netstack3_base::TcpSocketState::SynSent,
1368 tuple: TcpSocketDiagnosticTuple::Connected {
1369 src_addr: I::TEST_ADDRS.local_ip.get(),
1370 src_port: LOCAL_PORT_2,
1371 dst_addr: I::TEST_ADDRS.remote_ip.get(),
1372 dst_port: REMOTE_PORT_1,
1373 },
1374 cookie: s2.socket_cookie(),
1375 marks: Marks::default(),
1376 tcp_info: None,
1377 }]
1378 );
1379 }
1380
1381 const LOCAL: &'static str = "local";
1382 const REMOTE: &'static str = "remote";
1383
1384 #[ip_test(I)]
1385 fn disconnect_connected<I: TcpTestIpExt>()
1386 where
1387 TcpCoreCtx<FakeDeviceId, TcpBindingsCtx<FakeDeviceId>>: TcpContext<
1388 I,
1389 TcpBindingsCtx<FakeDeviceId>,
1390 SingleStackConverter = I::SingleStackConverter,
1391 DualStackConverter = I::DualStackConverter,
1392 >,
1393 {
1394 set_logger_for_test();
1395
1396 let mut net = FakeTcpNetworkSpec::new_network(
1397 [
1398 (
1399 LOCAL,
1400 TcpCtx::with_core_ctx(TcpCoreCtx::new::<I>(
1401 I::TEST_ADDRS.local_ip,
1402 I::TEST_ADDRS.remote_ip,
1403 )),
1404 ),
1405 (
1406 REMOTE,
1407 TcpCtx::with_core_ctx(TcpCoreCtx::new::<I>(
1408 I::TEST_ADDRS.remote_ip,
1409 I::TEST_ADDRS.local_ip,
1410 )),
1411 ),
1412 ],
1413 |net, meta| {
1414 if net == LOCAL {
1415 alloc::vec![(REMOTE, meta, None)]
1416 } else {
1417 alloc::vec![(LOCAL, meta, None)]
1418 }
1419 },
1420 );
1421
1422 let client_socket = net.with_context(LOCAL, |ctx| {
1423 let mut api = ctx.tcp_api();
1424 let s: TcpSocketId<I, _, _> = api.create(Default::default());
1425 api.set_device(&s, Some(FakeDeviceId)).expect("set device should succeed");
1427 api.bind(&s, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_1))
1428 .expect("bind should succeed");
1429 api.connect(&s, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)), REMOTE_PORT_1)
1430 .expect("can connect");
1431 s
1432 });
1433 let server_listener = net.with_context(REMOTE, |ctx| {
1434 let mut api = ctx.tcp_api::<I>();
1435 let s = api.create(Default::default());
1436 api.bind(&s, None, Some(REMOTE_PORT_1)).expect("failed to bind the server socket");
1437 api.listen(&s, NonZeroUsize::MIN).expect("can listen");
1438 s
1439 });
1440
1441 net.run_until_idle();
1442
1443 let server_socket = net.with_context(REMOTE, |ctx| {
1444 let mut api = ctx.tcp_api();
1445 let (server_connection, _addr, _buffers) =
1446 api.accept(&server_listener).expect("connection is waiting");
1447 server_connection
1448 });
1449
1450 net.with_context(LOCAL, |ctx| {
1451 let mut api = ctx.tcp_api();
1452 let count = api.disconnect_bound(&IpSocketMatcher::Cookie(SocketCookieMatcher {
1453 cookie: client_socket.socket_cookie().export_value(),
1454 invert: false,
1455 }));
1456 assert_eq!(count, 1);
1457
1458 ctx.core_ctx.with_socket(&client_socket, |s| {
1459 let conn = assert_matches!(
1460 &s.socket_state,
1461 TcpSocketStateInner::Connected {conn, ..} => conn
1462 );
1463
1464 let info = I::get_conn_info(conn);
1465 assert_eq!(info.local_addr.ip.addr().get(), I::TEST_ADDRS.local_ip.get());
1466 assert_eq!(info.remote_addr.ip.addr().get(), I::TEST_ADDRS.remote_ip.get());
1467 assert_eq!(info.local_addr.port, LOCAL_PORT_1);
1468 assert_eq!(info.remote_addr.port, REMOTE_PORT_1);
1469 assert_eq!(info.device, Some(FakeDeviceId.downgrade()));
1470 });
1471 });
1472
1473 net.run_until_idle();
1475
1476 net.with_context(REMOTE, |ctx| {
1477 let mut api = ctx.tcp_api();
1478 assert_matches!(
1479 api.get_socket_error(&server_socket),
1480 Some(ConnectionError::ConnectionReset)
1481 );
1482 });
1483
1484 net.with_context(LOCAL, |ctx| {
1490 let mut api = ctx.tcp_api::<I>();
1491 assert_matches!(
1492 api.connect(
1493 &client_socket,
1494 Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)),
1495 REMOTE_PORT_1
1496 ),
1497 Ok(())
1498 );
1499 });
1500 net.run_until_idle();
1501 net.with_context(REMOTE, |ctx| {
1502 let mut api = ctx.tcp_api();
1503 assert_matches!(api.accept(&server_listener), Err(AcceptError::WouldBlock));
1504 });
1505
1506 net.with_context(LOCAL, |ctx| {
1509 let mut api = ctx.tcp_api();
1510 let s: TcpSocketId<I, _, _> = api.create(Default::default());
1511 api.set_device(&s, Some(FakeDeviceId)).expect("set device should succeed");
1512 api.bind(&s, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_1))
1513 .expect("bind should succeed");
1514 api.connect(&s, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)), REMOTE_PORT_1)
1515 .expect("can connect");
1516
1517 api.close(s);
1520 api.close(client_socket);
1521 });
1522 }
1523
1524 #[ip_test(I)]
1525 fn disconnect_listener<I: TcpTestIpExt>()
1526 where
1527 TcpCoreCtx<FakeDeviceId, TcpBindingsCtx<FakeDeviceId>>: TcpContext<
1528 I,
1529 TcpBindingsCtx<FakeDeviceId>,
1530 SingleStackConverter = I::SingleStackConverter,
1531 DualStackConverter = I::DualStackConverter,
1532 >,
1533 {
1534 set_logger_for_test();
1535
1536 let mut net = FakeTcpNetworkSpec::new_network(
1537 [
1538 (
1539 LOCAL,
1540 TcpCtx::with_core_ctx(TcpCoreCtx::new::<I>(
1541 I::TEST_ADDRS.local_ip,
1542 I::TEST_ADDRS.remote_ip,
1543 )),
1544 ),
1545 (
1546 REMOTE,
1547 TcpCtx::with_core_ctx(TcpCoreCtx::new::<I>(
1548 I::TEST_ADDRS.remote_ip,
1549 I::TEST_ADDRS.local_ip,
1550 )),
1551 ),
1552 ],
1553 |net, meta| {
1554 if net == LOCAL {
1555 alloc::vec![(REMOTE, meta, None)]
1556 } else {
1557 alloc::vec![(LOCAL, meta, None)]
1558 }
1559 },
1560 );
1561
1562 let client_accepted_socket = net.with_context(LOCAL, |ctx| {
1563 let mut api = ctx.tcp_api();
1564 let s: TcpSocketId<I, _, _> = api.create(Default::default());
1565 api.connect(&s, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)), REMOTE_PORT_1)
1566 .expect("can connect");
1567 s
1568 });
1569 let client_unaccepted_socket = net.with_context(LOCAL, |ctx| {
1570 let mut api = ctx.tcp_api();
1571 let s: TcpSocketId<I, _, _> = api.create(Default::default());
1572 api.connect(&s, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)), REMOTE_PORT_1)
1573 .expect("can connect");
1574 s
1575 });
1576 let server_listener = net.with_context(REMOTE, |ctx| {
1577 let mut api = ctx.tcp_api::<I>();
1578 let s = api.create(Default::default());
1579 api.set_device(&s, Some(FakeDeviceId)).expect("set device should succeed");
1581 api.bind(&s, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)), Some(REMOTE_PORT_1))
1582 .expect("bind should succeed");
1583 api.listen(&s, NonZeroUsize::new(2).unwrap()).expect("listen should succeed");
1584 s
1585 });
1586
1587 net.run_until_idle();
1589
1590 net.with_context(REMOTE, |ctx| {
1591 let mut api = ctx.tcp_api();
1592 let _: (TcpSocketId<_, _, _>, _, _) =
1593 api.accept(&server_listener).expect("connection is waiting");
1594
1595 let count = api.disconnect_bound(&IpSocketMatcher::Cookie(SocketCookieMatcher {
1596 cookie: server_listener.socket_cookie().export_value(),
1597 invert: false,
1598 }));
1599 assert_eq!(count, 1);
1600
1601 ctx.core_ctx.with_socket(&server_listener, |s| {
1602 let addr = assert_matches!(
1603 &s.socket_state,
1604 TcpSocketStateInner::Bound(BoundState { addr, .. }) => addr
1605 );
1606 let info = I::get_bound_info(addr);
1607 assert_eq!(
1608 info.addr.expect("address is set").addr().get(),
1609 I::TEST_ADDRS.remote_ip.get(),
1610 );
1611 assert_eq!(info.port, REMOTE_PORT_1);
1612 assert_eq!(info.device, Some(FakeDeviceId.downgrade()));
1613 });
1614
1615 let mut api = ctx.tcp_api::<I>();
1616 api.listen(&server_listener, NonZeroUsize::new(1).unwrap())
1617 .expect("listen should succeed");
1618 });
1619
1620 net.run_until_idle();
1622
1623 net.with_context(LOCAL, |ctx| {
1627 let mut api = ctx.tcp_api();
1628 assert_matches!(api.get_socket_error(&client_accepted_socket), None);
1629 assert_matches!(
1630 api.get_socket_error(&client_unaccepted_socket),
1631 Some(ConnectionError::ConnectionReset)
1632 );
1633 })
1634 }
1635
1636 #[ip_test(I)]
1637 fn disconnect_bound<I: TcpTestIpExt>()
1638 where
1639 TcpCoreCtx<FakeDeviceId, TcpBindingsCtx<FakeDeviceId>>: TcpContext<
1640 I,
1641 TcpBindingsCtx<FakeDeviceId>,
1642 SingleStackConverter = I::SingleStackConverter,
1643 DualStackConverter = I::DualStackConverter,
1644 >,
1645 {
1646 set_logger_for_test();
1647
1648 let mut ctx = TcpCtx::with_core_ctx(TcpCoreCtx::new::<I>(
1649 I::TEST_ADDRS.local_ip,
1650 I::TEST_ADDRS.remote_ip,
1651 ));
1652 let mut api = ctx.tcp_api::<I>();
1653 let s = api.create(Default::default());
1654 api.set_device(&s, Some(FakeDeviceId)).expect("set device should succeed");
1656 api.bind(&s, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_1))
1657 .expect("bind should succeed");
1658
1659 let mut api = ctx.tcp_api::<I>();
1660 let count = api.disconnect_bound(&IpSocketMatcher::Cookie(SocketCookieMatcher {
1661 cookie: s.socket_cookie().export_value(),
1662 invert: false,
1663 }));
1664 assert_eq!(count, 1);
1665
1666 ctx.core_ctx.with_socket(&s, |s| {
1667 let addr = assert_matches!(
1668 &s.socket_state,
1669 TcpSocketStateInner::Bound(BoundState { addr, .. }) => addr
1670 );
1671 let info = I::get_bound_info(addr);
1672 assert_eq!(
1673 info.addr.expect("address is set").addr().get(),
1674 I::TEST_ADDRS.local_ip.get(),
1675 );
1676 assert_eq!(info.port, LOCAL_PORT_1);
1677 assert_eq!(info.device, Some(FakeDeviceId.downgrade()));
1678 });
1679 }
1680
1681 #[ip_test(I)]
1682 fn tcp_info_unbound<I: TcpTestIpExt>()
1683 where
1684 TcpCoreCtx<FakeDeviceId, TcpBindingsCtx<FakeDeviceId>>: TcpContext<
1685 I,
1686 TcpBindingsCtx<FakeDeviceId>,
1687 SingleStackConverter = I::SingleStackConverter,
1688 DualStackConverter = I::DualStackConverter,
1689 >,
1690 {
1691 set_logger_for_test();
1692 let mut ctx = TcpCtx::with_core_ctx(TcpCoreCtx::new::<I>(
1693 I::TEST_ADDRS.local_ip,
1694 I::TEST_ADDRS.remote_ip,
1695 ));
1696 let mut api = ctx.tcp_api::<I>();
1697 let s = api.create(Default::default());
1698 let info = api.get_tcp_info(&s);
1699 assert_eq!(
1700 info,
1701 TcpSocketInfo {
1702 state: netstack3_base::TcpSocketState::Close,
1703 ca_state: CongestionControlState::Open,
1704 rto: None,
1705 rtt: None,
1706 rtt_var: None,
1707 snd_ssthresh: 0,
1708 snd_cwnd: 0,
1709 retransmits: 0,
1710 last_ack_recv: None,
1711 segs_out: 0,
1712 segs_in: 0,
1713 last_data_sent: None,
1714 }
1715 );
1716 }
1717
1718 #[ip_test(I)]
1719 #[test_case(true, netstack3_base::TcpSocketState::Listen; "listen")]
1720 #[test_case(false, netstack3_base::TcpSocketState::Close; "bound")]
1721 fn tcp_info_bound<I: TcpTestIpExt>(
1722 should_listen: bool,
1723 expected_state: netstack3_base::TcpSocketState,
1724 ) where
1725 TcpCoreCtx<FakeDeviceId, TcpBindingsCtx<FakeDeviceId>>: TcpContext<
1726 I,
1727 TcpBindingsCtx<FakeDeviceId>,
1728 SingleStackConverter = I::SingleStackConverter,
1729 DualStackConverter = I::DualStackConverter,
1730 >,
1731 {
1732 set_logger_for_test();
1733 let mut ctx = TcpCtx::with_core_ctx(TcpCoreCtx::new::<I>(
1734 I::TEST_ADDRS.local_ip,
1735 I::TEST_ADDRS.remote_ip,
1736 ));
1737 let mut api = ctx.tcp_api::<I>();
1738 let s = api.create(Default::default());
1739 api.bind(&s, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT_1))
1740 .expect("bind should succeed");
1741 if should_listen {
1742 api.listen(&s, NonZeroUsize::new(1).unwrap()).expect("listen should succeed");
1743 }
1744
1745 let info = api.get_tcp_info(&s);
1746 assert_eq!(
1747 info,
1748 TcpSocketInfo {
1749 state: expected_state,
1750 ca_state: CongestionControlState::Open,
1751 rto: None,
1752 rtt: None,
1753 rtt_var: None,
1754 snd_ssthresh: 0,
1755 snd_cwnd: 0,
1756 retransmits: 0,
1757 last_ack_recv: None,
1758 segs_out: 0,
1759 segs_in: 0,
1760 last_data_sent: None,
1761 }
1762 );
1763 }
1764
1765 #[ip_test(I)]
1766 fn tcp_info_connected<I: TcpTestIpExt>()
1767 where
1768 TcpCoreCtx<FakeDeviceId, TcpBindingsCtx<FakeDeviceId>>: TcpContext<
1769 I,
1770 TcpBindingsCtx<FakeDeviceId>,
1771 SingleStackConverter = I::SingleStackConverter,
1772 DualStackConverter = I::DualStackConverter,
1773 >,
1774 {
1775 set_logger_for_test();
1776
1777 let mut net = FakeTcpNetworkSpec::new_network(
1778 [
1779 (
1780 LOCAL,
1781 TcpCtx::with_core_ctx(TcpCoreCtx::new::<I>(
1782 I::TEST_ADDRS.local_ip,
1783 I::TEST_ADDRS.remote_ip,
1784 )),
1785 ),
1786 (
1787 REMOTE,
1788 TcpCtx::with_core_ctx(TcpCoreCtx::new::<I>(
1789 I::TEST_ADDRS.remote_ip,
1790 I::TEST_ADDRS.local_ip,
1791 )),
1792 ),
1793 ],
1794 |net, meta| {
1795 if net == LOCAL {
1796 vec![(REMOTE, meta, Some(Duration::from_millis(100)))]
1797 } else {
1798 vec![(LOCAL, meta, Some(Duration::from_millis(100)))]
1799 }
1800 },
1801 );
1802
1803 let client_socket = net.with_context(LOCAL, |ctx| {
1804 let mut api = ctx.tcp_api();
1805 let s: TcpSocketId<I, _, _> = api.create(Default::default());
1806 s
1807 });
1808
1809 let server_socket = net.with_context(REMOTE, |ctx| {
1810 let mut api = ctx.tcp_api::<I>();
1811 let s = api.create(Default::default());
1812 api.set_device(&s, Some(FakeDeviceId)).expect("set device should succeed");
1813 api.bind(&s, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)), Some(REMOTE_PORT_1))
1814 .expect("bind should succeed");
1815 api.listen(&s, NonZeroUsize::new(1).unwrap()).expect("listen should succeed");
1816 s
1817 });
1818
1819 net.with_context(LOCAL, |ctx| {
1820 let mut api = ctx.tcp_api();
1821 api.connect(
1822 &client_socket,
1823 Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)),
1824 REMOTE_PORT_1,
1825 )
1826 .expect("should connect");
1827 });
1828
1829 net.run_until_idle();
1830
1831 net.with_context(LOCAL, |ctx| {
1832 let mut api = ctx.tcp_api();
1833 let data = [1u8; 10];
1834 api.with_send_buffer(&client_socket, |buf| {
1835 buf.enqueue_data(&data[..]);
1836 })
1837 .expect("send buffer should be available");
1838 api.do_send(&client_socket);
1839 });
1840
1841 net.run_until_idle();
1842
1843 let accepted_socket = net.with_context(REMOTE, |ctx| {
1844 let mut api = ctx.tcp_api();
1845 let (accepted, _, _) = api.accept(&server_socket).expect("accept should succeed");
1846
1847 let data = [2u8; 10];
1850 api.with_send_buffer(&accepted, |buf| {
1851 buf.enqueue_data(&data[..]);
1852 })
1853 .expect("send buffer should be available");
1854 api.do_send(&accepted);
1855
1856 accepted
1857 });
1858
1859 net.run_until_idle();
1860
1861 net.with_context(LOCAL, |ctx| {
1862 let mut api = ctx.tcp_api();
1863 let info = api.get_tcp_info(&client_socket);
1864
1865 assert_matches!(
1866 info,
1867 TcpSocketInfo {
1868 state: netstack3_base::TcpSocketState::Established,
1869 ca_state: CongestionControlState::Open,
1870 retransmits: 0,
1871 snd_ssthresh: u32::MAX,
1872 segs_out,
1873 segs_in,
1874 snd_cwnd,
1875 rto: Some(rto),
1876 rtt: Some(rtt),
1877 rtt_var: Some(rtt_var),
1878 last_ack_recv: Some(last_ack_recv),
1879 last_data_sent: Some(last_data_sent),
1880 } => {
1881 assert_eq!(segs_out, 4);
1882 assert_eq!(segs_in, 3);
1883 assert_gt!(snd_cwnd, 0);
1884 assert_eq!(rto, Duration::from_millis(500));
1885 assert_eq!(rtt, Duration::from_millis(200));
1886 assert_eq!(rtt_var, Duration::from_millis(75));
1887 assert_eq!(last_ack_recv, FakeInstant::from(Duration::from_millis(1800)));
1888 assert_eq!(last_data_sent, FakeInstant::from(Duration::from_millis(1100)));
1889 }
1890 );
1891 });
1892
1893 net.with_context(REMOTE, |ctx| {
1894 let mut api = ctx.tcp_api();
1895 let info = api.get_tcp_info(&accepted_socket);
1896
1897 assert_matches!(
1898 info,
1899 TcpSocketInfo {
1900 state: netstack3_base::TcpSocketState::Established,
1901 ca_state: CongestionControlState::Open,
1902 retransmits: 0,
1903 snd_ssthresh: u32::MAX,
1904 segs_out,
1905 segs_in,
1906 snd_cwnd,
1907 rto: Some(rto),
1908 rtt: Some(rtt),
1909 rtt_var: Some(rtt_var),
1910 last_ack_recv: Some(last_ack_recv),
1911 last_data_sent: Some(last_data_sent),
1912 } => {
1913 assert_eq!(segs_out, 2);
1914 assert_eq!(segs_in, 3);
1915 assert_gt!(snd_cwnd, 0);
1916 assert_eq!(rto, Duration::from_millis(500));
1917 assert_eq!(rtt, Duration::from_millis(200));
1918 assert_eq!(rtt_var, Duration::from_millis(75));
1919 assert_eq!(last_ack_recv, FakeInstant::from(Duration::from_millis(1200)));
1920 assert_eq!(last_data_sent, FakeInstant::from(Duration::from_millis(1700)));
1921 }
1922 );
1923 });
1924 }
1925}