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 pub tcpi_snd_mss: Option<u32>,
406 pub tcpi_rcv_mss: Option<u32>,
408}
409
410#[derive(Debug, PartialEq, Error)]
412pub enum TcpInfoError {
413 #[error("missing field: {0}")]
415 MissingField(&'static str),
416}
417
418impl TryFrom<fnet_tcp::Info> for TcpInfo {
419 type Error = TcpInfoError;
420
421 fn try_from(value: fnet_tcp::Info) -> Result<Self, Self::Error> {
422 let fnet_tcp::Info {
423 state,
424 ca_state,
425 rto_usec,
426 tcpi_last_data_sent_msec,
427 tcpi_last_ack_recv_msec,
428 rtt_usec,
429 rtt_var_usec,
430 snd_ssthresh,
431 snd_cwnd,
432 tcpi_total_retrans,
433 tcpi_segs_out,
434 tcpi_segs_in,
435 reorder_seen,
436 tcpi_snd_mss,
437 tcpi_rcv_mss,
438 __source_breaking,
439 } = value;
440
441 Ok(TcpInfo {
442 state: state.ok_or(TcpInfoError::MissingField("state"))?,
443 ca_state: ca_state.ok_or(TcpInfoError::MissingField("ca_state"))?,
444 rto_usec,
445 tcpi_last_data_sent_msec,
446 tcpi_last_ack_recv_msec,
447 rtt_usec,
448 rtt_var_usec,
449 snd_ssthresh: snd_ssthresh.ok_or(TcpInfoError::MissingField("snd_ssthresh"))?,
450 snd_cwnd: snd_cwnd.ok_or(TcpInfoError::MissingField("snd_cwnd"))?,
451 tcpi_total_retrans: tcpi_total_retrans
452 .ok_or(TcpInfoError::MissingField("tcpi_total_retrans"))?,
453 tcpi_segs_out: tcpi_segs_out.ok_or(TcpInfoError::MissingField("tcpi_segs_out"))?,
454 tcpi_segs_in: tcpi_segs_in.ok_or(TcpInfoError::MissingField("tcpi_segs_in"))?,
455 reorder_seen: reorder_seen.ok_or(TcpInfoError::MissingField("reorder_seen"))?,
456 tcpi_snd_mss,
457 tcpi_rcv_mss,
458 })
459 }
460}
461
462impl From<TcpInfo> for fnet_tcp::Info {
463 fn from(info: TcpInfo) -> Self {
464 let TcpInfo {
465 state,
466 ca_state,
467 rto_usec,
468 tcpi_last_data_sent_msec,
469 tcpi_last_ack_recv_msec,
470 rtt_usec,
471 rtt_var_usec,
472 snd_ssthresh,
473 snd_cwnd,
474 tcpi_total_retrans,
475 tcpi_segs_out,
476 tcpi_segs_in,
477 reorder_seen,
478 tcpi_snd_mss,
479 tcpi_rcv_mss,
480 } = info;
481 fnet_tcp::Info {
482 state: Some(state),
483 ca_state: Some(ca_state),
484 rto_usec: rto_usec,
485 tcpi_last_data_sent_msec,
486 tcpi_last_ack_recv_msec,
487 rtt_usec: rtt_usec,
488 rtt_var_usec: rtt_var_usec,
489 snd_ssthresh: Some(snd_ssthresh),
490 snd_cwnd: Some(snd_cwnd),
491 tcpi_total_retrans: Some(tcpi_total_retrans),
492 tcpi_segs_out: Some(tcpi_segs_out),
493 tcpi_segs_in: Some(tcpi_segs_in),
494 reorder_seen: Some(reorder_seen),
495 tcpi_snd_mss,
496 tcpi_rcv_mss,
497 __source_breaking: fidl::marker::SourceBreaking,
498 }
499 }
500}
501
502#[derive(Debug, PartialEq, Eq, Clone)]
504pub struct IpSocketUdpState {
505 pub src_port: Option<u16>,
507 pub dst_port: Option<u16>,
509 pub state: fnet_udp::State,
511}
512
513#[derive(Debug, PartialEq, Error)]
515pub enum IpSocketUdpStateError {
516 #[error("missing field: {0}")]
518 MissingField(&'static str),
519}
520
521impl TryFrom<fnet_sockets::IpSocketUdpState> for IpSocketUdpState {
522 type Error = IpSocketUdpStateError;
523
524 fn try_from(value: fnet_sockets::IpSocketUdpState) -> Result<Self, Self::Error> {
525 let fnet_sockets::IpSocketUdpState { src_port, dst_port, state, __source_breaking } = value;
526
527 let state = state.ok_or(IpSocketUdpStateError::MissingField("state"))?;
528
529 Ok(IpSocketUdpState { src_port, dst_port, state })
530 }
531}
532
533impl From<IpSocketUdpState> for fnet_sockets::IpSocketUdpState {
534 fn from(state: IpSocketUdpState) -> Self {
535 let IpSocketUdpState { src_port, dst_port, state } = state;
536 fnet_sockets::IpSocketUdpState {
537 src_port,
538 dst_port,
539 state: Some(state),
540 __source_breaking: fidl::marker::SourceBreaking,
541 }
542 }
543}
544
545#[derive(Debug, Error)]
547pub enum IterateIpError {
548 #[error("invalid matcher at position {0}")]
550 InvalidMatcher(usize),
551 #[error("unknown ordinal on Diagnostics.IterateIp call: {0}")]
553 UnknownOrdinal(u64),
554 #[error("fidl error during Diagnostics.IterateIp call: {0}")]
557 Fidl(fidl::Error),
558}
559
560impl From<fidl::Error> for IterateIpError {
561 fn from(e: fidl::Error) -> Self {
562 IterateIpError::Fidl(e)
563 }
564}
565
566#[derive(Debug, Error)]
568pub enum IpIteratorError {
569 #[error("received empty batch of sockets")]
571 EmptyBatch,
572 #[error("fidl error during Diagnostics.IterateIp call: {0}")]
575 Fidl(fidl::Error),
576 #[error("error converting socket state: {0}")]
578 Conversion(IpSocketStateError),
579}
580
581impl From<fidl::Error> for IpIteratorError {
582 fn from(e: fidl::Error) -> Self {
583 IpIteratorError::Fidl(e)
584 }
585}
586
587pub async fn iterate_ip<M, I>(
597 diagnostics: &fnet_sockets::DiagnosticsProxy,
598 extensions: fnet_sockets::Extensions,
599 matchers: M,
600) -> Result<impl Stream<Item = Result<IpSocketState, IpIteratorError>> + use<M, I>, IterateIpError>
601where
602 M: IntoIterator<Item = I>,
603 I: Into<fnet_sockets::IpSocketMatcher>,
604{
605 let (proxy, server_end) = fidl::endpoints::create_proxy::<fnet_sockets::IpIteratorMarker>();
606 match diagnostics
607 .iterate_ip(
608 server_end,
609 extensions,
610 &matchers.into_iter().map(Into::into).collect::<Vec<_>>()[..],
611 )
612 .await?
613 {
614 fnet_sockets::IterateIpResult::Ok(_empty) => Ok(()),
615 fnet_sockets::IterateIpResult::InvalidMatcher(fnet_sockets::InvalidMatcher { index }) => {
616 Err(IterateIpError::InvalidMatcher(index as usize))
617 }
618 fnet_sockets::IterateIpResult::__SourceBreaking { unknown_ordinal } => {
619 Err(IterateIpError::UnknownOrdinal(unknown_ordinal))
620 }
621 }?;
622
623 Ok(futures::stream::try_unfold((proxy, true), |(proxy, has_more)| async move {
624 if !has_more {
625 return Ok(None);
626 }
627
628 let (batch, has_more) = proxy.next().await?;
629 if batch.is_empty() && has_more {
630 Err(IpIteratorError::EmptyBatch)
631 } else {
632 Ok(Some((
633 futures::stream::iter(
634 batch
635 .into_iter()
636 .map(|s| s.try_into().map_err(|e| IpIteratorError::Conversion(e))),
637 ),
638 (proxy, has_more),
639 )))
640 }
641 })
642 .try_flatten())
643}
644
645#[derive(Debug, Error)]
647pub enum DisconnectIpError {
648 #[error("invalid matcher at position {0}")]
650 InvalidMatcher(usize),
651 #[error("matchers were unconstrained")]
653 UnconstrainedMatchers,
654 #[error("unknown ordinal on Control.DisconnectIp call: {0}")]
656 UnknownOrdinal(u64),
657 #[error("fidl error during Control.DisconnectIp call: {0}")]
660 Fidl(fidl::Error),
661}
662
663pub async fn disconnect_ip<M, I>(
665 control: &fnet_sockets::ControlProxy,
666 matchers: M,
667) -> Result<usize, DisconnectIpError>
668where
669 M: IntoIterator<Item = I>,
670 I: Into<fnet_sockets::IpSocketMatcher>,
671{
672 match control
673 .disconnect_ip(&fnet_sockets::ControlDisconnectIpRequest {
674 matchers: Some(matchers.into_iter().map(Into::into).collect()),
675 __source_breaking: fidl::marker::SourceBreaking,
676 })
677 .await
678 {
679 Ok(r) => match r {
680 fnet_sockets::DisconnectIpResult::Ok(fnet_sockets::DisconnectIpResponse {
681 disconnected,
682 }) => {
683 Ok(disconnected.try_into().unwrap())
685 }
686 fnet_sockets::DisconnectIpResult::InvalidMatcher(fnet_sockets::InvalidMatcher {
687 index,
688 }) => {
689 Err(DisconnectIpError::InvalidMatcher(index.try_into().unwrap()))
691 }
692 fnet_sockets::DisconnectIpResult::UnconstrainedMatchers(fnet_sockets::Empty) => {
693 Err(DisconnectIpError::UnconstrainedMatchers)
694 }
695 fnet_sockets::DisconnectIpResult::__SourceBreaking { unknown_ordinal } => {
696 Err(DisconnectIpError::UnknownOrdinal(unknown_ordinal))
697 }
698 },
699 Err(e) => Err(DisconnectIpError::Fidl(e)),
700 }
701}
702
703#[cfg(test)]
704mod tests {
705 use super::*;
706
707 use std::num::NonZeroU64;
708
709 use assert_matches::assert_matches;
710 use fidl_fuchsia_net as fnet;
711 use fidl_fuchsia_net_tcp as fnet_tcp;
712 use futures::{FutureExt as _, StreamExt as _, future, pin_mut};
713 use net_declare::{fidl_ip, fidl_subnet, net_ip_v4, net_ip_v6};
714 use test_case::test_case;
715
716 #[test_case(
717 fnet_sockets::IpSocketMatcher::Family(fnet::IpVersion::V4),
718 IpSocketMatcher::Family(ip::IpVersion::V4);
719 "FamilyIpv4"
720 )]
721 #[test_case(
722 fnet_sockets::IpSocketMatcher::Family(fnet::IpVersion::V6),
723 IpSocketMatcher::Family(ip::IpVersion::V6);
724 "FamilyIpv6"
725 )]
726 #[test_case(
727 fnet_sockets::IpSocketMatcher::SrcAddr(fnet_matchers::BoundAddress::Bound(
728 fnet_matchers::Address {
729 matcher: fnet_matchers::AddressMatcherType::Subnet(fidl_subnet!("192.0.2.0/24")),
730 invert: true,
731 }
732 )),
733 IpSocketMatcher::SrcAddr(fnet_matchers_ext::BoundAddress::Bound(
734 fnet_matchers_ext::Address {
735 matcher: fnet_matchers_ext::AddressMatcherType::Subnet(
736 fnet_matchers_ext::Subnet::try_from(fidl_subnet!("192.0.2.0/24")).unwrap()
737 ),
738 invert: true,
739 }
740 ));
741 "SrcAddr"
742 )]
743 #[test_case(
744 fnet_sockets::IpSocketMatcher::DstAddr(fnet_matchers::BoundAddress::Bound(
745 fnet_matchers::Address {
746 matcher: fnet_matchers::AddressMatcherType::Subnet(fidl_subnet!("2001:db8::/32")),
747 invert: false,
748 }
749 )),
750 IpSocketMatcher::DstAddr(fnet_matchers_ext::BoundAddress::Bound(
751 fnet_matchers_ext::Address {
752 matcher: fnet_matchers_ext::AddressMatcherType::Subnet(
753 fnet_matchers_ext::Subnet::try_from(fidl_subnet!("2001:db8::/32")).unwrap()
754 ),
755 invert: false,
756 }
757 ));
758 "DstAddr"
759 )]
760 #[test_case(
761 fnet_sockets::IpSocketMatcher::Proto(fnet_matchers::SocketTransportProtocol::Tcp(
762 fnet_matchers::TcpSocket::Empty(fnet_matchers::Empty)
763 )),
764 IpSocketMatcher::Proto(fnet_matchers_ext::SocketTransportProtocol::Tcp(
765 fnet_matchers_ext::TcpSocket::Empty
766 ));
767 "ProtoTcp"
768 )]
769 #[test_case(
770 fnet_sockets::IpSocketMatcher::Proto(fnet_matchers::SocketTransportProtocol::Udp(
771 fnet_matchers::UdpSocket::Empty(fnet_matchers::Empty)
772 )),
773 IpSocketMatcher::Proto(fnet_matchers_ext::SocketTransportProtocol::Udp(
774 fnet_matchers_ext::UdpSocket::Empty
775 ));
776 "ProtoUdp"
777 )]
778 #[test_case(
779 fnet_sockets::IpSocketMatcher::BoundInterface(fnet_matchers::BoundInterface::Unbound(
780 fnet_matchers::Empty
781 )),
782 IpSocketMatcher::BoundInterface(fnet_matchers_ext::BoundInterface::Unbound);
783 "BoundInterfaceUnbound"
784 )]
785 #[test_case(
786 fnet_sockets::IpSocketMatcher::BoundInterface(fnet_matchers::BoundInterface::Bound(
787 fnet_matchers::Interface::Id(1)
788 )),
789 IpSocketMatcher::BoundInterface(fnet_matchers_ext::BoundInterface::Bound(
790 fnet_matchers_ext::Interface::Id(NonZeroU64::new(1).unwrap())
791 ));
792 "BoundInterfaceBound"
793 )]
794 #[test_case(
795 fnet_sockets::IpSocketMatcher::Cookie(fnet_matchers::SocketCookie {
796 cookie: 12345,
797 invert: false,
798 }),
799 IpSocketMatcher::Cookie(fnet_matchers::SocketCookie {
800 cookie: 12345,
801 invert: false,
802 });
803 "Cookie"
804 )]
805 #[test_case(
806 fnet_sockets::IpSocketMatcher::Mark(fnet_matchers::MarkInDomain {
807 domain: fnet::MarkDomain::Mark1,
808 mark: fnet_matchers::Mark::Unmarked(fnet_matchers::Unmarked),
809 }),
810 IpSocketMatcher::Mark(fnet_matchers_ext::MarkInDomain {
811 domain: fnet::MarkDomain::Mark1,
812 mark: fnet_matchers_ext::Mark::Unmarked,
813 });
814 "Mark"
815 )]
816 #[test_case(
817 fnet_sockets::IpSocketMatcher::SrcAddr(fnet_matchers::BoundAddress::Unbound(fnet_matchers::Empty)),
818 IpSocketMatcher::SrcAddr(fnet_matchers_ext::BoundAddress::Unbound);
819 "SrcAddrUnbound"
820 )]
821 #[test_case(
822 fnet_sockets::IpSocketMatcher::DstAddr(fnet_matchers::BoundAddress::Unbound(fnet_matchers::Empty)),
823 IpSocketMatcher::DstAddr(fnet_matchers_ext::BoundAddress::Unbound);
824 "DstAddrUnbound"
825 )]
826 #[test_case(
827 fnet_sockets::IpSocketMatcher::Proto(fnet_matchers::SocketTransportProtocol::Tcp(
828 fnet_matchers::TcpSocket::SrcPort(fnet_matchers::BoundPort::Unbound(fnet_matchers::Empty))
829 )),
830 IpSocketMatcher::Proto(fnet_matchers_ext::SocketTransportProtocol::Tcp(
831 fnet_matchers_ext::TcpSocket::SrcPort(fnet_matchers_ext::BoundPort::Unbound)
832 ));
833 "ProtoTcpSrcPortUnbound"
834 )]
835 #[test_case(
836 fnet_sockets::IpSocketMatcher::Proto(fnet_matchers::SocketTransportProtocol::Tcp(
837 fnet_matchers::TcpSocket::DstPort(fnet_matchers::BoundPort::Unbound(fnet_matchers::Empty))
838 )),
839 IpSocketMatcher::Proto(fnet_matchers_ext::SocketTransportProtocol::Tcp(
840 fnet_matchers_ext::TcpSocket::DstPort(fnet_matchers_ext::BoundPort::Unbound)
841 ));
842 "ProtoTcpDstPortUnbound"
843 )]
844 #[test_case(
845 fnet_sockets::IpSocketMatcher::Proto(fnet_matchers::SocketTransportProtocol::Udp(
846 fnet_matchers::UdpSocket::SrcPort(fnet_matchers::BoundPort::Unbound(fnet_matchers::Empty))
847 )),
848 IpSocketMatcher::Proto(fnet_matchers_ext::SocketTransportProtocol::Udp(
849 fnet_matchers_ext::UdpSocket::SrcPort(fnet_matchers_ext::BoundPort::Unbound)
850 ));
851 "ProtoUdpSrcPortUnbound"
852 )]
853 #[test_case(
854 fnet_sockets::IpSocketMatcher::Proto(fnet_matchers::SocketTransportProtocol::Udp(
855 fnet_matchers::UdpSocket::DstPort(fnet_matchers::BoundPort::Unbound(fnet_matchers::Empty))
856 )),
857 IpSocketMatcher::Proto(fnet_matchers_ext::SocketTransportProtocol::Udp(
858 fnet_matchers_ext::UdpSocket::DstPort(fnet_matchers_ext::BoundPort::Unbound)
859 ));
860 "ProtoUdpDstPortUnbound"
861 )]
862 #[test_case(
863 fnet_tcp::Info {
864 state: Some(fnet_tcp::State::Established),
865 ca_state: Some(fnet_tcp::CongestionControlState::Open),
866 rto_usec: Some(1),
867 tcpi_last_data_sent_msec: Some(2),
868 tcpi_last_ack_recv_msec: Some(3),
869 rtt_usec: Some(4),
870 rtt_var_usec: Some(5),
871 snd_ssthresh: Some(6),
872 snd_cwnd: Some(7),
873 tcpi_total_retrans: Some(8),
874 tcpi_segs_out: Some(9),
875 tcpi_segs_in: Some(10),
876 reorder_seen: Some(true),
877 tcpi_snd_mss: Some(11),
878 tcpi_rcv_mss: Some(12),
879 __source_breaking: fidl::marker::SourceBreaking,
880 },
881 TcpInfo {
882 state: fnet_tcp::State::Established,
883 ca_state: fnet_tcp::CongestionControlState::Open,
884 rto_usec: Some(1),
885 tcpi_last_data_sent_msec: Some(2),
886 tcpi_last_ack_recv_msec: Some(3),
887 rtt_usec: Some(4),
888 rtt_var_usec: Some(5),
889 snd_ssthresh: 6,
890 snd_cwnd: 7,
891 tcpi_total_retrans: 8,
892 tcpi_segs_out: 9,
893 tcpi_segs_in: 10,
894 reorder_seen: true,
895 tcpi_snd_mss: Some(11),
896 tcpi_rcv_mss: Some(12),
897 };
898 "TcpInfo"
899 )]
900 #[test_case(
901 fnet_sockets::IpSocketState {
902 family: Some(fnet::IpVersion::V4),
903 src_addr: Some(fidl_ip!("192.168.1.1")),
904 dst_addr: Some(fidl_ip!("192.168.1.2")),
905 cookie: Some(1234),
906 marks: Some(fnet::Marks {
907 mark_1: Some(1111),
908 mark_2: None,
909 __source_breaking: fidl::marker::SourceBreaking,
910 }),
911 transport: Some(fnet_sockets::IpSocketTransportState::Tcp(
912 fnet_sockets::IpSocketTcpState {
913 src_port: Some(1111),
914 dst_port: Some(2222),
915 state: Some(fnet_tcp::State::Established),
916 tcp_info: None,
917 __source_breaking: fidl::marker::SourceBreaking,
918 },
919 )),
920 __source_breaking: fidl::marker::SourceBreaking,
921 },
922 IpSocketState::V4(IpSocketStateSpecific {
923 src_addr: Some(net_ip_v4!("192.168.1.1")),
924 dst_addr: Some(net_ip_v4!("192.168.1.2")),
925 cookie: 1234,
926 marks: fnet::Marks {
927 mark_1: Some(1111),
928 mark_2: None,
929 __source_breaking: fidl::marker::SourceBreaking,
930 }.into(),
931 transport: IpSocketTransportState::Tcp(IpSocketTcpState {
932 src_port: Some(1111),
933 dst_port: Some(2222),
934 state: fnet_tcp::State::Established,
935 tcp_info: None,
936 }),
937 });
938 "IpSocketStateV4"
939 )]
940 #[test_case(
941 fnet_sockets::IpSocketState {
942 family: Some(fnet::IpVersion::V6),
943 src_addr: Some(fidl_ip!("2001:db8::1")),
944 dst_addr: Some(fidl_ip!("2001:db8::2")),
945 cookie: Some(1234),
946 marks: Some(fnet::Marks {
947 mark_1: Some(1111),
948 mark_2: None,
949 __source_breaking: fidl::marker::SourceBreaking,
950 }),
951 transport: Some(fnet_sockets::IpSocketTransportState::Udp(
952 fnet_sockets::IpSocketUdpState {
953 src_port: Some(3333),
954 dst_port: Some(4444),
955 state: Some(fnet_udp::State::Connected),
956 __source_breaking: fidl::marker::SourceBreaking,
957 },
958 )),
959 __source_breaking: fidl::marker::SourceBreaking,
960 },
961 IpSocketState::V6(IpSocketStateSpecific {
962 src_addr: Some(net_ip_v6!("2001:db8::1")),
963 dst_addr: Some(net_ip_v6!("2001:db8::2")),
964 cookie: 1234,
965 marks: fnet::Marks {
966 mark_1: Some(1111),
967 mark_2: None,
968 __source_breaking: fidl::marker::SourceBreaking,
969 }.into(),
970 transport: IpSocketTransportState::Udp(IpSocketUdpState {
971 src_port: Some(3333),
972 dst_port: Some(4444),
973 state: fnet_udp::State::Connected,
974 }),
975 });
976 "IpSocketStateV6"
977 )]
978 fn convert_from_fidl_and_back<F, E>(fidl_type: F, local_type: E)
979 where
980 E: TryFrom<F> + Clone + std::fmt::Debug + PartialEq,
981 <E as TryFrom<F>>::Error: std::fmt::Debug + PartialEq,
982 F: From<E> + Clone + std::fmt::Debug + PartialEq,
983 {
984 assert_eq!(fidl_type.clone().try_into(), Ok(local_type.clone()));
985 assert_eq!(<_ as Into<F>>::into(local_type), fidl_type);
986 }
987
988 #[test_case(
989 fnet_sockets::IpSocketMatcher::__SourceBreaking { unknown_ordinal: 100 } =>
990 Err(IpSocketMatcherError::UnknownUnionVariant(100));
991 "UnknownUnionVariant"
992 )]
993 #[test_case(
994 fnet_sockets::IpSocketMatcher::SrcAddr(fnet_matchers::BoundAddress::Bound(
995 fnet_matchers::Address {
996 matcher: fnet_matchers::AddressMatcherType::__SourceBreaking { unknown_ordinal: 100 },
997 invert: false,
998 }
999 )) => Err(IpSocketMatcherError::Address(fnet_matchers_ext::BoundAddressError::Address(
1000 fnet_matchers_ext::AddressError::AddressMatcherType(
1001 fnet_matchers_ext::AddressMatcherTypeError::UnknownUnionVariant
1002 )
1003 )));
1004 "AddressError"
1005 )]
1006 #[test_case(
1007 fnet_sockets::IpSocketMatcher::Proto(
1008 fnet_matchers::SocketTransportProtocol::__SourceBreaking { unknown_ordinal: 100 }
1009 ) => Err(IpSocketMatcherError::TransportProtocol(
1010 fnet_matchers_ext::SocketTransportProtocolError::UnknownUnionVariant(100)
1011 ));
1012 "TransportProtocolError"
1013 )]
1014 #[test_case(
1015 fnet_sockets::IpSocketMatcher::BoundInterface(
1016 fnet_matchers::BoundInterface::__SourceBreaking { unknown_ordinal: 100 }
1017 ) => Err(IpSocketMatcherError::BoundInterface(
1018 fnet_matchers_ext::BoundInterfaceError::UnknownUnionVariant(100)
1019 ));
1020 "BoundInterfaceError"
1021 )]
1022 #[test_case(
1023 fnet_sockets::IpSocketMatcher::Mark(fnet_matchers::MarkInDomain {
1024 domain: fnet::MarkDomain::Mark1,
1025 mark: fnet_matchers::Mark::__SourceBreaking { unknown_ordinal: 100 },
1026 }) => Err(IpSocketMatcherError::Mark(
1027 fnet_matchers_ext::MarkInDomainError::Mark(
1028 fnet_matchers_ext::MarkError::UnknownUnionVariant(100)
1029 )
1030 ));
1031 "MarkError"
1032 )]
1033 fn ip_socket_matcher_try_from_error(
1034 fidl: fnet_sockets::IpSocketMatcher,
1035 ) -> Result<IpSocketMatcher, IpSocketMatcherError> {
1036 IpSocketMatcher::try_from(fidl)
1037 }
1038
1039 #[test_case(
1040 fnet_sockets::IpSocketState {
1041 family: None,
1042 src_addr: Some(fidl_ip!("192.168.1.1")),
1043 dst_addr: Some(fidl_ip!("192.168.1.2")),
1044 cookie: Some(1234),
1045 marks: Some(fnet::Marks {
1046 mark_1: Some(1111),
1047 mark_2: None,
1048 __source_breaking: fidl::marker::SourceBreaking,
1049 }),
1050 transport: Some(fnet_sockets::IpSocketTransportState::Tcp(
1051 fnet_sockets::IpSocketTcpState {
1052 src_port: Some(1111),
1053 dst_port: Some(2222),
1054 state: Some(fnet_tcp::State::Established),
1055 tcp_info: None,
1056 __source_breaking: fidl::marker::SourceBreaking,
1057 },
1058 )),
1059 __source_breaking: fidl::marker::SourceBreaking,
1060 } => Err(IpSocketStateError::MissingField("family"));
1061 "MissingFamily"
1062 )]
1063 #[test_case(
1064 fnet_sockets::IpSocketState {
1065 family: Some(fnet::IpVersion::V4),
1066 src_addr: Some(fidl_ip!("192.168.1.1")),
1067 dst_addr: Some(fidl_ip!("192.168.1.2")),
1068 cookie: None,
1069 marks: Some(fnet::Marks {
1070 mark_1: Some(1111),
1071 mark_2: None,
1072 __source_breaking: fidl::marker::SourceBreaking,
1073 }),
1074 transport: Some(fnet_sockets::IpSocketTransportState::Tcp(
1075 fnet_sockets::IpSocketTcpState {
1076 src_port: Some(1111),
1077 dst_port: Some(2222),
1078 state: Some(fnet_tcp::State::Established),
1079 tcp_info: None,
1080 __source_breaking: fidl::marker::SourceBreaking,
1081 },
1082 )),
1083 __source_breaking: fidl::marker::SourceBreaking,
1084 } => Err(IpSocketStateError::MissingField("cookie"));
1085 "MissingCookie"
1086 )]
1087 #[test_case(
1088 fnet_sockets::IpSocketState {
1089 family: Some(fnet::IpVersion::V4),
1090 src_addr: Some(fidl_ip!("192.168.1.1")),
1091 dst_addr: Some(fidl_ip!("192.168.1.2")),
1092 cookie: Some(1234),
1093 marks: None,
1094 transport: Some(fnet_sockets::IpSocketTransportState::Tcp(
1095 fnet_sockets::IpSocketTcpState {
1096 src_port: Some(1111),
1097 dst_port: Some(2222),
1098 state: Some(fnet_tcp::State::Established),
1099 tcp_info: None,
1100 __source_breaking: fidl::marker::SourceBreaking,
1101 },
1102 )),
1103 __source_breaking: fidl::marker::SourceBreaking,
1104 } => Err(IpSocketStateError::MissingField("marks"));
1105 "MissingMarks"
1106 )]
1107 #[test_case(
1108 fnet_sockets::IpSocketState {
1109 family: Some(fnet::IpVersion::V4),
1110 src_addr: Some(fidl_ip!("192.168.1.1")),
1111 dst_addr: Some(fidl_ip!("192.168.1.2")),
1112 cookie: Some(1234),
1113 marks: Some(fnet::Marks {
1114 mark_1: Some(1111),
1115 mark_2: None,
1116 __source_breaking: fidl::marker::SourceBreaking,
1117 }),
1118 transport: None,
1119 __source_breaking: fidl::marker::SourceBreaking,
1120 } => Err(IpSocketStateError::MissingField("transport"));
1121 "MissingTransport"
1122 )]
1123 #[test_case(
1124 fnet_sockets::IpSocketState {
1125 family: Some(fnet::IpVersion::V4),
1126 src_addr: Some(fidl_ip!("192.168.1.1")),
1127 dst_addr: Some(fidl_ip!("2001:db8::2")),
1128 cookie: Some(1234),
1129 marks: Some(fnet::Marks {
1130 mark_1: Some(1111),
1131 mark_2: None,
1132 __source_breaking: fidl::marker::SourceBreaking,
1133 }),
1134 transport: Some(fnet_sockets::IpSocketTransportState::Tcp(
1135 fnet_sockets::IpSocketTcpState {
1136 src_port: Some(1111),
1137 dst_port: Some(2222),
1138 state: Some(fnet_tcp::State::Established),
1139 tcp_info: None,
1140 __source_breaking: fidl::marker::SourceBreaking,
1141 },
1142 )),
1143 __source_breaking: fidl::marker::SourceBreaking,
1144 } => Err(IpSocketStateError::VersionMismatch);
1145 "VersionMismatchV4"
1146 )]
1147 #[test_case(
1148 fnet_sockets::IpSocketState {
1149 family: Some(fnet::IpVersion::V6),
1150 src_addr: Some(fidl_ip!("192.168.1.1")),
1151 dst_addr: Some(fidl_ip!("2001:db8::2")),
1152 cookie: Some(1234),
1153 marks: Some(fnet::Marks {
1154 mark_1: Some(1111),
1155 mark_2: None,
1156 __source_breaking: fidl::marker::SourceBreaking,
1157 }),
1158 transport: Some(fnet_sockets::IpSocketTransportState::Tcp(
1159 fnet_sockets::IpSocketTcpState {
1160 src_port: Some(1111),
1161 dst_port: Some(2222),
1162 state: Some(fnet_tcp::State::Established),
1163 tcp_info: None,
1164 __source_breaking: fidl::marker::SourceBreaking,
1165 },
1166 )),
1167 __source_breaking: fidl::marker::SourceBreaking,
1168 } => Err(IpSocketStateError::VersionMismatch);
1169 "VersionMismatchV6"
1170 )]
1171 #[test_case(
1172 fnet_sockets::IpSocketState {
1173 family: Some(fnet::IpVersion::V4),
1174 src_addr: Some(fidl_ip!("192.168.1.1")),
1175 dst_addr: Some(fidl_ip!("192.168.1.2")),
1176 cookie: Some(1234),
1177 marks: Some(fnet::Marks {
1178 mark_1: Some(1111),
1179 mark_2: None,
1180 __source_breaking: fidl::marker::SourceBreaking,
1181 }),
1182 transport: Some(fnet_sockets::IpSocketTransportState::Tcp(
1183 fnet_sockets::IpSocketTcpState {
1184 src_port: Some(1111),
1185 dst_port: Some(2222),
1186 state: None,
1187 tcp_info: None,
1188 __source_breaking: fidl::marker::SourceBreaking,
1189 },
1190 )),
1191 __source_breaking: fidl::marker::SourceBreaking,
1192 } => Err(IpSocketStateError::Transport(IpSocketTransportStateError::Tcp(
1193 IpSocketTcpStateError::MissingField("state"),
1194 )));
1195 "MissingTcpState"
1196 )]
1197 #[test_case(
1198 fnet_sockets::IpSocketState {
1199 family: Some(fnet::IpVersion::V6),
1200 src_addr: Some(fidl_ip!("2001:db8::1")),
1201 dst_addr: Some(fidl_ip!("2001:db8::2")),
1202 cookie: Some(1234),
1203 marks: Some(fnet::Marks {
1204 mark_1: Some(1111),
1205 mark_2: None,
1206 __source_breaking: fidl::marker::SourceBreaking,
1207 }),
1208 transport: Some(fnet_sockets::IpSocketTransportState::Udp(
1209 fnet_sockets::IpSocketUdpState {
1210 src_port: Some(3333),
1211 dst_port: Some(4444),
1212 state: None,
1213 __source_breaking: fidl::marker::SourceBreaking,
1214 },
1215 )),
1216 __source_breaking: fidl::marker::SourceBreaking,
1217 } => Err(IpSocketStateError::Transport(IpSocketTransportStateError::Udp(
1218 IpSocketUdpStateError::MissingField("state"),
1219 )));
1220 "MissingUdpState"
1221 )]
1222 fn ip_socket_state_try_from_error(
1223 fidl: fnet_sockets::IpSocketState,
1224 ) -> Result<IpSocketState, IpSocketStateError> {
1225 IpSocketState::try_from(fidl)
1226 }
1227
1228 #[fuchsia_async::run_singlethreaded(test)]
1229 async fn iterate_ip_diagnostics_iterate_ip_error() {
1230 async fn serve_matcher_error(req: fnet_sockets::DiagnosticsRequest) {
1231 match req {
1232 fnet_sockets::DiagnosticsRequest::IterateIp {
1233 s: _,
1234 extensions: _,
1235 matchers: _,
1236 responder,
1237 } => responder
1238 .send(&fnet_sockets::IterateIpResult::InvalidMatcher(
1239 fnet_sockets::InvalidMatcher { index: 0 },
1240 ))
1241 .unwrap(),
1242 };
1243 }
1244
1245 let (diagnostics, diagnostics_server_end) =
1246 fidl::endpoints::create_proxy::<fnet_sockets::DiagnosticsMarker>();
1247
1248 let (mut diagnostics_request_stream, _control_handle) =
1249 diagnostics_server_end.into_stream_and_control_handle();
1250 let server_fut = diagnostics_request_stream
1251 .next()
1252 .then(|req| {
1253 serve_matcher_error(req.expect("Request stream ended unexpectedly").unwrap())
1254 })
1255 .fuse();
1256 let client_fut = iterate_ip::<[IpSocketMatcher; 0], _>(
1257 &diagnostics,
1258 fnet_sockets::Extensions::empty(),
1259 [],
1260 );
1261
1262 pin_mut!(server_fut);
1263 pin_mut!(client_fut);
1264
1265 let ((), resp) = future::join(server_fut, client_fut).await;
1266
1267 assert_matches!(
1268 resp.map(|_| ()),
1270 Err(IterateIpError::InvalidMatcher(0))
1271 );
1272 }
1273
1274 #[fuchsia_async::run_singlethreaded(test)]
1275 async fn iterate_ip_next_error() {
1276 async fn serve_matcher(req: fnet_sockets::DiagnosticsRequest) {
1277 match req {
1278 fnet_sockets::DiagnosticsRequest::IterateIp {
1279 s,
1280 extensions: _,
1281 matchers: _,
1282 responder,
1283 } => {
1284 s.close_with_epitaph(zx_status::Status::PEER_CLOSED).unwrap();
1285 responder.send(&fnet_sockets::IterateIpResult::Ok(fnet_sockets::Empty)).unwrap()
1286 }
1287 }
1288 }
1289
1290 let (diagnostics, diagnostics_server_end) =
1291 fidl::endpoints::create_proxy::<fnet_sockets::DiagnosticsMarker>();
1292
1293 let (mut diagnostics_request_stream, _control_handle) =
1294 diagnostics_server_end.into_stream_and_control_handle();
1295 let server_fut = diagnostics_request_stream
1296 .next()
1297 .then(|req| serve_matcher(req.expect("Request stream ended unexpectedly").unwrap()))
1298 .fuse();
1299
1300 let client_fut = iterate_ip::<[IpSocketMatcher; 0], _>(
1301 &diagnostics,
1302 fnet_sockets::Extensions::empty(),
1303 [],
1304 );
1305
1306 let ((), resp) = future::join(server_fut, client_fut).await;
1307 let stream = resp.unwrap();
1308 pin_mut!(stream);
1309
1310 assert_matches!(
1311 stream.try_next().await,
1312 Err(IpIteratorError::Fidl(fidl::Error::ClientChannelClosed { .. }))
1313 );
1314 }
1315
1316 #[fuchsia_async::run_singlethreaded(test)]
1317 async fn iterate_ip_empty_batch() {
1318 async fn serve_matcher(req: fnet_sockets::DiagnosticsRequest) {
1319 match req {
1320 fnet_sockets::DiagnosticsRequest::IterateIp {
1321 s,
1322 extensions: _,
1323 matchers: _,
1324 responder,
1325 } => {
1326 responder
1327 .send(&fnet_sockets::IterateIpResult::Ok(fnet_sockets::Empty))
1328 .unwrap();
1329
1330 let (mut stream, _control) = s.into_stream_and_control_handle();
1331 match stream.next().await.unwrap().unwrap() {
1332 fidl_fuchsia_net_sockets::IpIteratorRequest::Next { responder } => {
1333 responder.send(&[], true).unwrap();
1335 }
1336 fidl_fuchsia_net_sockets::IpIteratorRequest::_UnknownMethod { .. } => {
1337 unreachable!()
1338 }
1339 }
1340 }
1341 }
1342 }
1343
1344 let (diagnostics, diagnostics_server_end) =
1345 fidl::endpoints::create_proxy::<fnet_sockets::DiagnosticsMarker>();
1346
1347 let (mut diagnostics_request_stream, _control_handle) =
1348 diagnostics_server_end.into_stream_and_control_handle();
1349 let server_fut = diagnostics_request_stream
1350 .next()
1351 .then(|req| serve_matcher(req.expect("Request stream ended unexpectedly").unwrap()))
1352 .fuse();
1353
1354 let client_fut = async {
1355 let stream = iterate_ip::<[IpSocketMatcher; 0], _>(
1356 &diagnostics,
1357 fnet_sockets::Extensions::empty(),
1358 [],
1359 )
1360 .await
1361 .unwrap();
1362 pin_mut!(stream);
1363 stream.try_next().await
1364 };
1365
1366 let ((), resp) = future::join(server_fut, client_fut).await;
1367 assert_matches!(resp, Err(IpIteratorError::EmptyBatch));
1368 }
1369
1370 #[fuchsia_async::run_singlethreaded(test)]
1371 async fn iterate_ip_success() {
1372 let socket_1 = fnet_sockets::IpSocketState {
1373 family: Some(fnet::IpVersion::V4),
1374 src_addr: Some(fidl_ip!("192.168.1.1")),
1375 dst_addr: Some(fidl_ip!("192.168.1.2")),
1376 cookie: Some(1234),
1377 marks: Some(fnet::Marks {
1378 mark_1: Some(1111),
1379 mark_2: None,
1380 __source_breaking: fidl::marker::SourceBreaking,
1381 }),
1382 transport: Some(fnet_sockets::IpSocketTransportState::Tcp(
1383 fnet_sockets::IpSocketTcpState {
1384 src_port: Some(1111),
1385 dst_port: Some(2222),
1386 state: Some(fnet_tcp::State::Established),
1387 tcp_info: None,
1388 __source_breaking: fidl::marker::SourceBreaking,
1389 },
1390 )),
1391 __source_breaking: fidl::marker::SourceBreaking,
1392 };
1393
1394 let socket_2 = fnet_sockets::IpSocketState {
1395 family: Some(fnet::IpVersion::V4),
1396 src_addr: Some(fidl_ip!("192.168.8.1")),
1397 dst_addr: Some(fidl_ip!("192.168.8.2")),
1398 cookie: Some(9876),
1399 marks: Some(fnet::Marks {
1400 mark_1: None,
1401 mark_2: Some(2222),
1402 __source_breaking: fidl::marker::SourceBreaking,
1403 }),
1404 transport: Some(fnet_sockets::IpSocketTransportState::Tcp(
1405 fnet_sockets::IpSocketTcpState {
1406 src_port: Some(3333),
1407 dst_port: Some(4444),
1408 state: Some(fnet_tcp::State::TimeWait),
1409 tcp_info: None,
1410 __source_breaking: fidl::marker::SourceBreaking,
1411 },
1412 )),
1413 __source_breaking: fidl::marker::SourceBreaking,
1414 };
1415
1416 let socket_3 = fnet_sockets::IpSocketState {
1417 family: Some(fnet::IpVersion::V6),
1418 src_addr: Some(fidl_ip!("2001:db8::1")),
1419 dst_addr: Some(fidl_ip!("2001:db8::2")),
1420 cookie: Some(5678),
1421 marks: Some(fnet::Marks {
1422 mark_1: None,
1423 mark_2: None,
1424 __source_breaking: fidl::marker::SourceBreaking,
1425 }),
1426 transport: Some(fnet_sockets::IpSocketTransportState::Tcp(
1427 fnet_sockets::IpSocketTcpState {
1428 src_port: Some(5555),
1429 dst_port: Some(6666),
1430 state: Some(fnet_tcp::State::TimeWait),
1431 tcp_info: None,
1432 __source_breaking: fidl::marker::SourceBreaking,
1433 },
1434 )),
1435 __source_breaking: fidl::marker::SourceBreaking,
1436 };
1437
1438 let (diagnostics, diagnostics_server_end) =
1439 fidl::endpoints::create_proxy::<fnet_sockets::DiagnosticsMarker>();
1440
1441 let serve_matcher = async |req: fnet_sockets::DiagnosticsRequest| {
1442 let responses = &[vec![socket_1.clone()], vec![socket_2.clone(), socket_3.clone()]];
1443
1444 match req {
1445 fnet_sockets::DiagnosticsRequest::IterateIp {
1446 s,
1447 extensions: _,
1448 matchers: _,
1449 responder,
1450 } => {
1451 responder
1452 .send(&fnet_sockets::IterateIpResult::Ok(fnet_sockets::Empty))
1453 .unwrap();
1454
1455 let (mut stream, _control) = s.into_stream_and_control_handle();
1456 for (i, resp) in responses.iter().enumerate() {
1457 match stream.next().await.unwrap().unwrap() {
1458 fidl_fuchsia_net_sockets::IpIteratorRequest::Next { responder } => {
1459 let has_more = i < responses.len() - 1;
1460 responder.send(&resp, has_more).unwrap();
1461 }
1462 fidl_fuchsia_net_sockets::IpIteratorRequest::_UnknownMethod {
1463 ..
1464 } => {
1465 unreachable!()
1466 }
1467 }
1468 }
1469 }
1470 };
1471 };
1472
1473 let (mut diagnostics_request_stream, _control_handle) =
1474 diagnostics_server_end.into_stream_and_control_handle();
1475
1476 let server_fut = diagnostics_request_stream
1477 .next()
1478 .then(|req| serve_matcher(req.expect("Request stream ended unexpectedly").unwrap()))
1479 .fuse();
1480
1481 let client_fut = async {
1482 iterate_ip::<[IpSocketMatcher; 0], _>(
1483 &diagnostics,
1484 fnet_sockets::Extensions::empty(),
1485 [],
1486 )
1487 .await
1488 .unwrap()
1489 .try_collect::<Vec<_>>()
1490 .await
1491 .unwrap()
1492 };
1493
1494 let ((), sockets) = future::join(server_fut, client_fut).await;
1495 assert_eq!(
1496 sockets,
1497 vec![
1498 socket_1.clone().try_into().unwrap(),
1499 socket_2.clone().try_into().unwrap(),
1500 socket_3.clone().try_into().unwrap()
1501 ]
1502 );
1503 }
1504}