1#![warn(
8 missing_docs,
9 unreachable_patterns,
10 clippy::useless_conversion,
11 clippy::redundant_clone,
12 clippy::precedence
13)]
14
15use fidl_fuchsia_net as fnet;
16use fidl_fuchsia_net_ext::{IntoExt, Marks};
17use fidl_fuchsia_net_matchers as fnet_matchers;
18use fidl_fuchsia_net_matchers_ext as fnet_matchers_ext;
19use fidl_fuchsia_net_sockets as fnet_sockets;
20use fidl_fuchsia_net_tcp as fnet_tcp;
21use fidl_fuchsia_net_udp as fnet_udp;
22use futures::{Stream, TryStreamExt as _};
23use net_types::ip::{self, GenericOverIp, Ip, IpInvariant, Ipv4, Ipv6};
24use thiserror::Error;
25
26#[derive(Debug, Clone, PartialEq, Eq)]
28pub enum IpSocketMatcher {
29 Family(ip::IpVersion),
31 SrcAddr(fnet_matchers_ext::BoundAddress),
33 DstAddr(fnet_matchers_ext::BoundAddress),
35 Proto(fnet_matchers_ext::SocketTransportProtocol),
37 BoundInterface(fnet_matchers_ext::BoundInterface),
40 Cookie(fnet_matchers::SocketCookie),
42 Mark(fnet_matchers_ext::MarkInDomain),
44}
45
46#[derive(Debug, PartialEq, Error)]
49pub enum IpSocketMatcherError {
50 #[error("got unexpected union variant: {0}")]
52 UnknownUnionVariant(u64),
53 #[error("address matcher conversion failure: {0}")]
55 Address(fnet_matchers_ext::BoundAddressError),
56 #[error("protocol matcher conversion failure: {0}")]
59 TransportProtocol(fnet_matchers_ext::SocketTransportProtocolError),
60 #[error("bound interface matcher conversion failure: {0}")]
62 BoundInterface(fnet_matchers_ext::BoundInterfaceError),
63 #[error("mark matcher conversion failure: {0}")]
65 Mark(fnet_matchers_ext::MarkInDomainError),
66}
67
68impl TryFrom<fnet_sockets::IpSocketMatcher> for IpSocketMatcher {
69 type Error = IpSocketMatcherError;
70
71 fn try_from(matcher: fnet_sockets::IpSocketMatcher) -> Result<Self, Self::Error> {
72 match matcher {
73 fnet_sockets::IpSocketMatcher::Family(ip_version) => {
74 Ok(Self::Family(ip_version.into_ext()))
75 }
76 fnet_sockets::IpSocketMatcher::SrcAddr(addr) => {
77 Ok(Self::SrcAddr(addr.try_into().map_err(|e| IpSocketMatcherError::Address(e))?))
78 }
79 fnet_sockets::IpSocketMatcher::DstAddr(addr) => {
80 Ok(Self::DstAddr(addr.try_into().map_err(|e| IpSocketMatcherError::Address(e))?))
81 }
82 fnet_sockets::IpSocketMatcher::Proto(proto) => Ok(Self::Proto(
83 proto.try_into().map_err(|e| IpSocketMatcherError::TransportProtocol(e))?,
84 )),
85 fnet_sockets::IpSocketMatcher::BoundInterface(bound_interface) => {
86 Ok(Self::BoundInterface(
87 bound_interface
88 .try_into()
89 .map_err(|e| IpSocketMatcherError::BoundInterface(e))?,
90 ))
91 }
92 fnet_sockets::IpSocketMatcher::Cookie(cookie) => Ok(Self::Cookie(cookie)),
93 fnet_sockets::IpSocketMatcher::Mark(mark) => {
94 Ok(Self::Mark(mark.try_into().map_err(|e| IpSocketMatcherError::Mark(e))?))
95 }
96 fnet_sockets::IpSocketMatcher::__SourceBreaking { unknown_ordinal } => {
97 Err(IpSocketMatcherError::UnknownUnionVariant(unknown_ordinal))
98 }
99 }
100 }
101}
102
103impl From<IpSocketMatcher> for fnet_sockets::IpSocketMatcher {
104 fn from(value: IpSocketMatcher) -> Self {
105 match value {
106 IpSocketMatcher::Family(ip_version) => {
107 fnet_sockets::IpSocketMatcher::Family(ip_version.into_ext())
108 }
109 IpSocketMatcher::SrcAddr(address) => {
110 fnet_sockets::IpSocketMatcher::SrcAddr(address.into())
111 }
112 IpSocketMatcher::DstAddr(address) => {
113 fnet_sockets::IpSocketMatcher::DstAddr(address.into())
114 }
115 IpSocketMatcher::Proto(socket_transport_protocol) => {
116 fnet_sockets::IpSocketMatcher::Proto(socket_transport_protocol.into())
117 }
118 IpSocketMatcher::BoundInterface(mark) => {
119 fnet_sockets::IpSocketMatcher::BoundInterface(mark.into())
120 }
121 IpSocketMatcher::Cookie(socket_cookie) => {
122 fnet_sockets::IpSocketMatcher::Cookie(socket_cookie)
123 }
124 IpSocketMatcher::Mark(mark) => fnet_sockets::IpSocketMatcher::Mark(mark.into()),
125 }
126 }
127}
128
129#[derive(Debug, PartialEq, Eq, Clone)]
131pub enum IpSocketState {
132 V4(IpSocketStateSpecific<Ipv4>),
134 V6(IpSocketStateSpecific<Ipv6>),
136}
137
138#[derive(Debug, Error, PartialEq)]
140pub enum IpSocketStateError {
141 #[error("missing field: {0}")]
143 MissingField(&'static str),
144 #[error("version mismatch")]
146 VersionMismatch,
147 #[error("transport state error: {0}")]
149 Transport(IpSocketTransportStateError),
150}
151
152impl TryFrom<fnet_sockets::IpSocketState> for IpSocketState {
153 type Error = IpSocketStateError;
154
155 fn try_from(value: fnet_sockets::IpSocketState) -> Result<Self, Self::Error> {
156 fn convert_address<I: Ip>(addr: fnet::IpAddress) -> Result<I::Addr, IpSocketStateError> {
157 I::map_ip::<_, Option<I::Addr>>(
158 IpInvariant(addr.into_ext()),
159 |IpInvariant(addr)| match addr {
160 net_types::ip::IpAddr::V4(addr) => Some(addr),
161 _ => None,
162 },
163 |IpInvariant(addr)| match addr {
164 net_types::ip::IpAddr::V6(addr) => Some(addr),
165 _ => None,
166 },
167 )
168 .ok_or(IpSocketStateError::VersionMismatch)
169 }
170
171 fn to_ip_socket_specific<I: Ip>(
172 src_addr: Option<fnet::IpAddress>,
173 dst_addr: Option<fnet::IpAddress>,
174 cookie: u64,
175 marks: fnet::Marks,
176 transport: fnet_sockets::IpSocketTransportState,
177 ) -> Result<IpSocketStateSpecific<I>, IpSocketStateError> {
178 let src_addr: Option<I::Addr> = src_addr.map(convert_address::<I>).transpose()?;
179 let dst_addr: Option<I::Addr> = dst_addr.map(convert_address::<I>).transpose()?;
180
181 Ok(IpSocketStateSpecific {
182 src_addr,
183 dst_addr,
184 cookie,
185 marks: marks.into(),
186 transport: transport.try_into().map_err(IpSocketStateError::Transport)?,
187 })
188 }
189
190 let fnet_sockets::IpSocketState {
191 family,
192 src_addr,
193 dst_addr,
194 cookie,
195 marks,
196 transport,
197 __source_breaking,
198 } = value;
199
200 let family = family.ok_or(IpSocketStateError::MissingField("family"))?;
201 let cookie = cookie.ok_or(IpSocketStateError::MissingField("cookie"))?;
202 let marks = marks.ok_or(IpSocketStateError::MissingField("marks"))?;
203 let transport = transport.ok_or(IpSocketStateError::MissingField("transport"))?;
204
205 match family {
206 fnet::IpVersion::V4 => Ok(IpSocketState::V4(to_ip_socket_specific(
207 src_addr, dst_addr, cookie, marks, transport,
208 )?)),
209 fnet::IpVersion::V6 => Ok(IpSocketState::V6(to_ip_socket_specific(
210 src_addr, dst_addr, cookie, marks, transport,
211 )?)),
212 }
213 }
214}
215
216impl From<IpSocketState> for fnet_sockets::IpSocketState {
217 fn from(state: IpSocketState) -> Self {
218 match state {
219 IpSocketState::V4(state) => state.into(),
220 IpSocketState::V6(state) => state.into(),
221 }
222 }
223}
224
225#[derive(Debug, PartialEq, Eq, Clone, GenericOverIp)]
228#[generic_over_ip(I, Ip)]
229pub struct IpSocketStateSpecific<I: Ip> {
230 pub src_addr: Option<I::Addr>,
232 pub dst_addr: Option<I::Addr>,
234 pub cookie: u64,
236 pub marks: Marks,
238 pub transport: IpSocketTransportState,
240}
241
242impl<I: Ip> From<IpSocketStateSpecific<I>> for fnet_sockets::IpSocketState {
243 fn from(value: IpSocketStateSpecific<I>) -> Self {
244 let IpSocketStateSpecific { src_addr, dst_addr, cookie, marks, transport } = value;
245
246 fnet_sockets::IpSocketState {
247 family: Some(I::VERSION.into_ext()),
248 src_addr: src_addr.map(|a| net_types::ip::IpAddr::from(a).into_ext()),
249 dst_addr: dst_addr.map(|a| net_types::ip::IpAddr::from(a).into_ext()),
250 cookie: Some(cookie),
251 marks: Some(marks.into()),
252 transport: Some(transport.into()),
253 __source_breaking: fidl::marker::SourceBreaking,
254 }
255 }
256}
257
258#[derive(Debug, PartialEq, Eq, Clone)]
260pub enum IpSocketTransportState {
261 Tcp(IpSocketTcpState),
263 Udp(IpSocketUdpState),
265}
266
267#[derive(Debug, PartialEq, Error)]
269pub enum IpSocketTransportStateError {
270 #[error("tcp validation error: {0}")]
272 Tcp(IpSocketTcpStateError),
273 #[error("udp validation error: {0}")]
275 Udp(IpSocketUdpStateError),
276 #[error("got unexpected union variant: {0}")]
278 UnknownUnionVariant(u64),
279}
280
281impl TryFrom<fnet_sockets::IpSocketTransportState> for IpSocketTransportState {
282 type Error = IpSocketTransportStateError;
283
284 fn try_from(value: fnet_sockets::IpSocketTransportState) -> Result<Self, Self::Error> {
285 match value {
286 fnet_sockets::IpSocketTransportState::Tcp(tcp) => Ok(IpSocketTransportState::Tcp(
287 tcp.try_into().map_err(IpSocketTransportStateError::Tcp)?,
288 )),
289 fnet_sockets::IpSocketTransportState::Udp(udp) => Ok(IpSocketTransportState::Udp(
290 udp.try_into().map_err(IpSocketTransportStateError::Udp)?,
291 )),
292 fnet_sockets::IpSocketTransportState::__SourceBreaking { unknown_ordinal } => {
293 Err(IpSocketTransportStateError::UnknownUnionVariant(unknown_ordinal))
294 }
295 }
296 }
297}
298
299impl From<IpSocketTransportState> for fnet_sockets::IpSocketTransportState {
300 fn from(state: IpSocketTransportState) -> Self {
301 match state {
302 IpSocketTransportState::Tcp(tcp) => {
303 fnet_sockets::IpSocketTransportState::Tcp(tcp.into())
304 }
305 IpSocketTransportState::Udp(udp) => {
306 fnet_sockets::IpSocketTransportState::Udp(udp.into())
307 }
308 }
309 }
310}
311
312#[derive(Debug, PartialEq, Eq, Clone)]
314pub struct IpSocketTcpState {
315 pub src_port: Option<u16>,
317 pub dst_port: Option<u16>,
319 pub state: fnet_tcp::State,
321 pub tcp_info: Option<TcpInfo>,
323}
324
325#[derive(Debug, PartialEq, Error)]
327pub enum IpSocketTcpStateError {
328 #[error("missing field: {0}")]
330 MissingField(&'static str),
331 #[error("tcp info error: {0}")]
333 TcpInfo(TcpInfoError),
334}
335
336impl TryFrom<fnet_sockets::IpSocketTcpState> for IpSocketTcpState {
337 type Error = IpSocketTcpStateError;
338
339 fn try_from(value: fnet_sockets::IpSocketTcpState) -> Result<Self, Self::Error> {
340 let fnet_sockets::IpSocketTcpState {
341 src_port,
342 dst_port,
343 state,
344 tcp_info,
345 __source_breaking,
346 } = value;
347
348 let state = state.ok_or(IpSocketTcpStateError::MissingField("state"))?;
349
350 Ok(IpSocketTcpState {
351 src_port,
352 dst_port,
353 state,
354 tcp_info: tcp_info
355 .map(|t| t.try_into())
356 .transpose()
357 .map_err(|e| IpSocketTcpStateError::TcpInfo(e))?,
358 })
359 }
360}
361
362impl From<IpSocketTcpState> for fnet_sockets::IpSocketTcpState {
363 fn from(state: IpSocketTcpState) -> Self {
364 let IpSocketTcpState { src_port, dst_port, state, tcp_info } = state;
365 fnet_sockets::IpSocketTcpState {
366 src_port,
367 dst_port,
368 state: Some(state),
369 tcp_info: tcp_info.map(Into::into),
370 __source_breaking: fidl::marker::SourceBreaking,
371 }
372 }
373}
374
375#[derive(Debug, PartialEq, Eq, Clone)]
377pub struct TcpInfo {
378 pub state: fnet_tcp::State,
380 pub ca_state: fnet_tcp::CongestionControlState,
382 pub rto_usec: Option<u32>,
384 pub tcpi_last_data_sent_msec: Option<u32>,
386 pub tcpi_last_ack_recv_msec: Option<u32>,
388 pub rtt_usec: Option<u32>,
390 pub rtt_var_usec: Option<u32>,
392 pub snd_ssthresh: u32,
394 pub snd_cwnd: u32,
396 pub tcpi_total_retrans: u32,
398 pub tcpi_segs_out: u64,
400 pub tcpi_segs_in: u64,
402 pub reorder_seen: bool,
404}
405
406#[derive(Debug, PartialEq, Error)]
408pub enum TcpInfoError {
409 #[error("missing field: {0}")]
411 MissingField(&'static str),
412}
413
414impl TryFrom<fnet_tcp::Info> for TcpInfo {
415 type Error = TcpInfoError;
416
417 fn try_from(value: fnet_tcp::Info) -> Result<Self, Self::Error> {
418 let fnet_tcp::Info {
419 state,
420 ca_state,
421 rto_usec,
422 tcpi_last_data_sent_msec,
423 tcpi_last_ack_recv_msec,
424 rtt_usec,
425 rtt_var_usec,
426 snd_ssthresh,
427 snd_cwnd,
428 tcpi_total_retrans,
429 tcpi_segs_out,
430 tcpi_segs_in,
431 reorder_seen,
432 __source_breaking,
433 } = value;
434
435 Ok(TcpInfo {
436 state: state.ok_or(TcpInfoError::MissingField("state"))?,
437 ca_state: ca_state.ok_or(TcpInfoError::MissingField("ca_state"))?,
438 rto_usec,
439 tcpi_last_data_sent_msec,
440 tcpi_last_ack_recv_msec,
441 rtt_usec,
442 rtt_var_usec,
443 snd_ssthresh: snd_ssthresh.ok_or(TcpInfoError::MissingField("snd_ssthresh"))?,
444 snd_cwnd: snd_cwnd.ok_or(TcpInfoError::MissingField("snd_cwnd"))?,
445 tcpi_total_retrans: tcpi_total_retrans
446 .ok_or(TcpInfoError::MissingField("tcpi_total_retrans"))?,
447 tcpi_segs_out: tcpi_segs_out.ok_or(TcpInfoError::MissingField("tcpi_segs_out"))?,
448 tcpi_segs_in: tcpi_segs_in.ok_or(TcpInfoError::MissingField("tcpi_segs_in"))?,
449 reorder_seen: reorder_seen.ok_or(TcpInfoError::MissingField("reorder_seen"))?,
450 })
451 }
452}
453
454impl From<TcpInfo> for fnet_tcp::Info {
455 fn from(info: TcpInfo) -> Self {
456 let TcpInfo {
457 state,
458 ca_state,
459 rto_usec,
460 tcpi_last_data_sent_msec,
461 tcpi_last_ack_recv_msec,
462 rtt_usec,
463 rtt_var_usec,
464 snd_ssthresh,
465 snd_cwnd,
466 tcpi_total_retrans,
467 tcpi_segs_out,
468 tcpi_segs_in,
469 reorder_seen,
470 } = info;
471 fnet_tcp::Info {
472 state: Some(state),
473 ca_state: Some(ca_state),
474 rto_usec: rto_usec,
475 tcpi_last_data_sent_msec,
476 tcpi_last_ack_recv_msec,
477 rtt_usec: rtt_usec,
478 rtt_var_usec: rtt_var_usec,
479 snd_ssthresh: Some(snd_ssthresh),
480 snd_cwnd: Some(snd_cwnd),
481 tcpi_total_retrans: Some(tcpi_total_retrans),
482 tcpi_segs_out: Some(tcpi_segs_out),
483 tcpi_segs_in: Some(tcpi_segs_in),
484 reorder_seen: Some(reorder_seen),
485 __source_breaking: fidl::marker::SourceBreaking,
486 }
487 }
488}
489
490#[derive(Debug, PartialEq, Eq, Clone)]
492pub struct IpSocketUdpState {
493 pub src_port: Option<u16>,
495 pub dst_port: Option<u16>,
497 pub state: fnet_udp::State,
499}
500
501#[derive(Debug, PartialEq, Error)]
503pub enum IpSocketUdpStateError {
504 #[error("missing field: {0}")]
506 MissingField(&'static str),
507}
508
509impl TryFrom<fnet_sockets::IpSocketUdpState> for IpSocketUdpState {
510 type Error = IpSocketUdpStateError;
511
512 fn try_from(value: fnet_sockets::IpSocketUdpState) -> Result<Self, Self::Error> {
513 let fnet_sockets::IpSocketUdpState { src_port, dst_port, state, __source_breaking } = value;
514
515 let state = state.ok_or(IpSocketUdpStateError::MissingField("state"))?;
516
517 Ok(IpSocketUdpState { src_port, dst_port, state })
518 }
519}
520
521impl From<IpSocketUdpState> for fnet_sockets::IpSocketUdpState {
522 fn from(state: IpSocketUdpState) -> Self {
523 let IpSocketUdpState { src_port, dst_port, state } = state;
524 fnet_sockets::IpSocketUdpState {
525 src_port,
526 dst_port,
527 state: Some(state),
528 __source_breaking: fidl::marker::SourceBreaking,
529 }
530 }
531}
532
533#[derive(Debug, Error)]
535pub enum IterateIpError {
536 #[error("invalid matcher at position {0}")]
538 InvalidMatcher(usize),
539 #[error("unknown ordinal on Diagnostics.IterateIp call: {0}")]
541 UnknownOrdinal(u64),
542 #[error("fidl error during Diagnostics.IterateIp call: {0}")]
545 Fidl(fidl::Error),
546}
547
548impl From<fidl::Error> for IterateIpError {
549 fn from(e: fidl::Error) -> Self {
550 IterateIpError::Fidl(e)
551 }
552}
553
554#[derive(Debug, Error)]
556pub enum IpIteratorError {
557 #[error("received empty batch of sockets")]
559 EmptyBatch,
560 #[error("fidl error during Diagnostics.IterateIp call: {0}")]
563 Fidl(fidl::Error),
564 #[error("error converting socket state: {0}")]
566 Conversion(IpSocketStateError),
567}
568
569impl From<fidl::Error> for IpIteratorError {
570 fn from(e: fidl::Error) -> Self {
571 IpIteratorError::Fidl(e)
572 }
573}
574
575pub async fn iterate_ip<M, I>(
582 diagnostics: &fnet_sockets::DiagnosticsProxy,
583 extensions: fnet_sockets::Extensions,
584 matchers: M,
585) -> Result<impl Stream<Item = Result<IpSocketState, IpIteratorError>>, IterateIpError>
586where
587 M: IntoIterator<Item = I>,
588 I: Into<fnet_sockets::IpSocketMatcher>,
589{
590 let (proxy, server_end) = fidl::endpoints::create_proxy::<fnet_sockets::IpIteratorMarker>();
591 match diagnostics
592 .iterate_ip(
593 server_end,
594 extensions,
595 &matchers.into_iter().map(Into::into).collect::<Vec<_>>()[..],
596 )
597 .await?
598 {
599 fnet_sockets::IterateIpResult::Ok(_empty) => Ok(()),
600 fnet_sockets::IterateIpResult::InvalidMatcher(fnet_sockets::InvalidMatcher { index }) => {
601 Err(IterateIpError::InvalidMatcher(index as usize))
602 }
603 fnet_sockets::IterateIpResult::__SourceBreaking { unknown_ordinal } => {
604 Err(IterateIpError::UnknownOrdinal(unknown_ordinal))
605 }
606 }?;
607
608 Ok(futures::stream::try_unfold((proxy, true), |(proxy, has_more)| async move {
609 if !has_more {
610 return Ok(None);
611 }
612
613 let (batch, has_more) = proxy.next().await?;
614 if batch.is_empty() && has_more {
615 Err(IpIteratorError::EmptyBatch)
616 } else {
617 Ok(Some((
618 futures::stream::iter(
619 batch
620 .into_iter()
621 .map(|s| s.try_into().map_err(|e| IpIteratorError::Conversion(e))),
622 ),
623 (proxy, has_more),
624 )))
625 }
626 })
627 .try_flatten())
628}
629
630#[derive(Debug, Error)]
632pub enum DisconnectIpError {
633 #[error("invalid matcher at position {0}")]
635 InvalidMatcher(usize),
636 #[error("matchers were unconstrained")]
638 UnconstrainedMatchers,
639 #[error("unknown ordinal on Control.DisconnectIp call: {0}")]
641 UnknownOrdinal(u64),
642 #[error("fidl error during Control.DisconnectIp call: {0}")]
645 Fidl(fidl::Error),
646}
647
648pub async fn disconnect_ip<M, I>(
650 control: &fnet_sockets::ControlProxy,
651 matchers: M,
652) -> Result<usize, DisconnectIpError>
653where
654 M: IntoIterator<Item = I>,
655 I: Into<fnet_sockets::IpSocketMatcher>,
656{
657 match control
658 .disconnect_ip(&fnet_sockets::ControlDisconnectIpRequest {
659 matchers: Some(matchers.into_iter().map(Into::into).collect()),
660 __source_breaking: fidl::marker::SourceBreaking,
661 })
662 .await
663 {
664 Ok(r) => match r {
665 fnet_sockets::DisconnectIpResult::Ok(fnet_sockets::DisconnectIpResponse {
666 disconnected,
667 }) => {
668 Ok(disconnected.try_into().unwrap())
670 }
671 fnet_sockets::DisconnectIpResult::InvalidMatcher(fnet_sockets::InvalidMatcher {
672 index,
673 }) => {
674 Err(DisconnectIpError::InvalidMatcher(index.try_into().unwrap()))
676 }
677 fnet_sockets::DisconnectIpResult::UnconstrainedMatchers(fnet_sockets::Empty) => {
678 Err(DisconnectIpError::UnconstrainedMatchers)
679 }
680 fnet_sockets::DisconnectIpResult::__SourceBreaking { unknown_ordinal } => {
681 Err(DisconnectIpError::UnknownOrdinal(unknown_ordinal))
682 }
683 },
684 Err(e) => Err(DisconnectIpError::Fidl(e)),
685 }
686}
687
688#[cfg(test)]
689mod tests {
690 use super::*;
691
692 use std::num::NonZeroU64;
693
694 use assert_matches::assert_matches;
695 use fidl_fuchsia_net as fnet;
696 use fidl_fuchsia_net_tcp as fnet_tcp;
697 use futures::{FutureExt as _, StreamExt as _, future, pin_mut};
698 use net_declare::{fidl_ip, fidl_subnet, net_ip_v4, net_ip_v6};
699 use test_case::test_case;
700
701 #[test_case(
702 fnet_sockets::IpSocketMatcher::Family(fnet::IpVersion::V4),
703 IpSocketMatcher::Family(ip::IpVersion::V4);
704 "FamilyIpv4"
705 )]
706 #[test_case(
707 fnet_sockets::IpSocketMatcher::Family(fnet::IpVersion::V6),
708 IpSocketMatcher::Family(ip::IpVersion::V6);
709 "FamilyIpv6"
710 )]
711 #[test_case(
712 fnet_sockets::IpSocketMatcher::SrcAddr(fnet_matchers::BoundAddress::Bound(
713 fnet_matchers::Address {
714 matcher: fnet_matchers::AddressMatcherType::Subnet(fidl_subnet!("192.0.2.0/24")),
715 invert: true,
716 }
717 )),
718 IpSocketMatcher::SrcAddr(fnet_matchers_ext::BoundAddress::Bound(
719 fnet_matchers_ext::Address {
720 matcher: fnet_matchers_ext::AddressMatcherType::Subnet(
721 fnet_matchers_ext::Subnet::try_from(fidl_subnet!("192.0.2.0/24")).unwrap()
722 ),
723 invert: true,
724 }
725 ));
726 "SrcAddr"
727 )]
728 #[test_case(
729 fnet_sockets::IpSocketMatcher::DstAddr(fnet_matchers::BoundAddress::Bound(
730 fnet_matchers::Address {
731 matcher: fnet_matchers::AddressMatcherType::Subnet(fidl_subnet!("2001:db8::/32")),
732 invert: false,
733 }
734 )),
735 IpSocketMatcher::DstAddr(fnet_matchers_ext::BoundAddress::Bound(
736 fnet_matchers_ext::Address {
737 matcher: fnet_matchers_ext::AddressMatcherType::Subnet(
738 fnet_matchers_ext::Subnet::try_from(fidl_subnet!("2001:db8::/32")).unwrap()
739 ),
740 invert: false,
741 }
742 ));
743 "DstAddr"
744 )]
745 #[test_case(
746 fnet_sockets::IpSocketMatcher::Proto(fnet_matchers::SocketTransportProtocol::Tcp(
747 fnet_matchers::TcpSocket::Empty(fnet_matchers::Empty)
748 )),
749 IpSocketMatcher::Proto(fnet_matchers_ext::SocketTransportProtocol::Tcp(
750 fnet_matchers_ext::TcpSocket::Empty
751 ));
752 "ProtoTcp"
753 )]
754 #[test_case(
755 fnet_sockets::IpSocketMatcher::Proto(fnet_matchers::SocketTransportProtocol::Udp(
756 fnet_matchers::UdpSocket::Empty(fnet_matchers::Empty)
757 )),
758 IpSocketMatcher::Proto(fnet_matchers_ext::SocketTransportProtocol::Udp(
759 fnet_matchers_ext::UdpSocket::Empty
760 ));
761 "ProtoUdp"
762 )]
763 #[test_case(
764 fnet_sockets::IpSocketMatcher::BoundInterface(fnet_matchers::BoundInterface::Unbound(
765 fnet_matchers::Empty
766 )),
767 IpSocketMatcher::BoundInterface(fnet_matchers_ext::BoundInterface::Unbound);
768 "BoundInterfaceUnbound"
769 )]
770 #[test_case(
771 fnet_sockets::IpSocketMatcher::BoundInterface(fnet_matchers::BoundInterface::Bound(
772 fnet_matchers::Interface::Id(1)
773 )),
774 IpSocketMatcher::BoundInterface(fnet_matchers_ext::BoundInterface::Bound(
775 fnet_matchers_ext::Interface::Id(NonZeroU64::new(1).unwrap())
776 ));
777 "BoundInterfaceBound"
778 )]
779 #[test_case(
780 fnet_sockets::IpSocketMatcher::Cookie(fnet_matchers::SocketCookie {
781 cookie: 12345,
782 invert: false,
783 }),
784 IpSocketMatcher::Cookie(fnet_matchers::SocketCookie {
785 cookie: 12345,
786 invert: false,
787 });
788 "Cookie"
789 )]
790 #[test_case(
791 fnet_sockets::IpSocketMatcher::Mark(fnet_matchers::MarkInDomain {
792 domain: fnet::MarkDomain::Mark1,
793 mark: fnet_matchers::Mark::Unmarked(fnet_matchers::Unmarked),
794 }),
795 IpSocketMatcher::Mark(fnet_matchers_ext::MarkInDomain {
796 domain: fnet::MarkDomain::Mark1,
797 mark: fnet_matchers_ext::Mark::Unmarked,
798 });
799 "Mark"
800 )]
801 #[test_case(
802 fnet_sockets::IpSocketMatcher::SrcAddr(fnet_matchers::BoundAddress::Unbound(fnet_matchers::Empty)),
803 IpSocketMatcher::SrcAddr(fnet_matchers_ext::BoundAddress::Unbound);
804 "SrcAddrUnbound"
805 )]
806 #[test_case(
807 fnet_sockets::IpSocketMatcher::DstAddr(fnet_matchers::BoundAddress::Unbound(fnet_matchers::Empty)),
808 IpSocketMatcher::DstAddr(fnet_matchers_ext::BoundAddress::Unbound);
809 "DstAddrUnbound"
810 )]
811 #[test_case(
812 fnet_sockets::IpSocketMatcher::Proto(fnet_matchers::SocketTransportProtocol::Tcp(
813 fnet_matchers::TcpSocket::SrcPort(fnet_matchers::BoundPort::Unbound(fnet_matchers::Empty))
814 )),
815 IpSocketMatcher::Proto(fnet_matchers_ext::SocketTransportProtocol::Tcp(
816 fnet_matchers_ext::TcpSocket::SrcPort(fnet_matchers_ext::BoundPort::Unbound)
817 ));
818 "ProtoTcpSrcPortUnbound"
819 )]
820 #[test_case(
821 fnet_sockets::IpSocketMatcher::Proto(fnet_matchers::SocketTransportProtocol::Tcp(
822 fnet_matchers::TcpSocket::DstPort(fnet_matchers::BoundPort::Unbound(fnet_matchers::Empty))
823 )),
824 IpSocketMatcher::Proto(fnet_matchers_ext::SocketTransportProtocol::Tcp(
825 fnet_matchers_ext::TcpSocket::DstPort(fnet_matchers_ext::BoundPort::Unbound)
826 ));
827 "ProtoTcpDstPortUnbound"
828 )]
829 #[test_case(
830 fnet_sockets::IpSocketMatcher::Proto(fnet_matchers::SocketTransportProtocol::Udp(
831 fnet_matchers::UdpSocket::SrcPort(fnet_matchers::BoundPort::Unbound(fnet_matchers::Empty))
832 )),
833 IpSocketMatcher::Proto(fnet_matchers_ext::SocketTransportProtocol::Udp(
834 fnet_matchers_ext::UdpSocket::SrcPort(fnet_matchers_ext::BoundPort::Unbound)
835 ));
836 "ProtoUdpSrcPortUnbound"
837 )]
838 #[test_case(
839 fnet_sockets::IpSocketMatcher::Proto(fnet_matchers::SocketTransportProtocol::Udp(
840 fnet_matchers::UdpSocket::DstPort(fnet_matchers::BoundPort::Unbound(fnet_matchers::Empty))
841 )),
842 IpSocketMatcher::Proto(fnet_matchers_ext::SocketTransportProtocol::Udp(
843 fnet_matchers_ext::UdpSocket::DstPort(fnet_matchers_ext::BoundPort::Unbound)
844 ));
845 "ProtoUdpDstPortUnbound"
846 )]
847 #[test_case(
848 fnet_tcp::Info {
849 state: Some(fnet_tcp::State::Established),
850 ca_state: Some(fnet_tcp::CongestionControlState::Open),
851 rto_usec: Some(1),
852 tcpi_last_data_sent_msec: Some(2),
853 tcpi_last_ack_recv_msec: Some(3),
854 rtt_usec: Some(4),
855 rtt_var_usec: Some(5),
856 snd_ssthresh: Some(6),
857 snd_cwnd: Some(7),
858 tcpi_total_retrans: Some(8),
859 tcpi_segs_out: Some(9),
860 tcpi_segs_in: Some(10),
861 reorder_seen: Some(true),
862 __source_breaking: fidl::marker::SourceBreaking,
863 },
864 TcpInfo {
865 state: fnet_tcp::State::Established,
866 ca_state: fnet_tcp::CongestionControlState::Open,
867 rto_usec: Some(1),
868 tcpi_last_data_sent_msec: Some(2),
869 tcpi_last_ack_recv_msec: Some(3),
870 rtt_usec: Some(4),
871 rtt_var_usec: Some(5),
872 snd_ssthresh: 6,
873 snd_cwnd: 7,
874 tcpi_total_retrans: 8,
875 tcpi_segs_out: 9,
876 tcpi_segs_in: 10,
877 reorder_seen: true,
878 };
879 "TcpInfo"
880 )]
881 #[test_case(
882 fnet_sockets::IpSocketState {
883 family: Some(fnet::IpVersion::V4),
884 src_addr: Some(fidl_ip!("192.168.1.1")),
885 dst_addr: Some(fidl_ip!("192.168.1.2")),
886 cookie: Some(1234),
887 marks: Some(fnet::Marks {
888 mark_1: Some(1111),
889 mark_2: None,
890 __source_breaking: fidl::marker::SourceBreaking,
891 }),
892 transport: Some(fnet_sockets::IpSocketTransportState::Tcp(
893 fnet_sockets::IpSocketTcpState {
894 src_port: Some(1111),
895 dst_port: Some(2222),
896 state: Some(fnet_tcp::State::Established),
897 tcp_info: None,
898 __source_breaking: fidl::marker::SourceBreaking,
899 },
900 )),
901 __source_breaking: fidl::marker::SourceBreaking,
902 },
903 IpSocketState::V4(IpSocketStateSpecific {
904 src_addr: Some(net_ip_v4!("192.168.1.1")),
905 dst_addr: Some(net_ip_v4!("192.168.1.2")),
906 cookie: 1234,
907 marks: fnet::Marks {
908 mark_1: Some(1111),
909 mark_2: None,
910 __source_breaking: fidl::marker::SourceBreaking,
911 }.into(),
912 transport: IpSocketTransportState::Tcp(IpSocketTcpState {
913 src_port: Some(1111),
914 dst_port: Some(2222),
915 state: fnet_tcp::State::Established,
916 tcp_info: None,
917 }),
918 });
919 "IpSocketStateV4"
920 )]
921 #[test_case(
922 fnet_sockets::IpSocketState {
923 family: Some(fnet::IpVersion::V6),
924 src_addr: Some(fidl_ip!("2001:db8::1")),
925 dst_addr: Some(fidl_ip!("2001:db8::2")),
926 cookie: Some(1234),
927 marks: Some(fnet::Marks {
928 mark_1: Some(1111),
929 mark_2: None,
930 __source_breaking: fidl::marker::SourceBreaking,
931 }),
932 transport: Some(fnet_sockets::IpSocketTransportState::Udp(
933 fnet_sockets::IpSocketUdpState {
934 src_port: Some(3333),
935 dst_port: Some(4444),
936 state: Some(fnet_udp::State::Connected),
937 __source_breaking: fidl::marker::SourceBreaking,
938 },
939 )),
940 __source_breaking: fidl::marker::SourceBreaking,
941 },
942 IpSocketState::V6(IpSocketStateSpecific {
943 src_addr: Some(net_ip_v6!("2001:db8::1")),
944 dst_addr: Some(net_ip_v6!("2001:db8::2")),
945 cookie: 1234,
946 marks: fnet::Marks {
947 mark_1: Some(1111),
948 mark_2: None,
949 __source_breaking: fidl::marker::SourceBreaking,
950 }.into(),
951 transport: IpSocketTransportState::Udp(IpSocketUdpState {
952 src_port: Some(3333),
953 dst_port: Some(4444),
954 state: fnet_udp::State::Connected,
955 }),
956 });
957 "IpSocketStateV6"
958 )]
959 fn convert_from_fidl_and_back<F, E>(fidl_type: F, local_type: E)
960 where
961 E: TryFrom<F> + Clone + std::fmt::Debug + PartialEq,
962 <E as TryFrom<F>>::Error: std::fmt::Debug + PartialEq,
963 F: From<E> + Clone + std::fmt::Debug + PartialEq,
964 {
965 assert_eq!(fidl_type.clone().try_into(), Ok(local_type.clone()));
966 assert_eq!(<_ as Into<F>>::into(local_type), fidl_type);
967 }
968
969 #[test_case(
970 fnet_sockets::IpSocketMatcher::__SourceBreaking { unknown_ordinal: 100 } =>
971 Err(IpSocketMatcherError::UnknownUnionVariant(100));
972 "UnknownUnionVariant"
973 )]
974 #[test_case(
975 fnet_sockets::IpSocketMatcher::SrcAddr(fnet_matchers::BoundAddress::Bound(
976 fnet_matchers::Address {
977 matcher: fnet_matchers::AddressMatcherType::__SourceBreaking { unknown_ordinal: 100 },
978 invert: false,
979 }
980 )) => Err(IpSocketMatcherError::Address(fnet_matchers_ext::BoundAddressError::Address(
981 fnet_matchers_ext::AddressError::AddressMatcherType(
982 fnet_matchers_ext::AddressMatcherTypeError::UnknownUnionVariant
983 )
984 )));
985 "AddressError"
986 )]
987 #[test_case(
988 fnet_sockets::IpSocketMatcher::Proto(
989 fnet_matchers::SocketTransportProtocol::__SourceBreaking { unknown_ordinal: 100 }
990 ) => Err(IpSocketMatcherError::TransportProtocol(
991 fnet_matchers_ext::SocketTransportProtocolError::UnknownUnionVariant(100)
992 ));
993 "TransportProtocolError"
994 )]
995 #[test_case(
996 fnet_sockets::IpSocketMatcher::BoundInterface(
997 fnet_matchers::BoundInterface::__SourceBreaking { unknown_ordinal: 100 }
998 ) => Err(IpSocketMatcherError::BoundInterface(
999 fnet_matchers_ext::BoundInterfaceError::UnknownUnionVariant(100)
1000 ));
1001 "BoundInterfaceError"
1002 )]
1003 #[test_case(
1004 fnet_sockets::IpSocketMatcher::Mark(fnet_matchers::MarkInDomain {
1005 domain: fnet::MarkDomain::Mark1,
1006 mark: fnet_matchers::Mark::__SourceBreaking { unknown_ordinal: 100 },
1007 }) => Err(IpSocketMatcherError::Mark(
1008 fnet_matchers_ext::MarkInDomainError::Mark(
1009 fnet_matchers_ext::MarkError::UnknownUnionVariant(100)
1010 )
1011 ));
1012 "MarkError"
1013 )]
1014 fn ip_socket_matcher_try_from_error(
1015 fidl: fnet_sockets::IpSocketMatcher,
1016 ) -> Result<IpSocketMatcher, IpSocketMatcherError> {
1017 IpSocketMatcher::try_from(fidl)
1018 }
1019
1020 #[test_case(
1021 fnet_sockets::IpSocketState {
1022 family: None,
1023 src_addr: Some(fidl_ip!("192.168.1.1")),
1024 dst_addr: Some(fidl_ip!("192.168.1.2")),
1025 cookie: Some(1234),
1026 marks: Some(fnet::Marks {
1027 mark_1: Some(1111),
1028 mark_2: None,
1029 __source_breaking: fidl::marker::SourceBreaking,
1030 }),
1031 transport: Some(fnet_sockets::IpSocketTransportState::Tcp(
1032 fnet_sockets::IpSocketTcpState {
1033 src_port: Some(1111),
1034 dst_port: Some(2222),
1035 state: Some(fnet_tcp::State::Established),
1036 tcp_info: None,
1037 __source_breaking: fidl::marker::SourceBreaking,
1038 },
1039 )),
1040 __source_breaking: fidl::marker::SourceBreaking,
1041 } => Err(IpSocketStateError::MissingField("family"));
1042 "MissingFamily"
1043 )]
1044 #[test_case(
1045 fnet_sockets::IpSocketState {
1046 family: Some(fnet::IpVersion::V4),
1047 src_addr: Some(fidl_ip!("192.168.1.1")),
1048 dst_addr: Some(fidl_ip!("192.168.1.2")),
1049 cookie: None,
1050 marks: Some(fnet::Marks {
1051 mark_1: Some(1111),
1052 mark_2: None,
1053 __source_breaking: fidl::marker::SourceBreaking,
1054 }),
1055 transport: Some(fnet_sockets::IpSocketTransportState::Tcp(
1056 fnet_sockets::IpSocketTcpState {
1057 src_port: Some(1111),
1058 dst_port: Some(2222),
1059 state: Some(fnet_tcp::State::Established),
1060 tcp_info: None,
1061 __source_breaking: fidl::marker::SourceBreaking,
1062 },
1063 )),
1064 __source_breaking: fidl::marker::SourceBreaking,
1065 } => Err(IpSocketStateError::MissingField("cookie"));
1066 "MissingCookie"
1067 )]
1068 #[test_case(
1069 fnet_sockets::IpSocketState {
1070 family: Some(fnet::IpVersion::V4),
1071 src_addr: Some(fidl_ip!("192.168.1.1")),
1072 dst_addr: Some(fidl_ip!("192.168.1.2")),
1073 cookie: Some(1234),
1074 marks: None,
1075 transport: Some(fnet_sockets::IpSocketTransportState::Tcp(
1076 fnet_sockets::IpSocketTcpState {
1077 src_port: Some(1111),
1078 dst_port: Some(2222),
1079 state: Some(fnet_tcp::State::Established),
1080 tcp_info: None,
1081 __source_breaking: fidl::marker::SourceBreaking,
1082 },
1083 )),
1084 __source_breaking: fidl::marker::SourceBreaking,
1085 } => Err(IpSocketStateError::MissingField("marks"));
1086 "MissingMarks"
1087 )]
1088 #[test_case(
1089 fnet_sockets::IpSocketState {
1090 family: Some(fnet::IpVersion::V4),
1091 src_addr: Some(fidl_ip!("192.168.1.1")),
1092 dst_addr: Some(fidl_ip!("192.168.1.2")),
1093 cookie: Some(1234),
1094 marks: Some(fnet::Marks {
1095 mark_1: Some(1111),
1096 mark_2: None,
1097 __source_breaking: fidl::marker::SourceBreaking,
1098 }),
1099 transport: None,
1100 __source_breaking: fidl::marker::SourceBreaking,
1101 } => Err(IpSocketStateError::MissingField("transport"));
1102 "MissingTransport"
1103 )]
1104 #[test_case(
1105 fnet_sockets::IpSocketState {
1106 family: Some(fnet::IpVersion::V4),
1107 src_addr: Some(fidl_ip!("192.168.1.1")),
1108 dst_addr: Some(fidl_ip!("2001:db8::2")),
1109 cookie: Some(1234),
1110 marks: Some(fnet::Marks {
1111 mark_1: Some(1111),
1112 mark_2: None,
1113 __source_breaking: fidl::marker::SourceBreaking,
1114 }),
1115 transport: Some(fnet_sockets::IpSocketTransportState::Tcp(
1116 fnet_sockets::IpSocketTcpState {
1117 src_port: Some(1111),
1118 dst_port: Some(2222),
1119 state: Some(fnet_tcp::State::Established),
1120 tcp_info: None,
1121 __source_breaking: fidl::marker::SourceBreaking,
1122 },
1123 )),
1124 __source_breaking: fidl::marker::SourceBreaking,
1125 } => Err(IpSocketStateError::VersionMismatch);
1126 "VersionMismatchV4"
1127 )]
1128 #[test_case(
1129 fnet_sockets::IpSocketState {
1130 family: Some(fnet::IpVersion::V6),
1131 src_addr: Some(fidl_ip!("192.168.1.1")),
1132 dst_addr: Some(fidl_ip!("2001:db8::2")),
1133 cookie: Some(1234),
1134 marks: Some(fnet::Marks {
1135 mark_1: Some(1111),
1136 mark_2: None,
1137 __source_breaking: fidl::marker::SourceBreaking,
1138 }),
1139 transport: Some(fnet_sockets::IpSocketTransportState::Tcp(
1140 fnet_sockets::IpSocketTcpState {
1141 src_port: Some(1111),
1142 dst_port: Some(2222),
1143 state: Some(fnet_tcp::State::Established),
1144 tcp_info: None,
1145 __source_breaking: fidl::marker::SourceBreaking,
1146 },
1147 )),
1148 __source_breaking: fidl::marker::SourceBreaking,
1149 } => Err(IpSocketStateError::VersionMismatch);
1150 "VersionMismatchV6"
1151 )]
1152 #[test_case(
1153 fnet_sockets::IpSocketState {
1154 family: Some(fnet::IpVersion::V4),
1155 src_addr: Some(fidl_ip!("192.168.1.1")),
1156 dst_addr: Some(fidl_ip!("192.168.1.2")),
1157 cookie: Some(1234),
1158 marks: Some(fnet::Marks {
1159 mark_1: Some(1111),
1160 mark_2: None,
1161 __source_breaking: fidl::marker::SourceBreaking,
1162 }),
1163 transport: Some(fnet_sockets::IpSocketTransportState::Tcp(
1164 fnet_sockets::IpSocketTcpState {
1165 src_port: Some(1111),
1166 dst_port: Some(2222),
1167 state: None,
1168 tcp_info: None,
1169 __source_breaking: fidl::marker::SourceBreaking,
1170 },
1171 )),
1172 __source_breaking: fidl::marker::SourceBreaking,
1173 } => Err(IpSocketStateError::Transport(IpSocketTransportStateError::Tcp(
1174 IpSocketTcpStateError::MissingField("state"),
1175 )));
1176 "MissingTcpState"
1177 )]
1178 #[test_case(
1179 fnet_sockets::IpSocketState {
1180 family: Some(fnet::IpVersion::V6),
1181 src_addr: Some(fidl_ip!("2001:db8::1")),
1182 dst_addr: Some(fidl_ip!("2001:db8::2")),
1183 cookie: Some(1234),
1184 marks: Some(fnet::Marks {
1185 mark_1: Some(1111),
1186 mark_2: None,
1187 __source_breaking: fidl::marker::SourceBreaking,
1188 }),
1189 transport: Some(fnet_sockets::IpSocketTransportState::Udp(
1190 fnet_sockets::IpSocketUdpState {
1191 src_port: Some(3333),
1192 dst_port: Some(4444),
1193 state: None,
1194 __source_breaking: fidl::marker::SourceBreaking,
1195 },
1196 )),
1197 __source_breaking: fidl::marker::SourceBreaking,
1198 } => Err(IpSocketStateError::Transport(IpSocketTransportStateError::Udp(
1199 IpSocketUdpStateError::MissingField("state"),
1200 )));
1201 "MissingUdpState"
1202 )]
1203 fn ip_socket_state_try_from_error(
1204 fidl: fnet_sockets::IpSocketState,
1205 ) -> Result<IpSocketState, IpSocketStateError> {
1206 IpSocketState::try_from(fidl)
1207 }
1208
1209 #[fuchsia_async::run_singlethreaded(test)]
1210 async fn iterate_ip_diagnostics_iterate_ip_error() {
1211 async fn serve_matcher_error(req: fnet_sockets::DiagnosticsRequest) {
1212 match req {
1213 fnet_sockets::DiagnosticsRequest::IterateIp {
1214 s: _,
1215 extensions: _,
1216 matchers: _,
1217 responder,
1218 } => responder
1219 .send(&fnet_sockets::IterateIpResult::InvalidMatcher(
1220 fnet_sockets::InvalidMatcher { index: 0 },
1221 ))
1222 .unwrap(),
1223 };
1224 }
1225
1226 let (diagnostics, diagnostics_server_end) =
1227 fidl::endpoints::create_proxy::<fnet_sockets::DiagnosticsMarker>();
1228
1229 let (mut diagnostics_request_stream, _control_handle) =
1230 diagnostics_server_end.into_stream_and_control_handle();
1231 let server_fut = diagnostics_request_stream
1232 .next()
1233 .then(|req| {
1234 serve_matcher_error(req.expect("Request stream ended unexpectedly").unwrap())
1235 })
1236 .fuse();
1237 let client_fut = iterate_ip::<[IpSocketMatcher; 0], _>(
1238 &diagnostics,
1239 fnet_sockets::Extensions::empty(),
1240 [],
1241 );
1242
1243 pin_mut!(server_fut);
1244 pin_mut!(client_fut);
1245
1246 let ((), resp) = future::join(server_fut, client_fut).await;
1247
1248 assert_matches!(
1249 resp.map(|_| ()),
1251 Err(IterateIpError::InvalidMatcher(0))
1252 );
1253 }
1254
1255 #[fuchsia_async::run_singlethreaded(test)]
1256 async fn iterate_ip_next_error() {
1257 async fn serve_matcher(req: fnet_sockets::DiagnosticsRequest) {
1258 match req {
1259 fnet_sockets::DiagnosticsRequest::IterateIp {
1260 s,
1261 extensions: _,
1262 matchers: _,
1263 responder,
1264 } => {
1265 s.close_with_epitaph(zx_status::Status::PEER_CLOSED).unwrap();
1266 responder.send(&fnet_sockets::IterateIpResult::Ok(fnet_sockets::Empty)).unwrap()
1267 }
1268 }
1269 }
1270
1271 let (diagnostics, diagnostics_server_end) =
1272 fidl::endpoints::create_proxy::<fnet_sockets::DiagnosticsMarker>();
1273
1274 let (mut diagnostics_request_stream, _control_handle) =
1275 diagnostics_server_end.into_stream_and_control_handle();
1276 let server_fut = diagnostics_request_stream
1277 .next()
1278 .then(|req| serve_matcher(req.expect("Request stream ended unexpectedly").unwrap()))
1279 .fuse();
1280
1281 let client_fut = iterate_ip::<[IpSocketMatcher; 0], _>(
1282 &diagnostics,
1283 fnet_sockets::Extensions::empty(),
1284 [],
1285 );
1286
1287 let ((), resp) = future::join(server_fut, client_fut).await;
1288 let stream = resp.unwrap();
1289 pin_mut!(stream);
1290
1291 assert_matches!(
1292 stream.try_next().await,
1293 Err(IpIteratorError::Fidl(fidl::Error::ClientChannelClosed { .. }))
1294 );
1295 }
1296
1297 #[fuchsia_async::run_singlethreaded(test)]
1298 async fn iterate_ip_empty_batch() {
1299 async fn serve_matcher(req: fnet_sockets::DiagnosticsRequest) {
1300 match req {
1301 fnet_sockets::DiagnosticsRequest::IterateIp {
1302 s,
1303 extensions: _,
1304 matchers: _,
1305 responder,
1306 } => {
1307 responder
1308 .send(&fnet_sockets::IterateIpResult::Ok(fnet_sockets::Empty))
1309 .unwrap();
1310
1311 let (mut stream, _control) = s.into_stream_and_control_handle();
1312 match stream.next().await.unwrap().unwrap() {
1313 fidl_fuchsia_net_sockets::IpIteratorRequest::Next { responder } => {
1314 responder.send(&[], true).unwrap();
1316 }
1317 fidl_fuchsia_net_sockets::IpIteratorRequest::_UnknownMethod { .. } => {
1318 unreachable!()
1319 }
1320 }
1321 }
1322 }
1323 }
1324
1325 let (diagnostics, diagnostics_server_end) =
1326 fidl::endpoints::create_proxy::<fnet_sockets::DiagnosticsMarker>();
1327
1328 let (mut diagnostics_request_stream, _control_handle) =
1329 diagnostics_server_end.into_stream_and_control_handle();
1330 let server_fut = diagnostics_request_stream
1331 .next()
1332 .then(|req| serve_matcher(req.expect("Request stream ended unexpectedly").unwrap()))
1333 .fuse();
1334
1335 let client_fut = async {
1336 let stream = iterate_ip::<[IpSocketMatcher; 0], _>(
1337 &diagnostics,
1338 fnet_sockets::Extensions::empty(),
1339 [],
1340 )
1341 .await
1342 .unwrap();
1343 pin_mut!(stream);
1344 stream.try_next().await
1345 };
1346
1347 let ((), resp) = future::join(server_fut, client_fut).await;
1348 assert_matches!(resp, Err(IpIteratorError::EmptyBatch));
1349 }
1350
1351 #[fuchsia_async::run_singlethreaded(test)]
1352 async fn iterate_ip_success() {
1353 let socket_1 = fnet_sockets::IpSocketState {
1354 family: Some(fnet::IpVersion::V4),
1355 src_addr: Some(fidl_ip!("192.168.1.1")),
1356 dst_addr: Some(fidl_ip!("192.168.1.2")),
1357 cookie: Some(1234),
1358 marks: Some(fnet::Marks {
1359 mark_1: Some(1111),
1360 mark_2: None,
1361 __source_breaking: fidl::marker::SourceBreaking,
1362 }),
1363 transport: Some(fnet_sockets::IpSocketTransportState::Tcp(
1364 fnet_sockets::IpSocketTcpState {
1365 src_port: Some(1111),
1366 dst_port: Some(2222),
1367 state: Some(fnet_tcp::State::Established),
1368 tcp_info: None,
1369 __source_breaking: fidl::marker::SourceBreaking,
1370 },
1371 )),
1372 __source_breaking: fidl::marker::SourceBreaking,
1373 };
1374
1375 let socket_2 = fnet_sockets::IpSocketState {
1376 family: Some(fnet::IpVersion::V4),
1377 src_addr: Some(fidl_ip!("192.168.8.1")),
1378 dst_addr: Some(fidl_ip!("192.168.8.2")),
1379 cookie: Some(9876),
1380 marks: Some(fnet::Marks {
1381 mark_1: None,
1382 mark_2: Some(2222),
1383 __source_breaking: fidl::marker::SourceBreaking,
1384 }),
1385 transport: Some(fnet_sockets::IpSocketTransportState::Tcp(
1386 fnet_sockets::IpSocketTcpState {
1387 src_port: Some(3333),
1388 dst_port: Some(4444),
1389 state: Some(fnet_tcp::State::TimeWait),
1390 tcp_info: None,
1391 __source_breaking: fidl::marker::SourceBreaking,
1392 },
1393 )),
1394 __source_breaking: fidl::marker::SourceBreaking,
1395 };
1396
1397 let socket_3 = fnet_sockets::IpSocketState {
1398 family: Some(fnet::IpVersion::V6),
1399 src_addr: Some(fidl_ip!("2001:db8::1")),
1400 dst_addr: Some(fidl_ip!("2001:db8::2")),
1401 cookie: Some(5678),
1402 marks: Some(fnet::Marks {
1403 mark_1: None,
1404 mark_2: None,
1405 __source_breaking: fidl::marker::SourceBreaking,
1406 }),
1407 transport: Some(fnet_sockets::IpSocketTransportState::Tcp(
1408 fnet_sockets::IpSocketTcpState {
1409 src_port: Some(5555),
1410 dst_port: Some(6666),
1411 state: Some(fnet_tcp::State::TimeWait),
1412 tcp_info: None,
1413 __source_breaking: fidl::marker::SourceBreaking,
1414 },
1415 )),
1416 __source_breaking: fidl::marker::SourceBreaking,
1417 };
1418
1419 let (diagnostics, diagnostics_server_end) =
1420 fidl::endpoints::create_proxy::<fnet_sockets::DiagnosticsMarker>();
1421
1422 let serve_matcher = async |req: fnet_sockets::DiagnosticsRequest| {
1423 let responses = &[vec![socket_1.clone()], vec![socket_2.clone(), socket_3.clone()]];
1424
1425 match req {
1426 fnet_sockets::DiagnosticsRequest::IterateIp {
1427 s,
1428 extensions: _,
1429 matchers: _,
1430 responder,
1431 } => {
1432 responder
1433 .send(&fnet_sockets::IterateIpResult::Ok(fnet_sockets::Empty))
1434 .unwrap();
1435
1436 let (mut stream, _control) = s.into_stream_and_control_handle();
1437 for (i, resp) in responses.iter().enumerate() {
1438 match stream.next().await.unwrap().unwrap() {
1439 fidl_fuchsia_net_sockets::IpIteratorRequest::Next { responder } => {
1440 let has_more = i < responses.len() - 1;
1441 responder.send(&resp, has_more).unwrap();
1442 }
1443 fidl_fuchsia_net_sockets::IpIteratorRequest::_UnknownMethod {
1444 ..
1445 } => {
1446 unreachable!()
1447 }
1448 }
1449 }
1450 }
1451 };
1452 };
1453
1454 let (mut diagnostics_request_stream, _control_handle) =
1455 diagnostics_server_end.into_stream_and_control_handle();
1456
1457 let server_fut = diagnostics_request_stream
1458 .next()
1459 .then(|req| serve_matcher(req.expect("Request stream ended unexpectedly").unwrap()))
1460 .fuse();
1461
1462 let client_fut = async {
1463 iterate_ip::<[IpSocketMatcher; 0], _>(
1464 &diagnostics,
1465 fnet_sockets::Extensions::empty(),
1466 [],
1467 )
1468 .await
1469 .unwrap()
1470 .try_collect::<Vec<_>>()
1471 .await
1472 .unwrap()
1473 };
1474
1475 let ((), sockets) = future::join(server_fut, client_fut).await;
1476 assert_eq!(
1477 sockets,
1478 vec![
1479 socket_1.clone().try_into().unwrap(),
1480 socket_2.clone().try_into().unwrap(),
1481 socket_3.clone().try_into().unwrap()
1482 ]
1483 );
1484 }
1485}