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