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
1199 const ARBITRARY_TABLE_ID: TableId = TableId::new(0);
1200
1201 trait ArbitraryTestValue {
1203 fn arbitrary_test_value() -> Self;
1204 }
1205
1206 impl ArbitraryTestValue for fnet_routes::SpecifiedRouteProperties {
1207 fn arbitrary_test_value() -> Self {
1208 fnet_routes::SpecifiedRouteProperties {
1209 metric: Some(fnet_routes::SpecifiedMetric::ExplicitMetric(0)),
1210 ..Default::default()
1211 }
1212 }
1213 }
1214
1215 impl ArbitraryTestValue for fnet_routes::EffectiveRouteProperties {
1216 fn arbitrary_test_value() -> Self {
1217 fnet_routes::EffectiveRouteProperties { metric: Some(0), ..Default::default() }
1218 }
1219 }
1220
1221 impl ArbitraryTestValue for fnet_routes::RoutePropertiesV4 {
1222 fn arbitrary_test_value() -> Self {
1223 fnet_routes::RoutePropertiesV4 {
1224 specified_properties: Some(
1225 fnet_routes::SpecifiedRouteProperties::arbitrary_test_value(),
1226 ),
1227 ..Default::default()
1228 }
1229 }
1230 }
1231
1232 impl ArbitraryTestValue for fnet_routes::RoutePropertiesV6 {
1233 fn arbitrary_test_value() -> Self {
1234 fnet_routes::RoutePropertiesV6 {
1235 specified_properties: Some(
1236 fnet_routes::SpecifiedRouteProperties::arbitrary_test_value(),
1237 ),
1238 ..Default::default()
1239 }
1240 }
1241 }
1242
1243 impl ArbitraryTestValue for fnet_routes::RouteTargetV4 {
1244 fn arbitrary_test_value() -> Self {
1245 fnet_routes::RouteTargetV4 { outbound_interface: 1, next_hop: None }
1246 }
1247 }
1248
1249 impl ArbitraryTestValue for fnet_routes::RouteTargetV6 {
1250 fn arbitrary_test_value() -> Self {
1251 fnet_routes::RouteTargetV6 { outbound_interface: 1, next_hop: None }
1252 }
1253 }
1254
1255 impl ArbitraryTestValue for fnet_routes::RouteActionV4 {
1256 fn arbitrary_test_value() -> Self {
1257 fnet_routes::RouteActionV4::Forward(fnet_routes::RouteTargetV4::arbitrary_test_value())
1258 }
1259 }
1260
1261 impl ArbitraryTestValue for fnet_routes::RouteActionV6 {
1262 fn arbitrary_test_value() -> Self {
1263 fnet_routes::RouteActionV6::Forward(fnet_routes::RouteTargetV6::arbitrary_test_value())
1264 }
1265 }
1266
1267 impl ArbitraryTestValue for fnet_routes::RouteV4 {
1268 fn arbitrary_test_value() -> Self {
1269 fnet_routes::RouteV4 {
1270 destination: fidl_ip_v4_with_prefix!("192.168.0.0/24"),
1271 action: fnet_routes::RouteActionV4::arbitrary_test_value(),
1272 properties: fnet_routes::RoutePropertiesV4::arbitrary_test_value(),
1273 }
1274 }
1275 }
1276
1277 impl ArbitraryTestValue for fnet_routes::RouteV6 {
1278 fn arbitrary_test_value() -> Self {
1279 fnet_routes::RouteV6 {
1280 destination: fidl_ip_v6_with_prefix!("fe80::0/64"),
1281 action: fnet_routes::RouteActionV6::arbitrary_test_value(),
1282 properties: fnet_routes::RoutePropertiesV6::arbitrary_test_value(),
1283 }
1284 }
1285 }
1286
1287 impl ArbitraryTestValue for fnet_routes::InstalledRouteV4 {
1288 fn arbitrary_test_value() -> Self {
1289 fnet_routes::InstalledRouteV4 {
1290 route: Some(fnet_routes::RouteV4::arbitrary_test_value()),
1291 effective_properties: Some(
1292 fnet_routes::EffectiveRouteProperties::arbitrary_test_value(),
1293 ),
1294 table_id: Some(ARBITRARY_TABLE_ID.get()),
1295 ..Default::default()
1296 }
1297 }
1298 }
1299
1300 impl ArbitraryTestValue for fnet_routes::InstalledRouteV6 {
1301 fn arbitrary_test_value() -> Self {
1302 fnet_routes::InstalledRouteV6 {
1303 route: Some(fnet_routes::RouteV6::arbitrary_test_value()),
1304 effective_properties: Some(
1305 fnet_routes::EffectiveRouteProperties::arbitrary_test_value(),
1306 ),
1307 table_id: Some(ARBITRARY_TABLE_ID.get()),
1308 ..Default::default()
1309 }
1310 }
1311 }
1312
1313 #[test]
1314 fn specified_route_properties_try_from_unset_metric() {
1315 assert_eq!(
1316 SpecifiedRouteProperties::try_from(fnet_routes::SpecifiedRouteProperties::default()),
1317 Err(FidlConversionError::RequiredFieldUnset(
1318 SpecifiedRoutePropertiesRequiredFields::Metric
1319 ))
1320 )
1321 }
1322
1323 #[test]
1324 fn specified_route_properties_try_from() {
1325 let fidl_type = fnet_routes::SpecifiedRouteProperties {
1326 metric: Some(fnet_routes::SpecifiedMetric::ExplicitMetric(1)),
1327 ..Default::default()
1328 };
1329 let local_type =
1330 SpecifiedRouteProperties { metric: fnet_routes::SpecifiedMetric::ExplicitMetric(1) };
1331 assert_eq!(fidl_type.clone().try_into(), Ok(local_type));
1332 assert_eq!(
1333 <SpecifiedRouteProperties as std::convert::Into<
1334 fnet_routes::SpecifiedRouteProperties,
1335 >>::into(local_type),
1336 fidl_type.clone()
1337 );
1338 }
1339
1340 #[test]
1341 fn effective_route_properties_try_from_unset_metric() {
1342 assert_eq!(
1343 EffectiveRouteProperties::try_from(fnet_routes::EffectiveRouteProperties::default()),
1344 Err(FidlConversionError::RequiredFieldUnset(
1345 EffectiveRoutePropertiesRequiredFields::Metric
1346 ))
1347 )
1348 }
1349
1350 #[test]
1351 fn effective_route_properties_try_from() {
1352 let fidl_type =
1353 fnet_routes::EffectiveRouteProperties { metric: Some(1), ..Default::default() };
1354 let local_type = EffectiveRouteProperties { metric: 1 };
1355 assert_eq!(fidl_type.clone().try_into(), Ok(EffectiveRouteProperties { metric: 1 }));
1356 assert_eq!(
1357 <EffectiveRouteProperties as std::convert::Into<
1358 fnet_routes::EffectiveRouteProperties,
1359 >>::into(local_type),
1360 fidl_type.clone()
1361 );
1362 }
1363
1364 #[test]
1365 fn route_properties_try_from_unset_specified_properties_v4() {
1366 assert_eq!(
1367 RouteProperties::try_from(fnet_routes::RoutePropertiesV4::default()),
1368 Err(FidlConversionError::RequiredFieldUnset(
1369 RoutePropertiesRequiredFields::SpecifiedProperties
1370 ))
1371 )
1372 }
1373
1374 #[test]
1375 fn route_properties_try_from_unset_specified_properties_v6() {
1376 assert_eq!(
1377 RouteProperties::try_from(fnet_routes::RoutePropertiesV6::default()),
1378 Err(FidlConversionError::RequiredFieldUnset(
1379 RoutePropertiesRequiredFields::SpecifiedProperties
1380 ))
1381 )
1382 }
1383
1384 #[test]
1385 fn route_properties_try_from_v4() {
1386 let fidl_type = fnet_routes::RoutePropertiesV4 {
1387 specified_properties: Some(
1388 fnet_routes::SpecifiedRouteProperties::arbitrary_test_value(),
1389 ),
1390 ..Default::default()
1391 };
1392 let local_type = RouteProperties {
1393 specified_properties: fnet_routes::SpecifiedRouteProperties::arbitrary_test_value()
1394 .try_into()
1395 .unwrap(),
1396 };
1397 assert_eq!(fidl_type.clone().try_into(), Ok(local_type));
1398 assert_eq!(
1399 <RouteProperties as std::convert::Into<fnet_routes::RoutePropertiesV4>>::into(
1400 local_type
1401 ),
1402 fidl_type.clone()
1403 );
1404 }
1405
1406 #[test]
1407 fn route_properties_try_from_v6() {
1408 let fidl_type = fnet_routes::RoutePropertiesV6 {
1409 specified_properties: Some(
1410 fnet_routes::SpecifiedRouteProperties::arbitrary_test_value(),
1411 ),
1412 ..Default::default()
1413 };
1414 let local_type = RouteProperties {
1415 specified_properties: fnet_routes::SpecifiedRouteProperties::arbitrary_test_value()
1416 .try_into()
1417 .unwrap(),
1418 };
1419 assert_eq!(fidl_type.clone().try_into(), Ok(local_type));
1420 assert_eq!(
1421 <RouteProperties as std::convert::Into<fnet_routes::RoutePropertiesV6>>::into(
1422 local_type
1423 ),
1424 fidl_type.clone()
1425 );
1426 }
1427
1428 #[test]
1429 fn route_target_try_from_unspecified_next_hop_v4() {
1430 assert_eq!(
1431 RouteTarget::try_from(fnet_routes::RouteTargetV4 {
1432 outbound_interface: 1,
1433 next_hop: Some(Box::new(fidl_ip_v4!("0.0.0.0"))),
1434 }),
1435 Err(FidlConversionError::UnspecifiedNextHop)
1436 )
1437 }
1438
1439 #[test]
1440 fn route_target_try_from_unspecified_next_hop_v6() {
1441 assert_eq!(
1442 RouteTarget::try_from(fnet_routes::RouteTargetV6 {
1443 outbound_interface: 1,
1444 next_hop: Some(Box::new(fidl_ip_v6!("::"))),
1445 }),
1446 Err(FidlConversionError::UnspecifiedNextHop)
1447 );
1448 }
1449
1450 #[test]
1451 fn route_target_try_from_limited_broadcast_next_hop_v4() {
1452 assert_eq!(
1453 RouteTarget::try_from(fnet_routes::RouteTargetV4 {
1454 outbound_interface: 1,
1455 next_hop: Some(Box::new(fidl_ip_v4!("255.255.255.255"))),
1456 }),
1457 Err(FidlConversionError::NextHopNotUnicast)
1458 )
1459 }
1460
1461 #[test]
1462 fn route_target_try_from_multicast_next_hop_v6() {
1463 assert_eq!(
1464 RouteTarget::try_from(fnet_routes::RouteTargetV6 {
1465 outbound_interface: 1,
1466 next_hop: Some(Box::new(fidl_ip_v6!("ff00::1"))),
1467 }),
1468 Err(FidlConversionError::NextHopNotUnicast)
1469 )
1470 }
1471
1472 #[test]
1473 fn route_target_try_from_v4() {
1474 let fidl_type = fnet_routes::RouteTargetV4 {
1475 outbound_interface: 1,
1476 next_hop: Some(Box::new(fidl_ip_v4!("192.168.0.1"))),
1477 };
1478 let local_type = RouteTarget {
1479 outbound_interface: 1,
1480 next_hop: Some(SpecifiedAddr::new(net_ip_v4!("192.168.0.1")).unwrap()),
1481 };
1482 assert_eq!(fidl_type.clone().try_into(), Ok(local_type));
1483 assert_eq!(
1484 <RouteTarget<Ipv4> as std::convert::Into<fnet_routes::RouteTargetV4>>::into(local_type),
1485 fidl_type
1486 );
1487 }
1488
1489 #[test]
1490 fn route_target_try_from_v6() {
1491 let fidl_type = fnet_routes::RouteTargetV6 {
1492 outbound_interface: 1,
1493 next_hop: Some(Box::new(fidl_ip_v6!("fe80::1"))),
1494 };
1495 let local_type = RouteTarget {
1496 outbound_interface: 1,
1497 next_hop: Some(SpecifiedAddr::new(net_ip_v6!("fe80::1")).unwrap()),
1498 };
1499 assert_eq!(fidl_type.clone().try_into(), Ok(local_type));
1500 assert_eq!(
1501 <RouteTarget<Ipv6> as std::convert::Into<fnet_routes::RouteTargetV6>>::into(local_type),
1502 fidl_type
1503 );
1504 }
1505
1506 #[test]
1507 fn route_action_try_from_forward_v4() {
1508 let fidl_type =
1509 fnet_routes::RouteActionV4::Forward(fnet_routes::RouteTargetV4::arbitrary_test_value());
1510 let local_type = RouteAction::Forward(
1511 fnet_routes::RouteTargetV4::arbitrary_test_value().try_into().unwrap(),
1512 );
1513 assert_eq!(fidl_type.clone().try_into(), Ok(local_type));
1514 assert_eq!(local_type.try_into(), Ok(fidl_type.clone()));
1515 }
1516
1517 #[test]
1518 fn route_action_try_from_forward_v6() {
1519 let fidl_type =
1520 fnet_routes::RouteActionV6::Forward(fnet_routes::RouteTargetV6::arbitrary_test_value());
1521 let local_type = RouteAction::Forward(
1522 fnet_routes::RouteTargetV6::arbitrary_test_value().try_into().unwrap(),
1523 );
1524 assert_eq!(fidl_type.clone().try_into(), Ok(local_type));
1525 assert_eq!(local_type.try_into(), Ok(fidl_type.clone()));
1526 }
1527
1528 #[test]
1529 fn route_action_try_from_unknown_v4() {
1530 let fidl_type = fnet_routes::RouteActionV4::unknown_variant_for_testing();
1531 const LOCAL_TYPE: RouteAction<Ipv4> = RouteAction::Unknown;
1532 assert_eq!(fidl_type.try_into(), Ok(LOCAL_TYPE));
1533 assert_eq!(
1534 LOCAL_TYPE.try_into(),
1535 Err::<fnet_routes::RouteActionV4, _>(NetTypeConversionError::UnknownUnionVariant(
1536 "fuchsia.net.routes/RouteActionV4"
1537 ))
1538 );
1539 }
1540
1541 #[test]
1542 fn route_action_try_from_unknown_v6() {
1543 let fidl_type = fnet_routes::RouteActionV6::unknown_variant_for_testing();
1544 const LOCAL_TYPE: RouteAction<Ipv6> = RouteAction::Unknown;
1545 assert_eq!(fidl_type.try_into(), Ok(LOCAL_TYPE));
1546 assert_eq!(
1547 LOCAL_TYPE.try_into(),
1548 Err::<fnet_routes::RouteActionV6, _>(NetTypeConversionError::UnknownUnionVariant(
1549 "fuchsia.net.routes/RouteActionV6"
1550 ))
1551 );
1552 }
1553
1554 #[test]
1555 fn route_try_from_invalid_destination_v4() {
1556 assert_matches!(
1557 Route::try_from(fnet_routes::RouteV4 {
1558 destination: fidl_ip_v4_with_prefix!("192.168.0.1/24"),
1560 action: fnet_routes::RouteActionV4::arbitrary_test_value(),
1561 properties: fnet_routes::RoutePropertiesV4::arbitrary_test_value(),
1562 }),
1563 Err(FidlConversionError::DestinationSubnet(_))
1564 );
1565 }
1566
1567 #[test]
1568 fn route_try_from_invalid_destination_v6() {
1569 assert_matches!(
1570 Route::try_from(fnet_routes::RouteV6 {
1571 destination: fidl_ip_v6_with_prefix!("fe80::1/64"),
1573 action: fnet_routes::RouteActionV6::arbitrary_test_value(),
1574 properties: fnet_routes::RoutePropertiesV6::arbitrary_test_value(),
1575 }),
1576 Err(FidlConversionError::DestinationSubnet(_))
1577 );
1578 }
1579
1580 #[test]
1581 fn route_try_from_v4() {
1582 let fidl_type = fnet_routes::RouteV4 {
1583 destination: fidl_ip_v4_with_prefix!("192.168.0.0/24"),
1584 action: fnet_routes::RouteActionV4::arbitrary_test_value(),
1585 properties: fnet_routes::RoutePropertiesV4::arbitrary_test_value(),
1586 };
1587 let local_type = Route {
1588 destination: net_subnet_v4!("192.168.0.0/24"),
1589 action: fnet_routes::RouteActionV4::arbitrary_test_value().try_into().unwrap(),
1590 properties: fnet_routes::RoutePropertiesV4::arbitrary_test_value().try_into().unwrap(),
1591 };
1592 assert_eq!(fidl_type.clone().try_into(), Ok(local_type));
1593 assert_eq!(local_type.try_into(), Ok(fidl_type.clone()));
1594 }
1595
1596 #[test]
1597 fn route_try_from_v6() {
1598 let fidl_type = fnet_routes::RouteV6 {
1599 destination: fidl_ip_v6_with_prefix!("fe80::0/64"),
1600 action: fnet_routes::RouteActionV6::arbitrary_test_value(),
1601 properties: fnet_routes::RoutePropertiesV6::arbitrary_test_value(),
1602 };
1603 let local_type = Route {
1604 destination: net_subnet_v6!("fe80::0/64"),
1605 action: fnet_routes::RouteActionV6::arbitrary_test_value().try_into().unwrap(),
1606 properties: fnet_routes::RoutePropertiesV6::arbitrary_test_value().try_into().unwrap(),
1607 };
1608 assert_eq!(fidl_type.clone().try_into(), Ok(local_type));
1609 assert_eq!(local_type.try_into(), Ok(fidl_type.clone()));
1610 }
1611
1612 #[test]
1613 fn installed_route_try_from_unset_route_v4() {
1614 assert_eq!(
1615 InstalledRoute::try_from(fnet_routes::InstalledRouteV4 {
1616 route: None,
1617 effective_properties: Some(
1618 fnet_routes::EffectiveRouteProperties::arbitrary_test_value(),
1619 ),
1620 table_id: Some(ARBITRARY_TABLE_ID.get()),
1621 ..Default::default()
1622 }),
1623 Err(FidlConversionError::RequiredFieldUnset(InstalledRouteRequiredFields::Route))
1624 )
1625 }
1626
1627 #[test]
1628 fn installed_route_try_from_unset_route_v6() {
1629 assert_eq!(
1630 InstalledRoute::try_from(fnet_routes::InstalledRouteV6 {
1631 route: None,
1632 effective_properties: Some(
1633 fnet_routes::EffectiveRouteProperties::arbitrary_test_value(),
1634 ),
1635 table_id: Some(ARBITRARY_TABLE_ID.get()),
1636 ..Default::default()
1637 }),
1638 Err(FidlConversionError::RequiredFieldUnset(InstalledRouteRequiredFields::Route))
1639 )
1640 }
1641
1642 #[test]
1643 fn installed_route_try_from_unset_effective_properties_v4() {
1644 assert_eq!(
1645 InstalledRoute::try_from(fnet_routes::InstalledRouteV4 {
1646 route: Some(fnet_routes::RouteV4::arbitrary_test_value()),
1647 effective_properties: None,
1648 table_id: Some(ARBITRARY_TABLE_ID.get()),
1649 ..Default::default()
1650 }),
1651 Err(FidlConversionError::RequiredFieldUnset(
1652 InstalledRouteRequiredFields::EffectiveProperties
1653 ))
1654 )
1655 }
1656
1657 #[test]
1658 fn installed_route_try_from_unset_effective_properties_v6() {
1659 assert_eq!(
1660 InstalledRoute::try_from(fnet_routes::InstalledRouteV6 {
1661 route: Some(fnet_routes::RouteV6::arbitrary_test_value()),
1662 effective_properties: None,
1663 table_id: Some(ARBITRARY_TABLE_ID.get()),
1664 ..Default::default()
1665 }),
1666 Err(FidlConversionError::RequiredFieldUnset(
1667 InstalledRouteRequiredFields::EffectiveProperties
1668 ))
1669 )
1670 }
1671
1672 #[test]
1673 fn installed_route_try_from_v4() {
1674 let fidl_type = fnet_routes::InstalledRouteV4 {
1675 route: Some(fnet_routes::RouteV4::arbitrary_test_value()),
1676 effective_properties: Some(
1677 fnet_routes::EffectiveRouteProperties::arbitrary_test_value(),
1678 ),
1679 table_id: Some(ARBITRARY_TABLE_ID.get()),
1680 ..Default::default()
1681 };
1682 let local_type = InstalledRoute {
1683 route: fnet_routes::RouteV4::arbitrary_test_value().try_into().unwrap(),
1684 effective_properties: fnet_routes::EffectiveRouteProperties::arbitrary_test_value()
1685 .try_into()
1686 .unwrap(),
1687 table_id: ARBITRARY_TABLE_ID,
1688 };
1689 assert_eq!(fidl_type.clone().try_into(), Ok(local_type));
1690 assert_eq!(local_type.try_into(), Ok(fidl_type.clone()));
1691 }
1692
1693 #[test]
1694 fn installed_route_try_from_v6() {
1695 let fidl_type = fnet_routes::InstalledRouteV6 {
1696 route: Some(fnet_routes::RouteV6::arbitrary_test_value()),
1697 effective_properties: Some(
1698 fnet_routes::EffectiveRouteProperties::arbitrary_test_value(),
1699 ),
1700 table_id: Some(ARBITRARY_TABLE_ID.get()),
1701 ..Default::default()
1702 };
1703 let local_type = InstalledRoute {
1704 route: fnet_routes::RouteV6::arbitrary_test_value().try_into().unwrap(),
1705 effective_properties: fnet_routes::EffectiveRouteProperties::arbitrary_test_value()
1706 .try_into()
1707 .unwrap(),
1708 table_id: ARBITRARY_TABLE_ID,
1709 };
1710 assert_eq!(fidl_type.clone().try_into(), Ok(local_type));
1711 assert_eq!(local_type.try_into(), Ok(fidl_type.clone()));
1712 }
1713
1714 #[test]
1715 fn event_try_from_v4() {
1716 let fidl_route = fnet_routes::InstalledRouteV4::arbitrary_test_value();
1717 let local_route = fidl_route.clone().try_into().unwrap();
1718 assert_eq!(
1719 fnet_routes::EventV4::unknown_variant_for_testing().try_into(),
1720 Ok(Event::Unknown)
1721 );
1722 assert_eq!(
1723 Event::<Ipv4>::Unknown.try_into(),
1724 Err::<fnet_routes::EventV4, _>(NetTypeConversionError::UnknownUnionVariant(
1725 "fuchsia_net_routes.EventV4"
1726 ))
1727 );
1728 assert_eq!(
1729 fnet_routes::EventV4::Existing(fidl_route.clone()).try_into(),
1730 Ok(Event::Existing(local_route))
1731 );
1732 assert_eq!(
1733 Event::Existing(local_route).try_into(),
1734 Ok(fnet_routes::EventV4::Existing(fidl_route.clone()))
1735 );
1736
1737 assert_eq!(fnet_routes::EventV4::Idle(fnet_routes::Empty).try_into(), Ok(Event::Idle));
1738 assert_eq!(Event::Idle.try_into(), Ok(fnet_routes::EventV4::Idle(fnet_routes::Empty)));
1739 assert_eq!(
1740 fnet_routes::EventV4::Added(fidl_route.clone()).try_into(),
1741 Ok(Event::Added(local_route))
1742 );
1743 assert_eq!(
1744 Event::Added(local_route).try_into(),
1745 Ok(fnet_routes::EventV4::Added(fidl_route.clone()))
1746 );
1747 assert_eq!(
1748 fnet_routes::EventV4::Removed(fidl_route.clone()).try_into(),
1749 Ok(Event::Removed(local_route))
1750 );
1751 assert_eq!(
1752 Event::Removed(local_route).try_into(),
1753 Ok(fnet_routes::EventV4::Removed(fidl_route.clone()))
1754 );
1755 }
1756
1757 #[test]
1758 fn event_try_from_v6() {
1759 let fidl_route = fnet_routes::InstalledRouteV6::arbitrary_test_value();
1760 let local_route = fidl_route.clone().try_into().unwrap();
1761 assert_eq!(
1762 fnet_routes::EventV6::unknown_variant_for_testing().try_into(),
1763 Ok(Event::Unknown)
1764 );
1765 assert_eq!(
1766 Event::<Ipv6>::Unknown.try_into(),
1767 Err::<fnet_routes::EventV6, _>(NetTypeConversionError::UnknownUnionVariant(
1768 "fuchsia_net_routes.EventV6"
1769 ))
1770 );
1771 assert_eq!(
1772 fnet_routes::EventV6::Existing(fidl_route.clone()).try_into(),
1773 Ok(Event::Existing(local_route))
1774 );
1775 assert_eq!(
1776 Event::Existing(local_route).try_into(),
1777 Ok(fnet_routes::EventV6::Existing(fidl_route.clone()))
1778 );
1779
1780 assert_eq!(fnet_routes::EventV6::Idle(fnet_routes::Empty).try_into(), Ok(Event::Idle));
1781 assert_eq!(Event::Idle.try_into(), Ok(fnet_routes::EventV6::Idle(fnet_routes::Empty)));
1782 assert_eq!(
1783 fnet_routes::EventV6::Added(fidl_route.clone()).try_into(),
1784 Ok(Event::Added(local_route))
1785 );
1786 assert_eq!(
1787 Event::Added(local_route).try_into(),
1788 Ok(fnet_routes::EventV6::Added(fidl_route.clone()))
1789 );
1790 assert_eq!(
1791 fnet_routes::EventV6::Removed(fidl_route.clone()).try_into(),
1792 Ok(Event::Removed(local_route))
1793 );
1794 assert_eq!(
1795 Event::Removed(local_route).try_into(),
1796 Ok(fnet_routes::EventV6::Removed(fidl_route.clone()))
1797 );
1798 }
1799
1800 #[ip_test(I)]
1804 #[test_case(Vec::new(); "no events")]
1805 #[test_case(vec![0..1]; "single_batch_single_event")]
1806 #[test_case(vec![0..10]; "single_batch_many_events")]
1807 #[test_case(vec![0..10, 10..20, 20..30]; "many_batches_many_events")]
1808 #[fuchsia_async::run_singlethreaded(test)]
1809 async fn event_stream_from_state_against_shape<I: FidlRouteIpExt>(
1810 test_shape: Vec<std::ops::Range<u32>>,
1811 ) {
1812 let (batches_sender, batches_receiver) =
1815 futures::channel::mpsc::unbounded::<Vec<I::WatchEvent>>();
1816 for batch_shape in &test_shape {
1817 batches_sender
1818 .unbounded_send(internal_testutil::generate_events_in_range::<I>(
1819 batch_shape.clone(),
1820 ))
1821 .expect("failed to send event batch");
1822 }
1823
1824 #[cfg(feature = "fdomain")]
1826 let client = fdomain_local::local_client_empty();
1827 #[cfg(not(feature = "fdomain"))]
1828 let client = fidl::endpoints::ZirconClient;
1829 let (state, state_server_end) = client.create_proxy::<I::StateMarker>();
1830 let (mut state_request_stream, _control_handle) =
1831 state_server_end.into_stream_and_control_handle();
1832 let watcher_fut = state_request_stream
1833 .next()
1834 .then(|req| {
1835 testutil::serve_state_request::<I>(
1836 req.expect("State request_stream unexpectedly ended"),
1837 batches_receiver,
1838 )
1839 })
1840 .fuse();
1841
1842 let event_stream =
1843 event_stream_from_state::<I>(&state).expect("failed to connect to watcher").fuse();
1844
1845 futures::pin_mut!(watcher_fut, event_stream);
1846
1847 for batch_shape in test_shape {
1848 for event_idx in batch_shape.into_iter() {
1849 futures::select! {
1850 () = watcher_fut => panic!("fake watcher implementation unexpectedly finished"),
1851 event = event_stream.next() => {
1852 let actual_event = event
1853 .expect("event stream unexpectedly empty")
1854 .expect("error processing event");
1855 let expected_event = internal_testutil::generate_event::<I>(event_idx)
1856 .try_into()
1857 .expect("test event is unexpectedly invalid");
1858 assert_eq!(actual_event, expected_event);
1859 }
1860 };
1861 }
1862 }
1863
1864 batches_sender.close_channel();
1866 let ((), mut events) = futures::join!(watcher_fut, event_stream.collect::<Vec<_>>());
1867 assert_matches!(
1868 events.pop(),
1869 Some(Err(WatchError::Fidl(fidl::Error::ClientChannelClosed {
1870 epitaph: fidl::Epitaph::PeerClosed,
1871 ..
1872 })))
1873 );
1874 assert_matches!(events[..], []);
1875 }
1876
1877 #[ip_test(I)]
1880 #[fuchsia_async::run_singlethreaded]
1881 async fn event_stream_from_state_multiple_watchers<I: FidlRouteIpExt>() {
1882 let test_data = vec![
1884 vec![internal_testutil::generate_events_in_range::<I>(0..10)],
1885 vec![internal_testutil::generate_events_in_range::<I>(10..20)],
1886 vec![internal_testutil::generate_events_in_range::<I>(20..30)],
1887 ];
1888
1889 #[cfg(feature = "fdomain")]
1891 let client = fdomain_local::local_client_empty();
1892 #[cfg(not(feature = "fdomain"))]
1893 let client = fidl::endpoints::ZirconClient;
1894 let (state, state_server_end) = client.create_proxy::<I::StateMarker>();
1895 let (state_request_stream, _control_handle) =
1896 state_server_end.into_stream_and_control_handle();
1897 let watchers_fut = state_request_stream
1898 .zip(futures::stream::iter(test_data.clone()))
1899 .for_each_concurrent(std::usize::MAX, |(request, watcher_data)| {
1900 testutil::serve_state_request::<I>(request, futures::stream::iter(watcher_data))
1901 });
1902
1903 let validate_event_streams_fut =
1904 futures::future::join_all(test_data.into_iter().map(|watcher_data| {
1905 let events_fut = event_stream_from_state::<I>(&state)
1906 .expect("failed to connect to watcher")
1907 .collect::<std::collections::VecDeque<_>>();
1908 events_fut.then(|mut events| {
1909 for expected_event in watcher_data.into_iter().flatten() {
1910 assert_eq!(
1911 events
1912 .pop_front()
1913 .expect("event_stream unexpectedly empty")
1914 .expect("error processing event"),
1915 expected_event.try_into().expect("test event is unexpectedly invalid"),
1916 );
1917 }
1918 assert_matches!(
1919 events.pop_front(),
1920 Some(Err(WatchError::Fidl(fidl::Error::ClientChannelClosed {
1921 epitaph: fidl::Epitaph::PeerClosed,
1922 ..
1923 })))
1924 );
1925 assert_matches!(events.make_contiguous(), []);
1926 futures::future::ready(())
1927 })
1928 }));
1929
1930 let ((), _): ((), Vec<()>) = futures::join!(watchers_fut, validate_event_streams_fut);
1931 }
1932
1933 #[ip_test(I)]
1939 #[test_case(false, false; "no_trailing")]
1940 #[test_case(true, false; "trailing_event")]
1941 #[test_case(false, true; "trailing_batch")]
1942 #[test_case(true, true; "trailing_event_and_batch")]
1943 #[fuchsia_async::run_singlethreaded(test)]
1944 async fn event_stream_from_state_conversion_error<I: FidlRouteIpExt>(
1945 trailing_event: bool,
1946 trailing_batch: bool,
1947 ) {
1948 #[derive(GenericOverIp)]
1951 #[generic_over_ip(I, Ip)]
1952 struct EventHolder<I: FidlRouteIpExt>(I::WatchEvent);
1953 let EventHolder(bad_event) = I::map_ip(
1954 (),
1955 |()| {
1956 EventHolder(fnet_routes::EventV4::Added(fnet_routes::InstalledRouteV4 {
1957 route: Some(fnet_routes::RouteV4 {
1958 destination: fidl_ip_v4_with_prefix!("192.168.0.1/24"),
1959 ..fnet_routes::RouteV4::arbitrary_test_value()
1960 }),
1961 ..fnet_routes::InstalledRouteV4::arbitrary_test_value()
1962 }))
1963 },
1964 |()| {
1965 EventHolder(fnet_routes::EventV6::Added(fnet_routes::InstalledRouteV6 {
1966 route: Some(fnet_routes::RouteV6 {
1967 destination: fidl_ip_v6_with_prefix!("fe80::1/64"),
1968 ..fnet_routes::RouteV6::arbitrary_test_value()
1969 }),
1970 ..fnet_routes::InstalledRouteV6::arbitrary_test_value()
1971 }))
1972 },
1973 );
1974
1975 let batch = std::iter::once(bad_event)
1976 .chain(trailing_event.then(|| internal_testutil::generate_event::<I>(0)).into_iter())
1978 .collect::<Vec<_>>();
1979 let batches = std::iter::once(batch)
1980 .chain(trailing_batch.then(|| vec![internal_testutil::generate_event::<I>(1)]))
1982 .collect::<Vec<_>>();
1983
1984 #[cfg(feature = "fdomain")]
1986 let client = fdomain_local::local_client_empty();
1987 #[cfg(not(feature = "fdomain"))]
1988 let client = fidl::endpoints::ZirconClient;
1989 let (state, state_server_end) = client.create_proxy::<I::StateMarker>();
1990 let (mut state_request_stream, _control_handle) =
1991 state_server_end.into_stream_and_control_handle();
1992 let watcher_fut = state_request_stream
1993 .next()
1994 .then(|req| {
1995 testutil::serve_state_request::<I>(
1996 req.expect("State request_stream unexpectedly ended"),
1997 futures::stream::iter(batches),
1998 )
1999 })
2000 .fuse();
2001
2002 let event_stream =
2003 event_stream_from_state::<I>(&state).expect("failed to connect to watcher").fuse();
2004
2005 futures::pin_mut!(watcher_fut, event_stream);
2006 let ((), events) = futures::join!(watcher_fut, event_stream.collect::<Vec<_>>());
2007 assert_matches!(&events[..], &[Err(WatchError::Conversion(_))]);
2008 }
2009
2010 #[ip_test(I)]
2015 #[test_case(false; "no_trailing_batch")]
2016 #[test_case(true; "trailing_batch")]
2017 #[fuchsia_async::run_singlethreaded(test)]
2018 async fn event_stream_from_state_empty_batch_error<I: FidlRouteIpExt>(trailing_batch: bool) {
2019 let batches = std::iter::once(Vec::new())
2020 .chain(trailing_batch.then(|| vec![internal_testutil::generate_event::<I>(0)]))
2022 .collect::<Vec<_>>();
2023
2024 #[cfg(feature = "fdomain")]
2026 let client = fdomain_local::local_client_empty();
2027 #[cfg(not(feature = "fdomain"))]
2028 let client = fidl::endpoints::ZirconClient;
2029 let (state, state_server_end) = client.create_proxy::<I::StateMarker>();
2030 let (mut state_request_stream, _control_handle) =
2031 state_server_end.into_stream_and_control_handle();
2032 let watcher_fut = state_request_stream
2033 .next()
2034 .then(|req| {
2035 testutil::serve_state_request::<I>(
2036 req.expect("State request_stream unexpectedly ended"),
2037 futures::stream::iter(batches),
2038 )
2039 })
2040 .fuse();
2041
2042 let event_stream =
2043 event_stream_from_state::<I>(&state).expect("failed to connect to watcher").fuse();
2044
2045 futures::pin_mut!(watcher_fut, event_stream);
2046 let ((), events) = futures::join!(watcher_fut, event_stream.collect::<Vec<_>>());
2047 assert_matches!(&events[..], &[Err(WatchError::EmptyEventBatch)]);
2048 }
2049
2050 fn arbitrary_test_route<I: Ip + FidlRouteIpExt>() -> InstalledRoute<I> {
2051 #[derive(GenericOverIp)]
2052 #[generic_over_ip(I, Ip)]
2053 struct RouteHolder<I: FidlRouteIpExt>(InstalledRoute<I>);
2054 let RouteHolder(route) = I::map_ip(
2055 (),
2056 |()| {
2057 RouteHolder(
2058 fnet_routes::InstalledRouteV4::arbitrary_test_value().try_into().unwrap(),
2059 )
2060 },
2061 |()| {
2062 RouteHolder(
2063 fnet_routes::InstalledRouteV6::arbitrary_test_value().try_into().unwrap(),
2064 )
2065 },
2066 );
2067 route
2068 }
2069
2070 enum CollectRoutesUntilIdleErrorTestCase {
2071 ErrorInStream,
2072 UnexpectedEvent,
2073 StreamEnded,
2074 }
2075
2076 #[ip_test(I)]
2077 #[test_case(CollectRoutesUntilIdleErrorTestCase::ErrorInStream; "error_in_stream")]
2078 #[test_case(CollectRoutesUntilIdleErrorTestCase::UnexpectedEvent; "unexpected_event")]
2079 #[test_case(CollectRoutesUntilIdleErrorTestCase::StreamEnded; "stream_ended")]
2080 #[fuchsia_async::run_singlethreaded(test)]
2081 async fn collect_routes_until_idle_error<I: FidlRouteIpExt>(
2082 test_case: CollectRoutesUntilIdleErrorTestCase,
2083 ) {
2084 let route = arbitrary_test_route();
2088 let (event, test_assertion): (_, Box<dyn FnOnce(_)>) = match test_case {
2089 CollectRoutesUntilIdleErrorTestCase::ErrorInStream => (
2090 Err(WatchError::EmptyEventBatch),
2091 Box::new(|result| {
2092 assert_matches!(result, Err(CollectRoutesUntilIdleError::ErrorInStream(_)))
2093 }),
2094 ),
2095 CollectRoutesUntilIdleErrorTestCase::UnexpectedEvent => (
2096 Ok(Event::Added(route)),
2097 Box::new(|result| {
2098 assert_matches!(result, Err(CollectRoutesUntilIdleError::UnexpectedEvent(_)))
2099 }),
2100 ),
2101 CollectRoutesUntilIdleErrorTestCase::StreamEnded => (
2102 Ok(Event::Existing(route)),
2103 Box::new(|result| {
2104 assert_matches!(result, Err(CollectRoutesUntilIdleError::StreamEnded))
2105 }),
2106 ),
2107 };
2108
2109 let event_stream = futures::stream::once(futures::future::ready(event));
2110 futures::pin_mut!(event_stream);
2111 let result = collect_routes_until_idle::<I, Vec<_>>(event_stream).await;
2112 test_assertion(result);
2113 }
2114
2115 #[ip_test(I)]
2118 #[fuchsia_async::run_singlethreaded]
2119 async fn collect_routes_until_idle_success<I: FidlRouteIpExt>() {
2120 let route = arbitrary_test_route();
2121 let event_stream = futures::stream::iter([
2122 Ok(Event::Existing(route)),
2123 Ok(Event::Idle),
2124 Ok(Event::Added(route)),
2125 ]);
2126
2127 futures::pin_mut!(event_stream);
2128 let existing = collect_routes_until_idle::<I, Vec<_>>(event_stream.by_ref())
2129 .await
2130 .expect("failed to collect existing routes");
2131 assert_eq!(&existing, &[route]);
2132
2133 let trailing_events = event_stream.collect::<Vec<_>>().await;
2134 assert_matches!(
2135 &trailing_events[..],
2136 &[Ok(Event::Added(found_route))] if found_route == route
2137 );
2138 }
2139
2140 #[ip_test(I)]
2141 #[fuchsia_async::run_singlethreaded]
2142 async fn wait_for_routes_errors<I: FidlRouteIpExt>() {
2143 let mut state = HashSet::new();
2144 let event_stream =
2145 futures::stream::once(futures::future::ready(Err(WatchError::EmptyEventBatch)));
2146 assert_matches!(
2147 wait_for_routes::<I, _, _>(event_stream, &mut state, |_| true).await,
2148 Err(WaitForRoutesError::ErrorInStream(WatchError::EmptyEventBatch))
2149 );
2150 assert!(state.is_empty());
2151
2152 let event_stream = futures::stream::empty();
2153 assert_matches!(
2154 wait_for_routes::<I, _, _>(event_stream, &mut state, |_| true).await,
2155 Err(WaitForRoutesError::StreamEnded)
2156 );
2157 assert!(state.is_empty());
2158
2159 let event_stream = futures::stream::once(futures::future::ready(Ok(Event::<I>::Unknown)));
2160 assert_matches!(
2161 wait_for_routes::<I, _, _>(event_stream, &mut state, |_| true).await,
2162 Err(WaitForRoutesError::UnknownEvent)
2163 );
2164 assert!(state.is_empty());
2165 }
2166
2167 #[ip_test(I)]
2168 #[fuchsia_async::run_singlethreaded]
2169 async fn wait_for_routes_add_remove<I: FidlRouteIpExt>() {
2170 let into_stream = |t| futures::stream::once(futures::future::ready(t));
2171
2172 let route = arbitrary_test_route::<I>();
2173 let mut state = HashSet::new();
2174
2175 let has_route = |routes: &HashSet<InstalledRoute<I>>| routes.contains(&route);
2178 assert_matches!(
2179 wait_for_routes::<I, _, _>(futures::stream::pending(), &mut state, has_route)
2180 .now_or_never(),
2181 None
2182 );
2183 assert!(state.is_empty());
2184 assert_matches!(
2185 wait_for_routes::<I, _, _>(into_stream(Ok(Event::Added(route))), &mut state, has_route)
2186 .now_or_never(),
2187 Some(Ok(()))
2188 );
2189 assert_eq!(state, HashSet::from_iter([route]));
2190
2191 assert_matches!(
2193 wait_for_routes::<I, _, _>(into_stream(Ok(Event::Added(route))), &mut state, has_route)
2194 .now_or_never(),
2195 Some(Err(WaitForRoutesError::AddedAlreadyExisting(r))) if r == route
2196 );
2197 assert_eq!(state, HashSet::from_iter([route]));
2198
2199 let does_not_have_route = |routes: &HashSet<InstalledRoute<I>>| !routes.contains(&route);
2202 assert_matches!(
2203 wait_for_routes::<I, _, _>(futures::stream::pending(), &mut state, does_not_have_route)
2204 .now_or_never(),
2205 None
2206 );
2207 assert_eq!(state, HashSet::from_iter([route]));
2208 assert_matches!(
2209 wait_for_routes::<I, _, _>(
2210 into_stream(Ok(Event::Removed(route))),
2211 &mut state,
2212 does_not_have_route
2213 )
2214 .now_or_never(),
2215 Some(Ok(()))
2216 );
2217 assert!(state.is_empty());
2218
2219 assert_matches!(
2221 wait_for_routes::<I, _, _>(
2222 into_stream(Ok(Event::Removed(route))),
2223 &mut state,
2224 does_not_have_route
2225 ).now_or_never(),
2226 Some(Err(WaitForRoutesError::RemovedNonExistent(r))) if r == route
2227 );
2228 assert!(state.is_empty());
2229 }
2230}