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