1#![deny(missing_docs)]
14
15pub mod admin;
16pub mod rules;
17pub mod testutil;
18
19use std::collections::HashSet;
20use std::fmt::{Debug, Display};
21
22use async_utils::{fold, stream};
23use fidl_fuchsia_net_ext::{self as fnet_ext, IntoExt as _, TryIntoExt as _};
24use flex_fuchsia_net as fnet;
25use flex_fuchsia_net_routes as fnet_routes;
26use flex_fuchsia_net_routes_admin as fnet_routes_admin;
27use flex_fuchsia_net_stack as fnet_stack;
28use futures::{Future, Stream, TryStreamExt as _};
29use net_types::ip::{GenericOverIp, Ip, Ipv4, Ipv6, Ipv6Addr, Subnet};
30use net_types::{SpecifiedAddr, UnicastAddress, Witness as _};
31use thiserror::Error;
32
33#[derive(Clone, Copy, Debug, Error, PartialEq)]
36pub enum FidlConversionError<UnsetFieldSpecifier: Debug + Display> {
37 #[error("required field is unset: {0}")]
40 RequiredFieldUnset(UnsetFieldSpecifier),
41 #[error("failed to convert `destination` to net_types subnet: {0:?}")]
43 DestinationSubnet(net_types::ip::SubnetError),
44 #[error("failed to convert `next_hop` to a specified addr")]
46 UnspecifiedNextHop,
47 #[error("failed to convert `next_hop` to a unicast addr")]
49 NextHopNotUnicast,
50}
51
52impl<T: Debug + Display> FidlConversionError<T> {
53 fn map_unset_fields<U: Debug + Display>(
54 self,
55 f: impl FnOnce(T) -> U,
56 ) -> FidlConversionError<U> {
57 match self {
58 FidlConversionError::RequiredFieldUnset(field) => {
59 FidlConversionError::RequiredFieldUnset(f(field))
60 }
61 FidlConversionError::DestinationSubnet(err) => {
62 FidlConversionError::DestinationSubnet(err)
63 }
64 FidlConversionError::UnspecifiedNextHop => FidlConversionError::UnspecifiedNextHop,
65 FidlConversionError::NextHopNotUnicast => FidlConversionError::NextHopNotUnicast,
66 }
67 }
68}
69
70impl From<FidlConversionError<RoutePropertiesRequiredFields>> for fnet_routes_admin::RouteSetError {
71 fn from(error: FidlConversionError<RoutePropertiesRequiredFields>) -> Self {
72 match error {
73 FidlConversionError::RequiredFieldUnset(field_name) => match field_name {
74 RoutePropertiesRequiredFields::SpecifiedProperties => {
75 fnet_routes_admin::RouteSetError::MissingRouteProperties
76 }
77 RoutePropertiesRequiredFields::WithinSpecifiedProperties(field_name) => {
78 match field_name {
79 SpecifiedRoutePropertiesRequiredFields::Metric => {
80 fnet_routes_admin::RouteSetError::MissingMetric
81 }
82 }
83 }
84 },
85 FidlConversionError::DestinationSubnet(_subnet_error) => {
86 fnet_routes_admin::RouteSetError::InvalidDestinationSubnet
87 }
88 FidlConversionError::UnspecifiedNextHop | FidlConversionError::NextHopNotUnicast => {
89 fnet_routes_admin::RouteSetError::InvalidNextHop
90 }
91 }
92 }
93}
94
95#[derive(Clone, Copy, Debug, Error, PartialEq)]
98pub enum NetTypeConversionError {
99 #[error("Union type is of the `Unknown` variant: {0}")]
101 UnknownUnionVariant(&'static str),
102}
103
104#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
107pub struct SpecifiedRouteProperties {
108 pub metric: fnet_routes::SpecifiedMetric,
110}
111
112#[derive(Error, Debug, Clone, PartialEq, Eq)]
114#[allow(missing_docs)]
115pub enum SpecifiedRoutePropertiesRequiredFields {
116 #[error("fuchsia.net.routes/SpecifiedRouteProperties.metric")]
117 Metric,
118}
119
120impl TryFrom<fnet_routes::SpecifiedRouteProperties> for SpecifiedRouteProperties {
121 type Error = FidlConversionError<SpecifiedRoutePropertiesRequiredFields>;
122 fn try_from(
123 specified_properties: fnet_routes::SpecifiedRouteProperties,
124 ) -> Result<Self, Self::Error> {
125 Ok(SpecifiedRouteProperties {
126 metric: specified_properties.metric.ok_or(FidlConversionError::RequiredFieldUnset(
127 SpecifiedRoutePropertiesRequiredFields::Metric,
128 ))?,
129 })
130 }
131}
132
133impl From<SpecifiedRouteProperties> for fnet_routes::SpecifiedRouteProperties {
134 fn from(
135 specified_properties: SpecifiedRouteProperties,
136 ) -> fnet_routes::SpecifiedRouteProperties {
137 let SpecifiedRouteProperties { metric } = specified_properties;
138 fnet_routes::SpecifiedRouteProperties { metric: Some(metric), ..Default::default() }
139 }
140}
141
142#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
145pub struct EffectiveRouteProperties {
146 pub metric: u32,
148}
149
150#[derive(Debug, Error, Clone, PartialEq, Eq)]
151#[allow(missing_docs)]
152pub enum EffectiveRoutePropertiesRequiredFields {
153 #[error("fuchsia.net.routes/EffectiveRouteProperties.metric")]
154 Metric,
155}
156
157impl TryFrom<fnet_routes::EffectiveRouteProperties> for EffectiveRouteProperties {
158 type Error = FidlConversionError<EffectiveRoutePropertiesRequiredFields>;
159 fn try_from(
160 effective_properties: fnet_routes::EffectiveRouteProperties,
161 ) -> Result<Self, Self::Error> {
162 Ok(EffectiveRouteProperties {
163 metric: effective_properties.metric.ok_or(FidlConversionError::RequiredFieldUnset(
164 EffectiveRoutePropertiesRequiredFields::Metric,
165 ))?,
166 })
167 }
168}
169
170impl From<EffectiveRouteProperties> for fnet_routes::EffectiveRouteProperties {
171 fn from(
172 effective_properties: EffectiveRouteProperties,
173 ) -> fnet_routes::EffectiveRouteProperties {
174 let EffectiveRouteProperties { metric } = effective_properties;
175 fnet_routes::EffectiveRouteProperties { metric: Some(metric), ..Default::default() }
176 }
177}
178
179#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
182pub struct RouteProperties {
183 pub specified_properties: SpecifiedRouteProperties,
185}
186
187impl RouteProperties {
188 pub fn from_explicit_metric(metric: u32) -> Self {
190 Self {
191 specified_properties: SpecifiedRouteProperties {
192 metric: fnet_routes::SpecifiedMetric::ExplicitMetric(metric),
193 },
194 }
195 }
196}
197
198#[derive(Debug, Error, Clone, PartialEq, Eq)]
199#[allow(missing_docs)]
200pub enum RoutePropertiesRequiredFields {
201 #[error("fuchsia.net.routes/RoutePropertiesV#.specified_properties")]
202 SpecifiedProperties,
203 #[error(transparent)]
204 WithinSpecifiedProperties(#[from] SpecifiedRoutePropertiesRequiredFields),
205}
206
207impl TryFrom<fnet_routes::RoutePropertiesV4> for RouteProperties {
208 type Error = FidlConversionError<RoutePropertiesRequiredFields>;
209 fn try_from(properties: fnet_routes::RoutePropertiesV4) -> Result<Self, Self::Error> {
210 Ok(RouteProperties {
211 specified_properties: properties
212 .specified_properties
213 .ok_or(FidlConversionError::RequiredFieldUnset(
214 RoutePropertiesRequiredFields::SpecifiedProperties,
215 ))?
216 .try_into()
217 .map_err(|e: FidlConversionError<_>| {
218 e.map_unset_fields(RoutePropertiesRequiredFields::WithinSpecifiedProperties)
219 })?,
220 })
221 }
222}
223
224impl TryFrom<fnet_routes::RoutePropertiesV6> for RouteProperties {
225 type Error = FidlConversionError<RoutePropertiesRequiredFields>;
226 fn try_from(properties: fnet_routes::RoutePropertiesV6) -> Result<Self, Self::Error> {
227 Ok(RouteProperties {
228 specified_properties: properties
229 .specified_properties
230 .ok_or(FidlConversionError::RequiredFieldUnset(
231 RoutePropertiesRequiredFields::SpecifiedProperties,
232 ))?
233 .try_into()
234 .map_err(|e: FidlConversionError<_>| {
235 e.map_unset_fields(RoutePropertiesRequiredFields::WithinSpecifiedProperties)
236 })?,
237 })
238 }
239}
240
241impl From<RouteProperties> for fnet_routes::RoutePropertiesV4 {
242 fn from(properties: RouteProperties) -> fnet_routes::RoutePropertiesV4 {
243 let RouteProperties { specified_properties } = properties;
244 fnet_routes::RoutePropertiesV4 {
245 specified_properties: Some(specified_properties.into()),
246 ..Default::default()
247 }
248 }
249}
250
251impl From<RouteProperties> for fnet_routes::RoutePropertiesV6 {
252 fn from(properties: RouteProperties) -> fnet_routes::RoutePropertiesV6 {
253 let RouteProperties { specified_properties } = properties;
254 fnet_routes::RoutePropertiesV6 {
255 specified_properties: Some(specified_properties.into()),
256 ..Default::default()
257 }
258 }
259}
260
261#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
269pub struct RouteTarget<I: Ip> {
270 pub outbound_interface: u64,
272 pub next_hop: Option<SpecifiedAddr<I::Addr>>,
274}
275
276impl TryFrom<fnet_routes::RouteTargetV4> for RouteTarget<Ipv4> {
277 type Error = FidlConversionError<NeverMissingFields>;
278 fn try_from(target: fnet_routes::RouteTargetV4) -> Result<Self, Self::Error> {
279 let fnet_routes::RouteTargetV4 { outbound_interface, next_hop } = target;
280 let next_hop: Option<SpecifiedAddr<net_types::ip::Ipv4Addr>> = next_hop
281 .map(|addr| {
282 SpecifiedAddr::new((*addr).into_ext())
283 .ok_or(FidlConversionError::UnspecifiedNextHop)
284 })
285 .transpose()?;
286 if let Some(next_hop) = next_hop {
287 if next_hop.is_limited_broadcast() {
288 return Err(FidlConversionError::NextHopNotUnicast);
289 }
290 }
291 Ok(RouteTarget { outbound_interface, next_hop })
292 }
293}
294
295impl TryFrom<fnet_routes::RouteTargetV6> for RouteTarget<Ipv6> {
296 type Error = FidlConversionError<NeverMissingFields>;
297 fn try_from(target: fnet_routes::RouteTargetV6) -> Result<Self, Self::Error> {
298 let fnet_routes::RouteTargetV6 { outbound_interface, next_hop } = target;
299 let addr: Option<SpecifiedAddr<Ipv6Addr>> = next_hop
300 .map(|addr| {
301 SpecifiedAddr::new((*addr).into_ext())
302 .ok_or(FidlConversionError::UnspecifiedNextHop)
303 })
304 .transpose()?;
305 if let Some(specified_addr) = addr {
306 if !specified_addr.is_unicast() {
307 return Err(FidlConversionError::NextHopNotUnicast);
308 }
309 }
310 Ok(RouteTarget { outbound_interface, next_hop: addr })
311 }
312}
313
314impl From<RouteTarget<Ipv4>> for fnet_routes::RouteTargetV4 {
315 fn from(target: RouteTarget<Ipv4>) -> fnet_routes::RouteTargetV4 {
316 let RouteTarget { outbound_interface, next_hop } = target;
317 fnet_routes::RouteTargetV4 {
318 outbound_interface: outbound_interface,
319 next_hop: next_hop.map(|addr| Box::new((*addr).into_ext())),
320 }
321 }
322}
323
324impl From<RouteTarget<Ipv6>> for fnet_routes::RouteTargetV6 {
325 fn from(target: RouteTarget<Ipv6>) -> fnet_routes::RouteTargetV6 {
326 let RouteTarget { outbound_interface, next_hop } = target;
327 fnet_routes::RouteTargetV6 {
328 outbound_interface: outbound_interface,
329 next_hop: next_hop.map(|addr| Box::new((*addr).into_ext())),
330 }
331 }
332}
333
334#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
341pub enum RouteAction<I: Ip> {
342 Unknown,
344 Forward(RouteTarget<I>),
346}
347
348#[derive(Debug, Error, PartialEq, Eq)]
349#[allow(missing_docs)]
350pub enum NeverMissingFields {}
351
352impl TryFrom<fnet_routes::RouteActionV4> for RouteAction<Ipv4> {
353 type Error = FidlConversionError<NeverMissingFields>;
354 fn try_from(action: fnet_routes::RouteActionV4) -> Result<Self, Self::Error> {
355 match action {
356 fnet_routes::RouteActionV4::Forward(target) => {
357 Ok(RouteAction::Forward(target.try_into()?))
358 }
359 fnet_routes::RouteActionV4Unknown!() => Ok(RouteAction::Unknown),
360 }
361 }
362}
363
364impl TryFrom<fnet_routes::RouteActionV6> for RouteAction<Ipv6> {
365 type Error = FidlConversionError<NeverMissingFields>;
366 fn try_from(action: fnet_routes::RouteActionV6) -> Result<Self, Self::Error> {
367 match action {
368 fnet_routes::RouteActionV6::Forward(target) => {
369 Ok(RouteAction::Forward(target.try_into()?))
370 }
371 fnet_routes::RouteActionV4Unknown!() => Ok(RouteAction::Unknown),
372 }
373 }
374}
375
376const ROUTE_ACTION_V4_UNKNOWN_VARIANT_TAG: &str = "fuchsia.net.routes/RouteActionV4";
377
378impl TryFrom<RouteAction<Ipv4>> for fnet_routes::RouteActionV4 {
379 type Error = NetTypeConversionError;
380 fn try_from(action: RouteAction<Ipv4>) -> Result<Self, Self::Error> {
381 match action {
382 RouteAction::Forward(target) => Ok(fnet_routes::RouteActionV4::Forward(target.into())),
383 RouteAction::Unknown => Err(NetTypeConversionError::UnknownUnionVariant(
384 ROUTE_ACTION_V4_UNKNOWN_VARIANT_TAG,
385 )),
386 }
387 }
388}
389
390const ROUTE_ACTION_V6_UNKNOWN_VARIANT_TAG: &str = "fuchsia.net.routes/RouteActionV6";
391
392impl TryFrom<RouteAction<Ipv6>> for fnet_routes::RouteActionV6 {
393 type Error = NetTypeConversionError;
394 fn try_from(action: RouteAction<Ipv6>) -> Result<Self, Self::Error> {
395 match action {
396 RouteAction::Forward(target) => Ok(fnet_routes::RouteActionV6::Forward(target.into())),
397 RouteAction::Unknown => Err(NetTypeConversionError::UnknownUnionVariant(
398 ROUTE_ACTION_V6_UNKNOWN_VARIANT_TAG,
399 )),
400 }
401 }
402}
403
404#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
410pub struct Route<I: Ip> {
411 pub destination: Subnet<I::Addr>,
413 pub action: RouteAction<I>,
415 pub properties: RouteProperties,
417}
418
419impl<I: Ip> Route<I> {
420 pub fn new_forward(
423 destination: Subnet<I::Addr>,
424 outbound_interface: u64,
425 next_hop: Option<SpecifiedAddr<I::Addr>>,
426 metric: fnet_routes::SpecifiedMetric,
427 ) -> Self {
428 Self {
429 destination,
430 action: RouteAction::Forward(RouteTarget { outbound_interface, next_hop }),
431 properties: RouteProperties {
432 specified_properties: SpecifiedRouteProperties { metric },
433 },
434 }
435 }
436
437 pub fn new_forward_with_inherited_metric(
440 destination: Subnet<I::Addr>,
441 outbound_interface: u64,
442 next_hop: Option<SpecifiedAddr<I::Addr>>,
443 ) -> Self {
444 Self::new_forward(
445 destination,
446 outbound_interface,
447 next_hop,
448 fnet_routes::SpecifiedMetric::InheritedFromInterface(fnet_routes::Empty),
449 )
450 }
451
452 pub fn new_forward_with_explicit_metric(
455 destination: Subnet<I::Addr>,
456 outbound_interface: u64,
457 next_hop: Option<SpecifiedAddr<I::Addr>>,
458 metric: u32,
459 ) -> Self {
460 Self::new_forward(
461 destination,
462 outbound_interface,
463 next_hop,
464 fnet_routes::SpecifiedMetric::ExplicitMetric(metric),
465 )
466 }
467}
468
469impl TryFrom<fnet_routes::RouteV4> for Route<Ipv4> {
470 type Error = FidlConversionError<RoutePropertiesRequiredFields>;
471 fn try_from(route: fnet_routes::RouteV4) -> Result<Self, Self::Error> {
472 let fnet_routes::RouteV4 { destination, action, properties } = route;
473 Ok(Route {
474 destination: destination
475 .try_into_ext()
476 .map_err(FidlConversionError::DestinationSubnet)?,
477 action: action
478 .try_into()
479 .map_err(|e: FidlConversionError<_>| e.map_unset_fields(|never| match never {}))?,
480 properties: properties.try_into()?,
481 })
482 }
483}
484
485impl TryFrom<fnet_routes::RouteV6> for Route<Ipv6> {
486 type Error = FidlConversionError<RoutePropertiesRequiredFields>;
487 fn try_from(route: fnet_routes::RouteV6) -> Result<Self, Self::Error> {
488 let fnet_routes::RouteV6 { destination, action, properties } = route;
489 let destination =
490 destination.try_into_ext().map_err(FidlConversionError::DestinationSubnet)?;
491 Ok(Route {
492 destination,
493 action: action
494 .try_into()
495 .map_err(|e: FidlConversionError<_>| e.map_unset_fields(|never| match never {}))?,
496 properties: properties.try_into()?,
497 })
498 }
499}
500
501impl TryFrom<Route<Ipv4>> for fnet_routes::RouteV4 {
502 type Error = NetTypeConversionError;
503 fn try_from(route: Route<Ipv4>) -> Result<Self, Self::Error> {
504 let Route { destination, action, properties } = route;
505 Ok(fnet_routes::RouteV4 {
506 destination: fnet::Ipv4AddressWithPrefix {
507 addr: destination.network().into_ext(),
508 prefix_len: destination.prefix(),
509 },
510 action: action.try_into()?,
511 properties: properties.into(),
512 })
513 }
514}
515
516impl TryFrom<Route<Ipv6>> for fnet_routes::RouteV6 {
517 type Error = NetTypeConversionError;
518 fn try_from(route: Route<Ipv6>) -> Result<Self, Self::Error> {
519 let Route { destination, action, properties } = route;
520 Ok(fnet_routes::RouteV6 {
521 destination: fnet::Ipv6AddressWithPrefix {
522 addr: destination.network().into_ext(),
523 prefix_len: destination.prefix(),
524 },
525 action: action.try_into()?,
526 properties: properties.into(),
527 })
528 }
529}
530
531impl<I: Ip> TryFrom<Route<I>> for fnet_stack::ForwardingEntry {
532 type Error = NetTypeConversionError;
533 fn try_from(
534 Route {
535 destination,
536 action,
537 properties:
538 RouteProperties { specified_properties: SpecifiedRouteProperties { metric } },
539 }: Route<I>,
540 ) -> Result<Self, Self::Error> {
541 let RouteTarget { outbound_interface, next_hop } = match action {
542 RouteAction::Unknown => {
543 return Err(NetTypeConversionError::UnknownUnionVariant(match I::VERSION {
544 net_types::ip::IpVersion::V4 => ROUTE_ACTION_V4_UNKNOWN_VARIANT_TAG,
545 net_types::ip::IpVersion::V6 => ROUTE_ACTION_V6_UNKNOWN_VARIANT_TAG,
546 }));
547 }
548 RouteAction::Forward(target) => target,
549 };
550
551 let next_hop = I::map_ip_in(
552 next_hop,
553 |next_hop| next_hop.map(|addr| fnet::IpAddress::Ipv4(addr.get().into_ext())),
554 |next_hop| next_hop.map(|addr| fnet::IpAddress::Ipv6(addr.get().into_ext())),
555 );
556
557 Ok(fnet_stack::ForwardingEntry {
558 subnet: destination.into_ext(),
559 device_id: outbound_interface,
560 next_hop: next_hop.map(Box::new),
561 metric: match metric {
562 fnet_routes::SpecifiedMetric::ExplicitMetric(metric) => metric,
563 fnet_routes::SpecifiedMetric::InheritedFromInterface(fnet_routes::Empty) => 0,
564 },
565 })
566 }
567}
568
569#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
572pub struct InstalledRoute<I: Ip> {
573 pub route: Route<I>,
575 pub effective_properties: EffectiveRouteProperties,
577 pub table_id: TableId,
579}
580
581impl<I: Ip> InstalledRoute<I> {
582 pub fn matches_route_and_table_id(&self, route: &Route<I>, table_id: TableId) -> bool {
584 &self.route == route && self.table_id == table_id
585 }
586}
587
588#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
590pub struct TableId(u32);
591
592impl TableId {
593 pub const fn new(id: u32) -> Self {
595 Self(id)
596 }
597
598 pub const fn get(self) -> u32 {
600 let Self(id) = self;
601 id
602 }
603}
604
605impl Display for TableId {
606 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
607 write!(f, "{}", self.get())
608 }
609}
610
611#[derive(Error, Clone, Debug, PartialEq, Eq)]
612#[allow(missing_docs)]
613pub enum InstalledRouteRequiredFields {
614 #[error("fuchsia.net.routes/InstalledRouteV#.route")]
615 Route,
616 #[error("fuchsia.net.routes/InstalledRouteV#.effective_properties")]
617 EffectiveProperties,
618 #[error(transparent)]
619 WithinRoute(#[from] RoutePropertiesRequiredFields),
620 #[error(transparent)]
621 WithinEffectiveProperties(#[from] EffectiveRoutePropertiesRequiredFields),
622 #[error("fuchsia.net.routes/InstalledRouteV#.table_id")]
623 TableId,
624}
625
626impl TryFrom<fnet_routes::InstalledRouteV4> for InstalledRoute<Ipv4> {
627 type Error = FidlConversionError<InstalledRouteRequiredFields>;
628 fn try_from(installed_route: fnet_routes::InstalledRouteV4) -> Result<Self, Self::Error> {
629 Ok(InstalledRoute {
630 route: installed_route
631 .route
632 .ok_or(FidlConversionError::RequiredFieldUnset(
633 InstalledRouteRequiredFields::Route,
634 ))?
635 .try_into()
636 .map_err(|e: FidlConversionError<_>| {
637 e.map_unset_fields(InstalledRouteRequiredFields::WithinRoute)
638 })?,
639 effective_properties: installed_route
640 .effective_properties
641 .ok_or(FidlConversionError::RequiredFieldUnset(
642 InstalledRouteRequiredFields::EffectiveProperties,
643 ))?
644 .try_into()
645 .map_err(|e: FidlConversionError<_>| {
646 e.map_unset_fields(InstalledRouteRequiredFields::WithinEffectiveProperties)
647 })?,
648 table_id: TableId(installed_route.table_id.ok_or(
649 FidlConversionError::RequiredFieldUnset(InstalledRouteRequiredFields::TableId),
650 )?),
651 })
652 }
653}
654
655impl TryFrom<fnet_routes::InstalledRouteV6> for InstalledRoute<Ipv6> {
656 type Error = FidlConversionError<InstalledRouteRequiredFields>;
657 fn try_from(installed_route: fnet_routes::InstalledRouteV6) -> Result<Self, Self::Error> {
658 Ok(InstalledRoute {
659 route: installed_route
660 .route
661 .ok_or(FidlConversionError::RequiredFieldUnset(
662 InstalledRouteRequiredFields::Route,
663 ))?
664 .try_into()
665 .map_err(|e: FidlConversionError<_>| {
666 e.map_unset_fields(InstalledRouteRequiredFields::WithinRoute)
667 })?,
668 effective_properties: installed_route
669 .effective_properties
670 .ok_or(FidlConversionError::RequiredFieldUnset(
671 InstalledRouteRequiredFields::EffectiveProperties,
672 ))?
673 .try_into()
674 .map_err(|e: FidlConversionError<_>| {
675 e.map_unset_fields(InstalledRouteRequiredFields::WithinEffectiveProperties)
676 })?,
677 table_id: TableId(installed_route.table_id.ok_or(
678 FidlConversionError::RequiredFieldUnset(InstalledRouteRequiredFields::TableId),
679 )?),
680 })
681 }
682}
683
684impl TryFrom<InstalledRoute<Ipv4>> for fnet_routes::InstalledRouteV4 {
685 type Error = NetTypeConversionError;
686 fn try_from(installed_route: InstalledRoute<Ipv4>) -> Result<Self, Self::Error> {
687 let InstalledRoute { route, effective_properties, table_id } = installed_route;
688 Ok(fnet_routes::InstalledRouteV4 {
689 route: Some(route.try_into()?),
690 effective_properties: Some(effective_properties.into()),
691 table_id: Some(table_id.get()),
692 ..Default::default()
693 })
694 }
695}
696
697impl TryFrom<InstalledRoute<Ipv6>> for fnet_routes::InstalledRouteV6 {
698 type Error = NetTypeConversionError;
699 fn try_from(installed_route: InstalledRoute<Ipv6>) -> Result<Self, Self::Error> {
700 let InstalledRoute { route, effective_properties, table_id } = installed_route;
701 Ok(fnet_routes::InstalledRouteV6 {
702 route: Some(route.try_into()?),
703 effective_properties: Some(effective_properties.into()),
704 table_id: Some(table_id.get()),
705 ..Default::default()
706 })
707 }
708}
709
710#[derive(Clone, Copy, Debug, PartialEq)]
717pub enum Event<I: Ip> {
718 Unknown,
720 Existing(InstalledRoute<I>),
722 Idle,
724 Added(InstalledRoute<I>),
726 Removed(InstalledRoute<I>),
728}
729
730impl TryFrom<fnet_routes::EventV4> for Event<Ipv4> {
731 type Error = FidlConversionError<InstalledRouteRequiredFields>;
732 fn try_from(event: fnet_routes::EventV4) -> Result<Self, Self::Error> {
733 match event {
734 fnet_routes::EventV4::Existing(route) => Ok(Event::Existing(route.try_into()?)),
735 fnet_routes::EventV4::Idle(fnet_routes::Empty) => Ok(Event::Idle),
736 fnet_routes::EventV4::Added(route) => Ok(Event::Added(route.try_into()?)),
737 fnet_routes::EventV4::Removed(route) => Ok(Event::Removed(route.try_into()?)),
738 fnet_routes::EventV4Unknown!() => Ok(Event::Unknown),
739 }
740 }
741}
742
743impl TryFrom<fnet_routes::EventV6> for Event<Ipv6> {
744 type Error = FidlConversionError<InstalledRouteRequiredFields>;
745 fn try_from(event: fnet_routes::EventV6) -> Result<Self, Self::Error> {
746 match event {
747 fnet_routes::EventV6::Existing(route) => Ok(Event::Existing(route.try_into()?)),
748 fnet_routes::EventV6::Idle(fnet_routes::Empty) => Ok(Event::Idle),
749 fnet_routes::EventV6::Added(route) => Ok(Event::Added(route.try_into()?)),
750 fnet_routes::EventV6::Removed(route) => Ok(Event::Removed(route.try_into()?)),
751 fnet_routes::EventV6Unknown!() => Ok(Event::Unknown),
752 }
753 }
754}
755
756impl TryFrom<Event<Ipv4>> for fnet_routes::EventV4 {
757 type Error = NetTypeConversionError;
758 fn try_from(event: Event<Ipv4>) -> Result<Self, Self::Error> {
759 match event {
760 Event::Existing(route) => Ok(fnet_routes::EventV4::Existing(route.try_into()?)),
761 Event::Idle => Ok(fnet_routes::EventV4::Idle(fnet_routes::Empty)),
762 Event::Added(route) => Ok(fnet_routes::EventV4::Added(route.try_into()?)),
763 Event::Removed(route) => Ok(fnet_routes::EventV4::Removed(route.try_into()?)),
764 Event::Unknown => {
765 Err(NetTypeConversionError::UnknownUnionVariant("fuchsia_net_routes.EventV4"))
766 }
767 }
768 }
769}
770
771impl TryFrom<Event<Ipv6>> for fnet_routes::EventV6 {
772 type Error = NetTypeConversionError;
773 fn try_from(event: Event<Ipv6>) -> Result<Self, Self::Error> {
774 match event {
775 Event::Existing(route) => Ok(fnet_routes::EventV6::Existing(route.try_into()?)),
776 Event::Idle => Ok(fnet_routes::EventV6::Idle(fnet_routes::Empty)),
777 Event::Added(route) => Ok(fnet_routes::EventV6::Added(route.try_into()?)),
778 Event::Removed(route) => Ok(fnet_routes::EventV6::Removed(route.try_into()?)),
779 Event::Unknown => {
780 Err(NetTypeConversionError::UnknownUnionVariant("fuchsia_net_routes.EventV6"))
781 }
782 }
783 }
784}
785
786#[derive(Clone, Debug, Error)]
788pub enum WatcherCreationError {
789 #[error("failed to create route watcher proxy: {0}")]
791 CreateProxy(fidl::Error),
792 #[error("failed to get route watcher: {0}")]
794 GetWatcher(fidl::Error),
795}
796
797#[derive(Clone, Debug, Error)]
799pub enum WatchError {
800 #[error("the call to `Watch()` failed: {0}")]
802 Fidl(fidl::Error),
803 #[error("failed to convert event returned by `Watch()`: {0}")]
805 Conversion(FidlConversionError<InstalledRouteRequiredFields>),
806 #[error("the call to `Watch()` returned an empty batch of events")]
808 EmptyEventBatch,
809}
810
811pub trait FidlRouteIpExt: Ip {
813 type StateMarker: flex_client::fidl::DiscoverableProtocolMarker;
815 type WatcherMarker: flex_client::fidl::ProtocolMarker;
817 type WatchEvent: TryInto<Event<Self>, Error = FidlConversionError<InstalledRouteRequiredFields>>
819 + TryFrom<Event<Self>, Error = NetTypeConversionError>
820 + Clone
821 + std::fmt::Debug
822 + PartialEq
823 + Unpin
824 + Send;
825 type Route: TryFrom<Route<Self>, Error = NetTypeConversionError>
827 + TryInto<Route<Self>, Error = FidlConversionError<RoutePropertiesRequiredFields>>
828 + std::fmt::Debug;
829}
830
831impl FidlRouteIpExt for Ipv4 {
832 type StateMarker = fnet_routes::StateV4Marker;
833 type WatcherMarker = fnet_routes::WatcherV4Marker;
834 type WatchEvent = fnet_routes::EventV4;
835 type Route = fnet_routes::RouteV4;
836}
837
838impl FidlRouteIpExt for Ipv6 {
839 type StateMarker = fnet_routes::StateV6Marker;
840 type WatcherMarker = fnet_routes::WatcherV6Marker;
841 type WatchEvent = fnet_routes::EventV6;
842 type Route = fnet_routes::RouteV6;
843}
844
845pub trait Responder: flex_client::fidl::Responder + Debug + Send {
847 type Payload;
849
850 fn send(self, result: Self::Payload) -> Result<(), fidl::Error>;
852}
853
854pub trait SliceResponder<Payload>: flex_client::fidl::Responder + Debug + Send {
860 fn send(self, payload: &[Payload]) -> Result<(), fidl::Error>;
862}
863
864macro_rules! impl_responder {
865 ($resp:ty, &[$payload:ty] $(,)?) => {
866 impl $crate::SliceResponder<$payload> for $resp {
867 fn send(self, result: &[$payload]) -> Result<(), fidl::Error> {
868 <$resp>::send(self, result)
869 }
870 }
871 };
872 ($resp:ty, $payload:ty $(,)?) => {
873 impl $crate::Responder for $resp {
874 type Payload = $payload;
875
876 fn send(self, result: Self::Payload) -> Result<(), fidl::Error> {
877 <$resp>::send(self, result)
878 }
879 }
880 };
881}
882pub(crate) use impl_responder;
883
884#[derive(Default, Clone)]
886pub struct WatcherOptions {
887 pub table_interest: Option<fnet_routes::TableInterest>,
889}
890
891impl From<WatcherOptions> for fnet_routes::WatcherOptionsV4 {
892 fn from(WatcherOptions { table_interest }: WatcherOptions) -> Self {
893 Self { table_interest, __source_breaking: fidl::marker::SourceBreaking }
894 }
895}
896
897impl From<WatcherOptions> for fnet_routes::WatcherOptionsV6 {
898 fn from(WatcherOptions { table_interest }: WatcherOptions) -> Self {
899 Self { table_interest, __source_breaking: fidl::marker::SourceBreaking }
900 }
901}
902
903impl From<fnet_routes::WatcherOptionsV4> for WatcherOptions {
904 fn from(
905 fnet_routes::WatcherOptionsV4 { table_interest, __source_breaking: _ }: fnet_routes::WatcherOptionsV4,
906 ) -> Self {
907 Self { table_interest }
908 }
909}
910
911impl From<fnet_routes::WatcherOptionsV6> for WatcherOptions {
912 fn from(
913 fnet_routes::WatcherOptionsV6 { table_interest, __source_breaking: _ }: fnet_routes::WatcherOptionsV6,
914 ) -> Self {
915 Self { table_interest }
916 }
917}
918
919pub fn get_watcher<I: FidlRouteIpExt>(
921 state_proxy: &<I::StateMarker as flex_client::fidl::ProtocolMarker>::Proxy,
922 options: WatcherOptions,
923) -> Result<<I::WatcherMarker as flex_client::fidl::ProtocolMarker>::Proxy, WatcherCreationError> {
924 use flex_client::ProxyHasDomain as _;
925 let (watcher_proxy, watcher_server_end) =
926 state_proxy.domain().create_proxy::<I::WatcherMarker>();
927
928 #[derive(GenericOverIp)]
929 #[generic_over_ip(I, Ip)]
930 struct GetWatcherInputs<'a, I: FidlRouteIpExt> {
931 watcher_server_end: flex_client::fidl::ServerEnd<I::WatcherMarker>,
932 state_proxy: &'a <I::StateMarker as flex_client::fidl::ProtocolMarker>::Proxy,
933 options: WatcherOptions,
934 }
935 let result = I::map_ip_in(
936 GetWatcherInputs::<'_, I> { watcher_server_end, state_proxy, options },
937 |GetWatcherInputs { watcher_server_end, state_proxy, options }| {
938 state_proxy.get_watcher_v4(watcher_server_end, &options.into())
939 },
940 |GetWatcherInputs { watcher_server_end, state_proxy, options }| {
941 state_proxy.get_watcher_v6(watcher_server_end, &options.into())
942 },
943 );
944
945 result.map_err(WatcherCreationError::GetWatcher)?;
946 Ok(watcher_proxy)
947}
948
949pub fn watch<'a, I: FidlRouteIpExt>(
951 watcher_proxy: &'a <I::WatcherMarker as flex_client::fidl::ProtocolMarker>::Proxy,
952) -> impl Future<Output = Result<Vec<I::WatchEvent>, fidl::Error>> {
953 #[derive(GenericOverIp)]
954 #[generic_over_ip(I, Ip)]
955 struct WatchInputs<'a, I: FidlRouteIpExt> {
956 watcher_proxy: &'a <I::WatcherMarker as flex_client::fidl::ProtocolMarker>::Proxy,
957 }
958 #[derive(GenericOverIp)]
959 #[generic_over_ip(I, Ip)]
960 struct WatchOutputs<I: FidlRouteIpExt> {
961 watch_fut: fidl::client::QueryResponseFut<Vec<I::WatchEvent>, flex_client::Dialect>,
962 }
963 let WatchOutputs { watch_fut } = I::map_ip::<WatchInputs<'_, I>, WatchOutputs<I>>(
964 WatchInputs { watcher_proxy },
965 |WatchInputs { watcher_proxy }| WatchOutputs { watch_fut: watcher_proxy.watch() },
966 |WatchInputs { watcher_proxy }| WatchOutputs { watch_fut: watcher_proxy.watch() },
967 );
968 watch_fut
969}
970
971pub fn event_stream_from_state<I: FidlRouteIpExt>(
973 routes_state: &<I::StateMarker as flex_client::fidl::ProtocolMarker>::Proxy,
974) -> Result<impl Stream<Item = Result<Event<I>, WatchError>> + use<I>, WatcherCreationError> {
975 event_stream_from_state_with_options(routes_state, Default::default())
976}
977
978pub fn event_stream_from_state_with_options<I: FidlRouteIpExt>(
985 routes_state: &<I::StateMarker as flex_client::fidl::ProtocolMarker>::Proxy,
986 options: WatcherOptions,
987) -> Result<impl Stream<Item = Result<Event<I>, WatchError>> + use<I>, WatcherCreationError> {
988 let watcher = get_watcher::<I>(routes_state, options)?;
989 event_stream_from_watcher(watcher)
990}
991
992pub fn event_stream_from_watcher<I: FidlRouteIpExt>(
999 watcher: <I::WatcherMarker as flex_client::fidl::ProtocolMarker>::Proxy,
1000) -> Result<impl Stream<Item = Result<Event<I>, WatchError>> + use<I>, WatcherCreationError> {
1001 Ok(stream::ShortCircuit::new(
1002 futures::stream::try_unfold(watcher, |watcher| async {
1003 let events_batch = watch::<I>(&watcher).await.map_err(WatchError::Fidl)?;
1004 if events_batch.is_empty() {
1005 return Err(WatchError::EmptyEventBatch);
1006 }
1007 let events_batch = events_batch
1008 .into_iter()
1009 .map(|event| event.try_into().map_err(WatchError::Conversion));
1010 let event_stream = futures::stream::iter(events_batch);
1011 Ok(Some((event_stream, watcher)))
1012 })
1013 .try_flatten(),
1015 ))
1016}
1017
1018#[derive(Clone, Debug, Error)]
1020pub enum CollectRoutesUntilIdleError<I: FidlRouteIpExt> {
1021 #[error("there was an error in the event stream: {0}")]
1023 ErrorInStream(WatchError),
1024 #[error("there was an unexpected event in the event stream: {0:?}")]
1027 UnexpectedEvent(Event<I>),
1028 #[error("the event stream unexpectedly ended")]
1030 StreamEnded,
1031}
1032
1033pub async fn collect_routes_until_idle<
1036 I: FidlRouteIpExt,
1037 C: Extend<InstalledRoute<I>> + Default,
1038>(
1039 event_stream: impl futures::Stream<Item = Result<Event<I>, WatchError>> + Unpin,
1040) -> Result<C, CollectRoutesUntilIdleError<I>> {
1041 fold::fold_while(
1042 event_stream,
1043 Ok(C::default()),
1044 |existing_routes: Result<C, CollectRoutesUntilIdleError<I>>, event| {
1045 futures::future::ready(match existing_routes {
1046 Err(_) => {
1047 unreachable!("`existing_routes` must be `Ok`, because we stop folding on err")
1048 }
1049 Ok(mut existing_routes) => match event {
1050 Err(e) => {
1051 fold::FoldWhile::Done(Err(CollectRoutesUntilIdleError::ErrorInStream(e)))
1052 }
1053 Ok(e) => match e {
1054 Event::Existing(e) => {
1055 existing_routes.extend([e]);
1056 fold::FoldWhile::Continue(Ok(existing_routes))
1057 }
1058 Event::Idle => fold::FoldWhile::Done(Ok(existing_routes)),
1059 e @ Event::Unknown | e @ Event::Added(_) | e @ Event::Removed(_) => {
1060 fold::FoldWhile::Done(Err(
1061 CollectRoutesUntilIdleError::UnexpectedEvent(e),
1062 ))
1063 }
1064 },
1065 },
1066 })
1067 },
1068 )
1069 .await
1070 .short_circuited()
1071 .map_err(|_accumulated_thus_far: Result<C, CollectRoutesUntilIdleError<I>>| {
1072 CollectRoutesUntilIdleError::StreamEnded
1073 })?
1074}
1075
1076#[derive(Clone, Debug, Error)]
1078pub enum WaitForRoutesError<I: FidlRouteIpExt> {
1079 #[error("there was an error in the event stream: {0}")]
1081 ErrorInStream(WatchError),
1082 #[error("observed an added event for an already existing route: {0:?}")]
1084 AddedAlreadyExisting(InstalledRoute<I>),
1085 #[error("observed a removed event for a non-existent route: {0:?}")]
1087 RemovedNonExistent(InstalledRoute<I>),
1088 #[error("observed an unknown event")]
1090 UnknownEvent,
1091 #[error("the event stream unexpectedly ended")]
1093 StreamEnded,
1094}
1095
1096pub async fn wait_for_routes_map<
1104 I: FidlRouteIpExt,
1105 S: futures::Stream<Item = Result<Event<I>, WatchError>> + Unpin,
1106 T,
1107 F: Fn(&HashSet<InstalledRoute<I>>) -> Option<T>,
1108>(
1109 event_stream: S,
1110 initial_state: &mut HashSet<InstalledRoute<I>>,
1111 predicate: F,
1112) -> Result<T, WaitForRoutesError<I>> {
1113 fold::try_fold_while(
1114 event_stream.map_err(WaitForRoutesError::ErrorInStream),
1115 initial_state,
1116 |accumulated_routes, event| {
1117 futures::future::ready({
1118 match event {
1119 Event::Existing(route) | Event::Added(route) => accumulated_routes
1120 .insert(route)
1121 .then_some(())
1122 .ok_or(WaitForRoutesError::AddedAlreadyExisting(route)),
1123 Event::Removed(route) => accumulated_routes
1124 .remove(&route)
1125 .then_some(())
1126 .ok_or(WaitForRoutesError::RemovedNonExistent(route)),
1127 Event::Idle => Ok(()),
1128 Event::Unknown => Err(WaitForRoutesError::UnknownEvent),
1129 }
1130 .map(|()| match predicate(&accumulated_routes) {
1131 Some(t) => fold::FoldWhile::Done(t),
1132 None => fold::FoldWhile::Continue(accumulated_routes),
1133 })
1134 })
1135 },
1136 )
1137 .await?
1138 .short_circuited()
1139 .map_err(|_accumulated_thus_far: &mut HashSet<InstalledRoute<I>>| {
1140 WaitForRoutesError::StreamEnded
1141 })
1142}
1143
1144pub async fn wait_for_routes<
1150 I: FidlRouteIpExt,
1151 S: futures::Stream<Item = Result<Event<I>, WatchError>> + Unpin,
1152 F: Fn(&HashSet<InstalledRoute<I>>) -> bool,
1153>(
1154 event_stream: S,
1155 initial_state: &mut HashSet<InstalledRoute<I>>,
1156 predicate: F,
1157) -> Result<(), WaitForRoutesError<I>> {
1158 wait_for_routes_map::<I, S, (), _>(event_stream, initial_state, |routes| {
1159 predicate(routes).then_some(())
1160 })
1161 .await
1162}
1163
1164#[derive(Debug, Default, Clone)]
1166pub struct ResolveOptions {
1167 pub marks: fnet_ext::Marks,
1169}
1170
1171impl From<fnet_routes::ResolveOptions> for ResolveOptions {
1172 fn from(value: fnet_routes::ResolveOptions) -> Self {
1173 let fnet_routes::ResolveOptions { marks, __source_breaking } = value;
1174 Self { marks: marks.map(fnet_ext::Marks::from).unwrap_or_default() }
1175 }
1176}
1177
1178impl From<ResolveOptions> for fnet_routes::ResolveOptions {
1179 fn from(value: ResolveOptions) -> Self {
1180 let ResolveOptions { marks } = value;
1181 Self { marks: Some(marks.into()), __source_breaking: fidl::marker::SourceBreaking }
1182 }
1183}
1184
1185#[cfg(test)]
1186mod tests {
1187 use super::*;
1188 use crate::testutil::internal as internal_testutil;
1189 use assert_matches::assert_matches;
1190 use flex_fuchsia_net as _;
1191 use futures::{FutureExt as _, StreamExt as _};
1192 use ip_test_macro::ip_test;
1193 use net_declare::{
1194 fidl_ip_v4, fidl_ip_v4_with_prefix, fidl_ip_v6, fidl_ip_v6_with_prefix, net_ip_v4,
1195 net_ip_v6, net_subnet_v4, net_subnet_v6,
1196 };
1197 use test_case::test_case;
1198 use zx_status;
1199
1200 const ARBITRARY_TABLE_ID: TableId = TableId::new(0);
1201
1202 trait ArbitraryTestValue {
1204 fn arbitrary_test_value() -> Self;
1205 }
1206
1207 impl ArbitraryTestValue for fnet_routes::SpecifiedRouteProperties {
1208 fn arbitrary_test_value() -> Self {
1209 fnet_routes::SpecifiedRouteProperties {
1210 metric: Some(fnet_routes::SpecifiedMetric::ExplicitMetric(0)),
1211 ..Default::default()
1212 }
1213 }
1214 }
1215
1216 impl ArbitraryTestValue for fnet_routes::EffectiveRouteProperties {
1217 fn arbitrary_test_value() -> Self {
1218 fnet_routes::EffectiveRouteProperties { metric: Some(0), ..Default::default() }
1219 }
1220 }
1221
1222 impl ArbitraryTestValue for fnet_routes::RoutePropertiesV4 {
1223 fn arbitrary_test_value() -> Self {
1224 fnet_routes::RoutePropertiesV4 {
1225 specified_properties: Some(
1226 fnet_routes::SpecifiedRouteProperties::arbitrary_test_value(),
1227 ),
1228 ..Default::default()
1229 }
1230 }
1231 }
1232
1233 impl ArbitraryTestValue for fnet_routes::RoutePropertiesV6 {
1234 fn arbitrary_test_value() -> Self {
1235 fnet_routes::RoutePropertiesV6 {
1236 specified_properties: Some(
1237 fnet_routes::SpecifiedRouteProperties::arbitrary_test_value(),
1238 ),
1239 ..Default::default()
1240 }
1241 }
1242 }
1243
1244 impl ArbitraryTestValue for fnet_routes::RouteTargetV4 {
1245 fn arbitrary_test_value() -> Self {
1246 fnet_routes::RouteTargetV4 { outbound_interface: 1, next_hop: None }
1247 }
1248 }
1249
1250 impl ArbitraryTestValue for fnet_routes::RouteTargetV6 {
1251 fn arbitrary_test_value() -> Self {
1252 fnet_routes::RouteTargetV6 { outbound_interface: 1, next_hop: None }
1253 }
1254 }
1255
1256 impl ArbitraryTestValue for fnet_routes::RouteActionV4 {
1257 fn arbitrary_test_value() -> Self {
1258 fnet_routes::RouteActionV4::Forward(fnet_routes::RouteTargetV4::arbitrary_test_value())
1259 }
1260 }
1261
1262 impl ArbitraryTestValue for fnet_routes::RouteActionV6 {
1263 fn arbitrary_test_value() -> Self {
1264 fnet_routes::RouteActionV6::Forward(fnet_routes::RouteTargetV6::arbitrary_test_value())
1265 }
1266 }
1267
1268 impl ArbitraryTestValue for fnet_routes::RouteV4 {
1269 fn arbitrary_test_value() -> Self {
1270 fnet_routes::RouteV4 {
1271 destination: fidl_ip_v4_with_prefix!("192.168.0.0/24"),
1272 action: fnet_routes::RouteActionV4::arbitrary_test_value(),
1273 properties: fnet_routes::RoutePropertiesV4::arbitrary_test_value(),
1274 }
1275 }
1276 }
1277
1278 impl ArbitraryTestValue for fnet_routes::RouteV6 {
1279 fn arbitrary_test_value() -> Self {
1280 fnet_routes::RouteV6 {
1281 destination: fidl_ip_v6_with_prefix!("fe80::0/64"),
1282 action: fnet_routes::RouteActionV6::arbitrary_test_value(),
1283 properties: fnet_routes::RoutePropertiesV6::arbitrary_test_value(),
1284 }
1285 }
1286 }
1287
1288 impl ArbitraryTestValue for fnet_routes::InstalledRouteV4 {
1289 fn arbitrary_test_value() -> Self {
1290 fnet_routes::InstalledRouteV4 {
1291 route: Some(fnet_routes::RouteV4::arbitrary_test_value()),
1292 effective_properties: Some(
1293 fnet_routes::EffectiveRouteProperties::arbitrary_test_value(),
1294 ),
1295 table_id: Some(ARBITRARY_TABLE_ID.get()),
1296 ..Default::default()
1297 }
1298 }
1299 }
1300
1301 impl ArbitraryTestValue for fnet_routes::InstalledRouteV6 {
1302 fn arbitrary_test_value() -> Self {
1303 fnet_routes::InstalledRouteV6 {
1304 route: Some(fnet_routes::RouteV6::arbitrary_test_value()),
1305 effective_properties: Some(
1306 fnet_routes::EffectiveRouteProperties::arbitrary_test_value(),
1307 ),
1308 table_id: Some(ARBITRARY_TABLE_ID.get()),
1309 ..Default::default()
1310 }
1311 }
1312 }
1313
1314 #[test]
1315 fn specified_route_properties_try_from_unset_metric() {
1316 assert_eq!(
1317 SpecifiedRouteProperties::try_from(fnet_routes::SpecifiedRouteProperties::default()),
1318 Err(FidlConversionError::RequiredFieldUnset(
1319 SpecifiedRoutePropertiesRequiredFields::Metric
1320 ))
1321 )
1322 }
1323
1324 #[test]
1325 fn specified_route_properties_try_from() {
1326 let fidl_type = fnet_routes::SpecifiedRouteProperties {
1327 metric: Some(fnet_routes::SpecifiedMetric::ExplicitMetric(1)),
1328 ..Default::default()
1329 };
1330 let local_type =
1331 SpecifiedRouteProperties { metric: fnet_routes::SpecifiedMetric::ExplicitMetric(1) };
1332 assert_eq!(fidl_type.clone().try_into(), Ok(local_type));
1333 assert_eq!(
1334 <SpecifiedRouteProperties as std::convert::Into<
1335 fnet_routes::SpecifiedRouteProperties,
1336 >>::into(local_type),
1337 fidl_type.clone()
1338 );
1339 }
1340
1341 #[test]
1342 fn effective_route_properties_try_from_unset_metric() {
1343 assert_eq!(
1344 EffectiveRouteProperties::try_from(fnet_routes::EffectiveRouteProperties::default()),
1345 Err(FidlConversionError::RequiredFieldUnset(
1346 EffectiveRoutePropertiesRequiredFields::Metric
1347 ))
1348 )
1349 }
1350
1351 #[test]
1352 fn effective_route_properties_try_from() {
1353 let fidl_type =
1354 fnet_routes::EffectiveRouteProperties { metric: Some(1), ..Default::default() };
1355 let local_type = EffectiveRouteProperties { metric: 1 };
1356 assert_eq!(fidl_type.clone().try_into(), Ok(EffectiveRouteProperties { metric: 1 }));
1357 assert_eq!(
1358 <EffectiveRouteProperties as std::convert::Into<
1359 fnet_routes::EffectiveRouteProperties,
1360 >>::into(local_type),
1361 fidl_type.clone()
1362 );
1363 }
1364
1365 #[test]
1366 fn route_properties_try_from_unset_specified_properties_v4() {
1367 assert_eq!(
1368 RouteProperties::try_from(fnet_routes::RoutePropertiesV4::default()),
1369 Err(FidlConversionError::RequiredFieldUnset(
1370 RoutePropertiesRequiredFields::SpecifiedProperties
1371 ))
1372 )
1373 }
1374
1375 #[test]
1376 fn route_properties_try_from_unset_specified_properties_v6() {
1377 assert_eq!(
1378 RouteProperties::try_from(fnet_routes::RoutePropertiesV6::default()),
1379 Err(FidlConversionError::RequiredFieldUnset(
1380 RoutePropertiesRequiredFields::SpecifiedProperties
1381 ))
1382 )
1383 }
1384
1385 #[test]
1386 fn route_properties_try_from_v4() {
1387 let fidl_type = fnet_routes::RoutePropertiesV4 {
1388 specified_properties: Some(
1389 fnet_routes::SpecifiedRouteProperties::arbitrary_test_value(),
1390 ),
1391 ..Default::default()
1392 };
1393 let local_type = RouteProperties {
1394 specified_properties: fnet_routes::SpecifiedRouteProperties::arbitrary_test_value()
1395 .try_into()
1396 .unwrap(),
1397 };
1398 assert_eq!(fidl_type.clone().try_into(), Ok(local_type));
1399 assert_eq!(
1400 <RouteProperties as std::convert::Into<fnet_routes::RoutePropertiesV4>>::into(
1401 local_type
1402 ),
1403 fidl_type.clone()
1404 );
1405 }
1406
1407 #[test]
1408 fn route_properties_try_from_v6() {
1409 let fidl_type = fnet_routes::RoutePropertiesV6 {
1410 specified_properties: Some(
1411 fnet_routes::SpecifiedRouteProperties::arbitrary_test_value(),
1412 ),
1413 ..Default::default()
1414 };
1415 let local_type = RouteProperties {
1416 specified_properties: fnet_routes::SpecifiedRouteProperties::arbitrary_test_value()
1417 .try_into()
1418 .unwrap(),
1419 };
1420 assert_eq!(fidl_type.clone().try_into(), Ok(local_type));
1421 assert_eq!(
1422 <RouteProperties as std::convert::Into<fnet_routes::RoutePropertiesV6>>::into(
1423 local_type
1424 ),
1425 fidl_type.clone()
1426 );
1427 }
1428
1429 #[test]
1430 fn route_target_try_from_unspecified_next_hop_v4() {
1431 assert_eq!(
1432 RouteTarget::try_from(fnet_routes::RouteTargetV4 {
1433 outbound_interface: 1,
1434 next_hop: Some(Box::new(fidl_ip_v4!("0.0.0.0"))),
1435 }),
1436 Err(FidlConversionError::UnspecifiedNextHop)
1437 )
1438 }
1439
1440 #[test]
1441 fn route_target_try_from_unspecified_next_hop_v6() {
1442 assert_eq!(
1443 RouteTarget::try_from(fnet_routes::RouteTargetV6 {
1444 outbound_interface: 1,
1445 next_hop: Some(Box::new(fidl_ip_v6!("::"))),
1446 }),
1447 Err(FidlConversionError::UnspecifiedNextHop)
1448 );
1449 }
1450
1451 #[test]
1452 fn route_target_try_from_limited_broadcast_next_hop_v4() {
1453 assert_eq!(
1454 RouteTarget::try_from(fnet_routes::RouteTargetV4 {
1455 outbound_interface: 1,
1456 next_hop: Some(Box::new(fidl_ip_v4!("255.255.255.255"))),
1457 }),
1458 Err(FidlConversionError::NextHopNotUnicast)
1459 )
1460 }
1461
1462 #[test]
1463 fn route_target_try_from_multicast_next_hop_v6() {
1464 assert_eq!(
1465 RouteTarget::try_from(fnet_routes::RouteTargetV6 {
1466 outbound_interface: 1,
1467 next_hop: Some(Box::new(fidl_ip_v6!("ff00::1"))),
1468 }),
1469 Err(FidlConversionError::NextHopNotUnicast)
1470 )
1471 }
1472
1473 #[test]
1474 fn route_target_try_from_v4() {
1475 let fidl_type = fnet_routes::RouteTargetV4 {
1476 outbound_interface: 1,
1477 next_hop: Some(Box::new(fidl_ip_v4!("192.168.0.1"))),
1478 };
1479 let local_type = RouteTarget {
1480 outbound_interface: 1,
1481 next_hop: Some(SpecifiedAddr::new(net_ip_v4!("192.168.0.1")).unwrap()),
1482 };
1483 assert_eq!(fidl_type.clone().try_into(), Ok(local_type));
1484 assert_eq!(
1485 <RouteTarget<Ipv4> as std::convert::Into<fnet_routes::RouteTargetV4>>::into(local_type),
1486 fidl_type
1487 );
1488 }
1489
1490 #[test]
1491 fn route_target_try_from_v6() {
1492 let fidl_type = fnet_routes::RouteTargetV6 {
1493 outbound_interface: 1,
1494 next_hop: Some(Box::new(fidl_ip_v6!("fe80::1"))),
1495 };
1496 let local_type = RouteTarget {
1497 outbound_interface: 1,
1498 next_hop: Some(SpecifiedAddr::new(net_ip_v6!("fe80::1")).unwrap()),
1499 };
1500 assert_eq!(fidl_type.clone().try_into(), Ok(local_type));
1501 assert_eq!(
1502 <RouteTarget<Ipv6> as std::convert::Into<fnet_routes::RouteTargetV6>>::into(local_type),
1503 fidl_type
1504 );
1505 }
1506
1507 #[test]
1508 fn route_action_try_from_forward_v4() {
1509 let fidl_type =
1510 fnet_routes::RouteActionV4::Forward(fnet_routes::RouteTargetV4::arbitrary_test_value());
1511 let local_type = RouteAction::Forward(
1512 fnet_routes::RouteTargetV4::arbitrary_test_value().try_into().unwrap(),
1513 );
1514 assert_eq!(fidl_type.clone().try_into(), Ok(local_type));
1515 assert_eq!(local_type.try_into(), Ok(fidl_type.clone()));
1516 }
1517
1518 #[test]
1519 fn route_action_try_from_forward_v6() {
1520 let fidl_type =
1521 fnet_routes::RouteActionV6::Forward(fnet_routes::RouteTargetV6::arbitrary_test_value());
1522 let local_type = RouteAction::Forward(
1523 fnet_routes::RouteTargetV6::arbitrary_test_value().try_into().unwrap(),
1524 );
1525 assert_eq!(fidl_type.clone().try_into(), Ok(local_type));
1526 assert_eq!(local_type.try_into(), Ok(fidl_type.clone()));
1527 }
1528
1529 #[test]
1530 fn route_action_try_from_unknown_v4() {
1531 let fidl_type = fnet_routes::RouteActionV4::unknown_variant_for_testing();
1532 const LOCAL_TYPE: RouteAction<Ipv4> = RouteAction::Unknown;
1533 assert_eq!(fidl_type.try_into(), Ok(LOCAL_TYPE));
1534 assert_eq!(
1535 LOCAL_TYPE.try_into(),
1536 Err::<fnet_routes::RouteActionV4, _>(NetTypeConversionError::UnknownUnionVariant(
1537 "fuchsia.net.routes/RouteActionV4"
1538 ))
1539 );
1540 }
1541
1542 #[test]
1543 fn route_action_try_from_unknown_v6() {
1544 let fidl_type = fnet_routes::RouteActionV6::unknown_variant_for_testing();
1545 const LOCAL_TYPE: RouteAction<Ipv6> = RouteAction::Unknown;
1546 assert_eq!(fidl_type.try_into(), Ok(LOCAL_TYPE));
1547 assert_eq!(
1548 LOCAL_TYPE.try_into(),
1549 Err::<fnet_routes::RouteActionV6, _>(NetTypeConversionError::UnknownUnionVariant(
1550 "fuchsia.net.routes/RouteActionV6"
1551 ))
1552 );
1553 }
1554
1555 #[test]
1556 fn route_try_from_invalid_destination_v4() {
1557 assert_matches!(
1558 Route::try_from(fnet_routes::RouteV4 {
1559 destination: fidl_ip_v4_with_prefix!("192.168.0.1/24"),
1561 action: fnet_routes::RouteActionV4::arbitrary_test_value(),
1562 properties: fnet_routes::RoutePropertiesV4::arbitrary_test_value(),
1563 }),
1564 Err(FidlConversionError::DestinationSubnet(_))
1565 );
1566 }
1567
1568 #[test]
1569 fn route_try_from_invalid_destination_v6() {
1570 assert_matches!(
1571 Route::try_from(fnet_routes::RouteV6 {
1572 destination: fidl_ip_v6_with_prefix!("fe80::1/64"),
1574 action: fnet_routes::RouteActionV6::arbitrary_test_value(),
1575 properties: fnet_routes::RoutePropertiesV6::arbitrary_test_value(),
1576 }),
1577 Err(FidlConversionError::DestinationSubnet(_))
1578 );
1579 }
1580
1581 #[test]
1582 fn route_try_from_v4() {
1583 let fidl_type = fnet_routes::RouteV4 {
1584 destination: fidl_ip_v4_with_prefix!("192.168.0.0/24"),
1585 action: fnet_routes::RouteActionV4::arbitrary_test_value(),
1586 properties: fnet_routes::RoutePropertiesV4::arbitrary_test_value(),
1587 };
1588 let local_type = Route {
1589 destination: net_subnet_v4!("192.168.0.0/24"),
1590 action: fnet_routes::RouteActionV4::arbitrary_test_value().try_into().unwrap(),
1591 properties: fnet_routes::RoutePropertiesV4::arbitrary_test_value().try_into().unwrap(),
1592 };
1593 assert_eq!(fidl_type.clone().try_into(), Ok(local_type));
1594 assert_eq!(local_type.try_into(), Ok(fidl_type.clone()));
1595 }
1596
1597 #[test]
1598 fn route_try_from_v6() {
1599 let fidl_type = fnet_routes::RouteV6 {
1600 destination: fidl_ip_v6_with_prefix!("fe80::0/64"),
1601 action: fnet_routes::RouteActionV6::arbitrary_test_value(),
1602 properties: fnet_routes::RoutePropertiesV6::arbitrary_test_value(),
1603 };
1604 let local_type = Route {
1605 destination: net_subnet_v6!("fe80::0/64"),
1606 action: fnet_routes::RouteActionV6::arbitrary_test_value().try_into().unwrap(),
1607 properties: fnet_routes::RoutePropertiesV6::arbitrary_test_value().try_into().unwrap(),
1608 };
1609 assert_eq!(fidl_type.clone().try_into(), Ok(local_type));
1610 assert_eq!(local_type.try_into(), Ok(fidl_type.clone()));
1611 }
1612
1613 #[test]
1614 fn installed_route_try_from_unset_route_v4() {
1615 assert_eq!(
1616 InstalledRoute::try_from(fnet_routes::InstalledRouteV4 {
1617 route: None,
1618 effective_properties: Some(
1619 fnet_routes::EffectiveRouteProperties::arbitrary_test_value(),
1620 ),
1621 table_id: Some(ARBITRARY_TABLE_ID.get()),
1622 ..Default::default()
1623 }),
1624 Err(FidlConversionError::RequiredFieldUnset(InstalledRouteRequiredFields::Route))
1625 )
1626 }
1627
1628 #[test]
1629 fn installed_route_try_from_unset_route_v6() {
1630 assert_eq!(
1631 InstalledRoute::try_from(fnet_routes::InstalledRouteV6 {
1632 route: None,
1633 effective_properties: Some(
1634 fnet_routes::EffectiveRouteProperties::arbitrary_test_value(),
1635 ),
1636 table_id: Some(ARBITRARY_TABLE_ID.get()),
1637 ..Default::default()
1638 }),
1639 Err(FidlConversionError::RequiredFieldUnset(InstalledRouteRequiredFields::Route))
1640 )
1641 }
1642
1643 #[test]
1644 fn installed_route_try_from_unset_effective_properties_v4() {
1645 assert_eq!(
1646 InstalledRoute::try_from(fnet_routes::InstalledRouteV4 {
1647 route: Some(fnet_routes::RouteV4::arbitrary_test_value()),
1648 effective_properties: None,
1649 table_id: Some(ARBITRARY_TABLE_ID.get()),
1650 ..Default::default()
1651 }),
1652 Err(FidlConversionError::RequiredFieldUnset(
1653 InstalledRouteRequiredFields::EffectiveProperties
1654 ))
1655 )
1656 }
1657
1658 #[test]
1659 fn installed_route_try_from_unset_effective_properties_v6() {
1660 assert_eq!(
1661 InstalledRoute::try_from(fnet_routes::InstalledRouteV6 {
1662 route: Some(fnet_routes::RouteV6::arbitrary_test_value()),
1663 effective_properties: None,
1664 table_id: Some(ARBITRARY_TABLE_ID.get()),
1665 ..Default::default()
1666 }),
1667 Err(FidlConversionError::RequiredFieldUnset(
1668 InstalledRouteRequiredFields::EffectiveProperties
1669 ))
1670 )
1671 }
1672
1673 #[test]
1674 fn installed_route_try_from_v4() {
1675 let fidl_type = fnet_routes::InstalledRouteV4 {
1676 route: Some(fnet_routes::RouteV4::arbitrary_test_value()),
1677 effective_properties: Some(
1678 fnet_routes::EffectiveRouteProperties::arbitrary_test_value(),
1679 ),
1680 table_id: Some(ARBITRARY_TABLE_ID.get()),
1681 ..Default::default()
1682 };
1683 let local_type = InstalledRoute {
1684 route: fnet_routes::RouteV4::arbitrary_test_value().try_into().unwrap(),
1685 effective_properties: fnet_routes::EffectiveRouteProperties::arbitrary_test_value()
1686 .try_into()
1687 .unwrap(),
1688 table_id: ARBITRARY_TABLE_ID,
1689 };
1690 assert_eq!(fidl_type.clone().try_into(), Ok(local_type));
1691 assert_eq!(local_type.try_into(), Ok(fidl_type.clone()));
1692 }
1693
1694 #[test]
1695 fn installed_route_try_from_v6() {
1696 let fidl_type = fnet_routes::InstalledRouteV6 {
1697 route: Some(fnet_routes::RouteV6::arbitrary_test_value()),
1698 effective_properties: Some(
1699 fnet_routes::EffectiveRouteProperties::arbitrary_test_value(),
1700 ),
1701 table_id: Some(ARBITRARY_TABLE_ID.get()),
1702 ..Default::default()
1703 };
1704 let local_type = InstalledRoute {
1705 route: fnet_routes::RouteV6::arbitrary_test_value().try_into().unwrap(),
1706 effective_properties: fnet_routes::EffectiveRouteProperties::arbitrary_test_value()
1707 .try_into()
1708 .unwrap(),
1709 table_id: ARBITRARY_TABLE_ID,
1710 };
1711 assert_eq!(fidl_type.clone().try_into(), Ok(local_type));
1712 assert_eq!(local_type.try_into(), Ok(fidl_type.clone()));
1713 }
1714
1715 #[test]
1716 fn event_try_from_v4() {
1717 let fidl_route = fnet_routes::InstalledRouteV4::arbitrary_test_value();
1718 let local_route = fidl_route.clone().try_into().unwrap();
1719 assert_eq!(
1720 fnet_routes::EventV4::unknown_variant_for_testing().try_into(),
1721 Ok(Event::Unknown)
1722 );
1723 assert_eq!(
1724 Event::<Ipv4>::Unknown.try_into(),
1725 Err::<fnet_routes::EventV4, _>(NetTypeConversionError::UnknownUnionVariant(
1726 "fuchsia_net_routes.EventV4"
1727 ))
1728 );
1729 assert_eq!(
1730 fnet_routes::EventV4::Existing(fidl_route.clone()).try_into(),
1731 Ok(Event::Existing(local_route))
1732 );
1733 assert_eq!(
1734 Event::Existing(local_route).try_into(),
1735 Ok(fnet_routes::EventV4::Existing(fidl_route.clone()))
1736 );
1737
1738 assert_eq!(fnet_routes::EventV4::Idle(fnet_routes::Empty).try_into(), Ok(Event::Idle));
1739 assert_eq!(Event::Idle.try_into(), Ok(fnet_routes::EventV4::Idle(fnet_routes::Empty)));
1740 assert_eq!(
1741 fnet_routes::EventV4::Added(fidl_route.clone()).try_into(),
1742 Ok(Event::Added(local_route))
1743 );
1744 assert_eq!(
1745 Event::Added(local_route).try_into(),
1746 Ok(fnet_routes::EventV4::Added(fidl_route.clone()))
1747 );
1748 assert_eq!(
1749 fnet_routes::EventV4::Removed(fidl_route.clone()).try_into(),
1750 Ok(Event::Removed(local_route))
1751 );
1752 assert_eq!(
1753 Event::Removed(local_route).try_into(),
1754 Ok(fnet_routes::EventV4::Removed(fidl_route.clone()))
1755 );
1756 }
1757
1758 #[test]
1759 fn event_try_from_v6() {
1760 let fidl_route = fnet_routes::InstalledRouteV6::arbitrary_test_value();
1761 let local_route = fidl_route.clone().try_into().unwrap();
1762 assert_eq!(
1763 fnet_routes::EventV6::unknown_variant_for_testing().try_into(),
1764 Ok(Event::Unknown)
1765 );
1766 assert_eq!(
1767 Event::<Ipv6>::Unknown.try_into(),
1768 Err::<fnet_routes::EventV6, _>(NetTypeConversionError::UnknownUnionVariant(
1769 "fuchsia_net_routes.EventV6"
1770 ))
1771 );
1772 assert_eq!(
1773 fnet_routes::EventV6::Existing(fidl_route.clone()).try_into(),
1774 Ok(Event::Existing(local_route))
1775 );
1776 assert_eq!(
1777 Event::Existing(local_route).try_into(),
1778 Ok(fnet_routes::EventV6::Existing(fidl_route.clone()))
1779 );
1780
1781 assert_eq!(fnet_routes::EventV6::Idle(fnet_routes::Empty).try_into(), Ok(Event::Idle));
1782 assert_eq!(Event::Idle.try_into(), Ok(fnet_routes::EventV6::Idle(fnet_routes::Empty)));
1783 assert_eq!(
1784 fnet_routes::EventV6::Added(fidl_route.clone()).try_into(),
1785 Ok(Event::Added(local_route))
1786 );
1787 assert_eq!(
1788 Event::Added(local_route).try_into(),
1789 Ok(fnet_routes::EventV6::Added(fidl_route.clone()))
1790 );
1791 assert_eq!(
1792 fnet_routes::EventV6::Removed(fidl_route.clone()).try_into(),
1793 Ok(Event::Removed(local_route))
1794 );
1795 assert_eq!(
1796 Event::Removed(local_route).try_into(),
1797 Ok(fnet_routes::EventV6::Removed(fidl_route.clone()))
1798 );
1799 }
1800
1801 #[ip_test(I)]
1805 #[test_case(Vec::new(); "no events")]
1806 #[test_case(vec![0..1]; "single_batch_single_event")]
1807 #[test_case(vec![0..10]; "single_batch_many_events")]
1808 #[test_case(vec![0..10, 10..20, 20..30]; "many_batches_many_events")]
1809 #[fuchsia_async::run_singlethreaded(test)]
1810 async fn event_stream_from_state_against_shape<I: FidlRouteIpExt>(
1811 test_shape: Vec<std::ops::Range<u32>>,
1812 ) {
1813 let (batches_sender, batches_receiver) =
1816 futures::channel::mpsc::unbounded::<Vec<I::WatchEvent>>();
1817 for batch_shape in &test_shape {
1818 batches_sender
1819 .unbounded_send(internal_testutil::generate_events_in_range::<I>(
1820 batch_shape.clone(),
1821 ))
1822 .expect("failed to send event batch");
1823 }
1824
1825 #[cfg(feature = "fdomain")]
1827 let client = fdomain_local::local_client_empty();
1828 #[cfg(not(feature = "fdomain"))]
1829 let client = fidl::endpoints::ZirconClient;
1830 let (state, state_server_end) = client.create_proxy::<I::StateMarker>();
1831 let (mut state_request_stream, _control_handle) =
1832 state_server_end.into_stream_and_control_handle();
1833 let watcher_fut = state_request_stream
1834 .next()
1835 .then(|req| {
1836 testutil::serve_state_request::<I>(
1837 req.expect("State request_stream unexpectedly ended"),
1838 batches_receiver,
1839 )
1840 })
1841 .fuse();
1842
1843 let event_stream =
1844 event_stream_from_state::<I>(&state).expect("failed to connect to watcher").fuse();
1845
1846 futures::pin_mut!(watcher_fut, event_stream);
1847
1848 for batch_shape in test_shape {
1849 for event_idx in batch_shape.into_iter() {
1850 futures::select! {
1851 () = watcher_fut => panic!("fake watcher implementation unexpectedly finished"),
1852 event = event_stream.next() => {
1853 let actual_event = event
1854 .expect("event stream unexpectedly empty")
1855 .expect("error processing event");
1856 let expected_event = internal_testutil::generate_event::<I>(event_idx)
1857 .try_into()
1858 .expect("test event is unexpectedly invalid");
1859 assert_eq!(actual_event, expected_event);
1860 }
1861 };
1862 }
1863 }
1864
1865 batches_sender.close_channel();
1867 let ((), mut events) = futures::join!(watcher_fut, event_stream.collect::<Vec<_>>());
1868 assert_matches!(
1869 events.pop(),
1870 Some(Err(WatchError::Fidl(fidl::Error::ClientChannelClosed {
1871 status: zx_status::Status::PEER_CLOSED,
1872 ..
1873 })))
1874 );
1875 assert_matches!(events[..], []);
1876 }
1877
1878 #[ip_test(I)]
1881 #[fuchsia_async::run_singlethreaded]
1882 async fn event_stream_from_state_multiple_watchers<I: FidlRouteIpExt>() {
1883 let test_data = vec![
1885 vec![internal_testutil::generate_events_in_range::<I>(0..10)],
1886 vec![internal_testutil::generate_events_in_range::<I>(10..20)],
1887 vec![internal_testutil::generate_events_in_range::<I>(20..30)],
1888 ];
1889
1890 #[cfg(feature = "fdomain")]
1892 let client = fdomain_local::local_client_empty();
1893 #[cfg(not(feature = "fdomain"))]
1894 let client = fidl::endpoints::ZirconClient;
1895 let (state, state_server_end) = client.create_proxy::<I::StateMarker>();
1896 let (state_request_stream, _control_handle) =
1897 state_server_end.into_stream_and_control_handle();
1898 let watchers_fut = state_request_stream
1899 .zip(futures::stream::iter(test_data.clone()))
1900 .for_each_concurrent(std::usize::MAX, |(request, watcher_data)| {
1901 testutil::serve_state_request::<I>(request, futures::stream::iter(watcher_data))
1902 });
1903
1904 let validate_event_streams_fut =
1905 futures::future::join_all(test_data.into_iter().map(|watcher_data| {
1906 let events_fut = event_stream_from_state::<I>(&state)
1907 .expect("failed to connect to watcher")
1908 .collect::<std::collections::VecDeque<_>>();
1909 events_fut.then(|mut events| {
1910 for expected_event in watcher_data.into_iter().flatten() {
1911 assert_eq!(
1912 events
1913 .pop_front()
1914 .expect("event_stream unexpectedly empty")
1915 .expect("error processing event"),
1916 expected_event.try_into().expect("test event is unexpectedly invalid"),
1917 );
1918 }
1919 assert_matches!(
1920 events.pop_front(),
1921 Some(Err(WatchError::Fidl(fidl::Error::ClientChannelClosed {
1922 status: zx_status::Status::PEER_CLOSED,
1923 ..
1924 })))
1925 );
1926 assert_matches!(events.make_contiguous(), []);
1927 futures::future::ready(())
1928 })
1929 }));
1930
1931 let ((), _): ((), Vec<()>) = futures::join!(watchers_fut, validate_event_streams_fut);
1932 }
1933
1934 #[ip_test(I)]
1940 #[test_case(false, false; "no_trailing")]
1941 #[test_case(true, false; "trailing_event")]
1942 #[test_case(false, true; "trailing_batch")]
1943 #[test_case(true, true; "trailing_event_and_batch")]
1944 #[fuchsia_async::run_singlethreaded(test)]
1945 async fn event_stream_from_state_conversion_error<I: FidlRouteIpExt>(
1946 trailing_event: bool,
1947 trailing_batch: bool,
1948 ) {
1949 #[derive(GenericOverIp)]
1952 #[generic_over_ip(I, Ip)]
1953 struct EventHolder<I: FidlRouteIpExt>(I::WatchEvent);
1954 let EventHolder(bad_event) = I::map_ip(
1955 (),
1956 |()| {
1957 EventHolder(fnet_routes::EventV4::Added(fnet_routes::InstalledRouteV4 {
1958 route: Some(fnet_routes::RouteV4 {
1959 destination: fidl_ip_v4_with_prefix!("192.168.0.1/24"),
1960 ..fnet_routes::RouteV4::arbitrary_test_value()
1961 }),
1962 ..fnet_routes::InstalledRouteV4::arbitrary_test_value()
1963 }))
1964 },
1965 |()| {
1966 EventHolder(fnet_routes::EventV6::Added(fnet_routes::InstalledRouteV6 {
1967 route: Some(fnet_routes::RouteV6 {
1968 destination: fidl_ip_v6_with_prefix!("fe80::1/64"),
1969 ..fnet_routes::RouteV6::arbitrary_test_value()
1970 }),
1971 ..fnet_routes::InstalledRouteV6::arbitrary_test_value()
1972 }))
1973 },
1974 );
1975
1976 let batch = std::iter::once(bad_event)
1977 .chain(trailing_event.then(|| internal_testutil::generate_event::<I>(0)).into_iter())
1979 .collect::<Vec<_>>();
1980 let batches = std::iter::once(batch)
1981 .chain(trailing_batch.then(|| vec![internal_testutil::generate_event::<I>(1)]))
1983 .collect::<Vec<_>>();
1984
1985 #[cfg(feature = "fdomain")]
1987 let client = fdomain_local::local_client_empty();
1988 #[cfg(not(feature = "fdomain"))]
1989 let client = fidl::endpoints::ZirconClient;
1990 let (state, state_server_end) = client.create_proxy::<I::StateMarker>();
1991 let (mut state_request_stream, _control_handle) =
1992 state_server_end.into_stream_and_control_handle();
1993 let watcher_fut = state_request_stream
1994 .next()
1995 .then(|req| {
1996 testutil::serve_state_request::<I>(
1997 req.expect("State request_stream unexpectedly ended"),
1998 futures::stream::iter(batches),
1999 )
2000 })
2001 .fuse();
2002
2003 let event_stream =
2004 event_stream_from_state::<I>(&state).expect("failed to connect to watcher").fuse();
2005
2006 futures::pin_mut!(watcher_fut, event_stream);
2007 let ((), events) = futures::join!(watcher_fut, event_stream.collect::<Vec<_>>());
2008 assert_matches!(&events[..], &[Err(WatchError::Conversion(_))]);
2009 }
2010
2011 #[ip_test(I)]
2016 #[test_case(false; "no_trailing_batch")]
2017 #[test_case(true; "trailing_batch")]
2018 #[fuchsia_async::run_singlethreaded(test)]
2019 async fn event_stream_from_state_empty_batch_error<I: FidlRouteIpExt>(trailing_batch: bool) {
2020 let batches = std::iter::once(Vec::new())
2021 .chain(trailing_batch.then(|| vec![internal_testutil::generate_event::<I>(0)]))
2023 .collect::<Vec<_>>();
2024
2025 #[cfg(feature = "fdomain")]
2027 let client = fdomain_local::local_client_empty();
2028 #[cfg(not(feature = "fdomain"))]
2029 let client = fidl::endpoints::ZirconClient;
2030 let (state, state_server_end) = client.create_proxy::<I::StateMarker>();
2031 let (mut state_request_stream, _control_handle) =
2032 state_server_end.into_stream_and_control_handle();
2033 let watcher_fut = state_request_stream
2034 .next()
2035 .then(|req| {
2036 testutil::serve_state_request::<I>(
2037 req.expect("State request_stream unexpectedly ended"),
2038 futures::stream::iter(batches),
2039 )
2040 })
2041 .fuse();
2042
2043 let event_stream =
2044 event_stream_from_state::<I>(&state).expect("failed to connect to watcher").fuse();
2045
2046 futures::pin_mut!(watcher_fut, event_stream);
2047 let ((), events) = futures::join!(watcher_fut, event_stream.collect::<Vec<_>>());
2048 assert_matches!(&events[..], &[Err(WatchError::EmptyEventBatch)]);
2049 }
2050
2051 fn arbitrary_test_route<I: Ip + FidlRouteIpExt>() -> InstalledRoute<I> {
2052 #[derive(GenericOverIp)]
2053 #[generic_over_ip(I, Ip)]
2054 struct RouteHolder<I: FidlRouteIpExt>(InstalledRoute<I>);
2055 let RouteHolder(route) = I::map_ip(
2056 (),
2057 |()| {
2058 RouteHolder(
2059 fnet_routes::InstalledRouteV4::arbitrary_test_value().try_into().unwrap(),
2060 )
2061 },
2062 |()| {
2063 RouteHolder(
2064 fnet_routes::InstalledRouteV6::arbitrary_test_value().try_into().unwrap(),
2065 )
2066 },
2067 );
2068 route
2069 }
2070
2071 enum CollectRoutesUntilIdleErrorTestCase {
2072 ErrorInStream,
2073 UnexpectedEvent,
2074 StreamEnded,
2075 }
2076
2077 #[ip_test(I)]
2078 #[test_case(CollectRoutesUntilIdleErrorTestCase::ErrorInStream; "error_in_stream")]
2079 #[test_case(CollectRoutesUntilIdleErrorTestCase::UnexpectedEvent; "unexpected_event")]
2080 #[test_case(CollectRoutesUntilIdleErrorTestCase::StreamEnded; "stream_ended")]
2081 #[fuchsia_async::run_singlethreaded(test)]
2082 async fn collect_routes_until_idle_error<I: FidlRouteIpExt>(
2083 test_case: CollectRoutesUntilIdleErrorTestCase,
2084 ) {
2085 let route = arbitrary_test_route();
2089 let (event, test_assertion): (_, Box<dyn FnOnce(_)>) = match test_case {
2090 CollectRoutesUntilIdleErrorTestCase::ErrorInStream => (
2091 Err(WatchError::EmptyEventBatch),
2092 Box::new(|result| {
2093 assert_matches!(result, Err(CollectRoutesUntilIdleError::ErrorInStream(_)))
2094 }),
2095 ),
2096 CollectRoutesUntilIdleErrorTestCase::UnexpectedEvent => (
2097 Ok(Event::Added(route)),
2098 Box::new(|result| {
2099 assert_matches!(result, Err(CollectRoutesUntilIdleError::UnexpectedEvent(_)))
2100 }),
2101 ),
2102 CollectRoutesUntilIdleErrorTestCase::StreamEnded => (
2103 Ok(Event::Existing(route)),
2104 Box::new(|result| {
2105 assert_matches!(result, Err(CollectRoutesUntilIdleError::StreamEnded))
2106 }),
2107 ),
2108 };
2109
2110 let event_stream = futures::stream::once(futures::future::ready(event));
2111 futures::pin_mut!(event_stream);
2112 let result = collect_routes_until_idle::<I, Vec<_>>(event_stream).await;
2113 test_assertion(result);
2114 }
2115
2116 #[ip_test(I)]
2119 #[fuchsia_async::run_singlethreaded]
2120 async fn collect_routes_until_idle_success<I: FidlRouteIpExt>() {
2121 let route = arbitrary_test_route();
2122 let event_stream = futures::stream::iter([
2123 Ok(Event::Existing(route)),
2124 Ok(Event::Idle),
2125 Ok(Event::Added(route)),
2126 ]);
2127
2128 futures::pin_mut!(event_stream);
2129 let existing = collect_routes_until_idle::<I, Vec<_>>(event_stream.by_ref())
2130 .await
2131 .expect("failed to collect existing routes");
2132 assert_eq!(&existing, &[route]);
2133
2134 let trailing_events = event_stream.collect::<Vec<_>>().await;
2135 assert_matches!(
2136 &trailing_events[..],
2137 &[Ok(Event::Added(found_route))] if found_route == route
2138 );
2139 }
2140
2141 #[ip_test(I)]
2142 #[fuchsia_async::run_singlethreaded]
2143 async fn wait_for_routes_errors<I: FidlRouteIpExt>() {
2144 let mut state = HashSet::new();
2145 let event_stream =
2146 futures::stream::once(futures::future::ready(Err(WatchError::EmptyEventBatch)));
2147 assert_matches!(
2148 wait_for_routes::<I, _, _>(event_stream, &mut state, |_| true).await,
2149 Err(WaitForRoutesError::ErrorInStream(WatchError::EmptyEventBatch))
2150 );
2151 assert!(state.is_empty());
2152
2153 let event_stream = futures::stream::empty();
2154 assert_matches!(
2155 wait_for_routes::<I, _, _>(event_stream, &mut state, |_| true).await,
2156 Err(WaitForRoutesError::StreamEnded)
2157 );
2158 assert!(state.is_empty());
2159
2160 let event_stream = futures::stream::once(futures::future::ready(Ok(Event::<I>::Unknown)));
2161 assert_matches!(
2162 wait_for_routes::<I, _, _>(event_stream, &mut state, |_| true).await,
2163 Err(WaitForRoutesError::UnknownEvent)
2164 );
2165 assert!(state.is_empty());
2166 }
2167
2168 #[ip_test(I)]
2169 #[fuchsia_async::run_singlethreaded]
2170 async fn wait_for_routes_add_remove<I: FidlRouteIpExt>() {
2171 let into_stream = |t| futures::stream::once(futures::future::ready(t));
2172
2173 let route = arbitrary_test_route::<I>();
2174 let mut state = HashSet::new();
2175
2176 let has_route = |routes: &HashSet<InstalledRoute<I>>| routes.contains(&route);
2179 assert_matches!(
2180 wait_for_routes::<I, _, _>(futures::stream::pending(), &mut state, has_route)
2181 .now_or_never(),
2182 None
2183 );
2184 assert!(state.is_empty());
2185 assert_matches!(
2186 wait_for_routes::<I, _, _>(into_stream(Ok(Event::Added(route))), &mut state, has_route)
2187 .now_or_never(),
2188 Some(Ok(()))
2189 );
2190 assert_eq!(state, HashSet::from_iter([route]));
2191
2192 assert_matches!(
2194 wait_for_routes::<I, _, _>(into_stream(Ok(Event::Added(route))), &mut state, has_route)
2195 .now_or_never(),
2196 Some(Err(WaitForRoutesError::AddedAlreadyExisting(r))) if r == route
2197 );
2198 assert_eq!(state, HashSet::from_iter([route]));
2199
2200 let does_not_have_route = |routes: &HashSet<InstalledRoute<I>>| !routes.contains(&route);
2203 assert_matches!(
2204 wait_for_routes::<I, _, _>(futures::stream::pending(), &mut state, does_not_have_route)
2205 .now_or_never(),
2206 None
2207 );
2208 assert_eq!(state, HashSet::from_iter([route]));
2209 assert_matches!(
2210 wait_for_routes::<I, _, _>(
2211 into_stream(Ok(Event::Removed(route))),
2212 &mut state,
2213 does_not_have_route
2214 )
2215 .now_or_never(),
2216 Some(Ok(()))
2217 );
2218 assert!(state.is_empty());
2219
2220 assert_matches!(
2222 wait_for_routes::<I, _, _>(
2223 into_stream(Ok(Event::Removed(route))),
2224 &mut state,
2225 does_not_have_route
2226 ).now_or_never(),
2227 Some(Err(WaitForRoutesError::RemovedNonExistent(r))) if r == route
2228 );
2229 assert!(state.is_empty());
2230 }
2231}