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 futures::{Future, Stream, TryStreamExt as _};
25use net_types::ip::{GenericOverIp, Ip, Ipv4, Ipv6, Ipv6Addr, Subnet};
26use net_types::{SpecifiedAddr, UnicastAddress, Witness as _};
27use thiserror::Error;
28use {
29 fidl_fuchsia_net as fnet, fidl_fuchsia_net_routes as fnet_routes,
30 fidl_fuchsia_net_routes_admin as fnet_routes_admin, fidl_fuchsia_net_stack as fnet_stack,
31};
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: fidl::endpoints::DiscoverableProtocolMarker;
815 type WatcherMarker: fidl::endpoints::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: fidl::endpoints::Responder + Debug + Send {
847 type Payload;
849
850 fn send(self, result: Self::Payload) -> Result<(), fidl::Error>;
852}
853
854pub trait SliceResponder<Payload>: fidl::endpoints::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 fidl::endpoints::ProtocolMarker>::Proxy,
922 options: WatcherOptions,
923) -> Result<<I::WatcherMarker as fidl::endpoints::ProtocolMarker>::Proxy, WatcherCreationError> {
924 let (watcher_proxy, watcher_server_end) = fidl::endpoints::create_proxy::<I::WatcherMarker>();
925
926 #[derive(GenericOverIp)]
927 #[generic_over_ip(I, Ip)]
928 struct GetWatcherInputs<'a, I: FidlRouteIpExt> {
929 watcher_server_end: fidl::endpoints::ServerEnd<I::WatcherMarker>,
930 state_proxy: &'a <I::StateMarker as fidl::endpoints::ProtocolMarker>::Proxy,
931 options: WatcherOptions,
932 }
933 let result = I::map_ip_in(
934 GetWatcherInputs::<'_, I> { watcher_server_end, state_proxy, options },
935 |GetWatcherInputs { watcher_server_end, state_proxy, options }| {
936 state_proxy.get_watcher_v4(watcher_server_end, &options.into())
937 },
938 |GetWatcherInputs { watcher_server_end, state_proxy, options }| {
939 state_proxy.get_watcher_v6(watcher_server_end, &options.into())
940 },
941 );
942
943 result.map_err(WatcherCreationError::GetWatcher)?;
944 Ok(watcher_proxy)
945}
946
947pub fn watch<'a, I: FidlRouteIpExt>(
949 watcher_proxy: &'a <I::WatcherMarker as fidl::endpoints::ProtocolMarker>::Proxy,
950) -> impl Future<Output = Result<Vec<I::WatchEvent>, fidl::Error>> {
951 #[derive(GenericOverIp)]
952 #[generic_over_ip(I, Ip)]
953 struct WatchInputs<'a, I: FidlRouteIpExt> {
954 watcher_proxy: &'a <I::WatcherMarker as fidl::endpoints::ProtocolMarker>::Proxy,
955 }
956 #[derive(GenericOverIp)]
957 #[generic_over_ip(I, Ip)]
958 struct WatchOutputs<I: FidlRouteIpExt> {
959 watch_fut: fidl::client::QueryResponseFut<Vec<I::WatchEvent>>,
960 }
961 let WatchOutputs { watch_fut } = I::map_ip::<WatchInputs<'_, I>, WatchOutputs<I>>(
962 WatchInputs { watcher_proxy },
963 |WatchInputs { watcher_proxy }| WatchOutputs { watch_fut: watcher_proxy.watch() },
964 |WatchInputs { watcher_proxy }| WatchOutputs { watch_fut: watcher_proxy.watch() },
965 );
966 watch_fut
967}
968
969pub fn event_stream_from_state<I: FidlRouteIpExt>(
971 routes_state: &<I::StateMarker as fidl::endpoints::ProtocolMarker>::Proxy,
972) -> Result<impl Stream<Item = Result<Event<I>, WatchError>>, WatcherCreationError> {
973 event_stream_from_state_with_options(routes_state, Default::default())
974}
975
976pub fn event_stream_from_state_with_options<I: FidlRouteIpExt>(
983 routes_state: &<I::StateMarker as fidl::endpoints::ProtocolMarker>::Proxy,
984 options: WatcherOptions,
985) -> Result<impl Stream<Item = Result<Event<I>, WatchError>>, WatcherCreationError> {
986 let watcher = get_watcher::<I>(routes_state, options)?;
987 event_stream_from_watcher(watcher)
988}
989
990pub fn event_stream_from_watcher<I: FidlRouteIpExt>(
997 watcher: <I::WatcherMarker as fidl::endpoints::ProtocolMarker>::Proxy,
998) -> Result<impl Stream<Item = Result<Event<I>, WatchError>>, WatcherCreationError> {
999 Ok(stream::ShortCircuit::new(
1000 futures::stream::try_unfold(watcher, |watcher| async {
1001 let events_batch = watch::<I>(&watcher).await.map_err(WatchError::Fidl)?;
1002 if events_batch.is_empty() {
1003 return Err(WatchError::EmptyEventBatch);
1004 }
1005 let events_batch = events_batch
1006 .into_iter()
1007 .map(|event| event.try_into().map_err(WatchError::Conversion));
1008 let event_stream = futures::stream::iter(events_batch);
1009 Ok(Some((event_stream, watcher)))
1010 })
1011 .try_flatten(),
1013 ))
1014}
1015
1016#[derive(Clone, Debug, Error)]
1018pub enum CollectRoutesUntilIdleError<I: FidlRouteIpExt> {
1019 #[error("there was an error in the event stream: {0}")]
1021 ErrorInStream(WatchError),
1022 #[error("there was an unexpected event in the event stream: {0:?}")]
1025 UnexpectedEvent(Event<I>),
1026 #[error("the event stream unexpectedly ended")]
1028 StreamEnded,
1029}
1030
1031pub async fn collect_routes_until_idle<
1034 I: FidlRouteIpExt,
1035 C: Extend<InstalledRoute<I>> + Default,
1036>(
1037 event_stream: impl futures::Stream<Item = Result<Event<I>, WatchError>> + Unpin,
1038) -> Result<C, CollectRoutesUntilIdleError<I>> {
1039 fold::fold_while(
1040 event_stream,
1041 Ok(C::default()),
1042 |existing_routes: Result<C, CollectRoutesUntilIdleError<I>>, event| {
1043 futures::future::ready(match existing_routes {
1044 Err(_) => {
1045 unreachable!("`existing_routes` must be `Ok`, because we stop folding on err")
1046 }
1047 Ok(mut existing_routes) => match event {
1048 Err(e) => {
1049 fold::FoldWhile::Done(Err(CollectRoutesUntilIdleError::ErrorInStream(e)))
1050 }
1051 Ok(e) => match e {
1052 Event::Existing(e) => {
1053 existing_routes.extend([e]);
1054 fold::FoldWhile::Continue(Ok(existing_routes))
1055 }
1056 Event::Idle => fold::FoldWhile::Done(Ok(existing_routes)),
1057 e @ Event::Unknown | e @ Event::Added(_) | e @ Event::Removed(_) => {
1058 fold::FoldWhile::Done(Err(
1059 CollectRoutesUntilIdleError::UnexpectedEvent(e),
1060 ))
1061 }
1062 },
1063 },
1064 })
1065 },
1066 )
1067 .await
1068 .short_circuited()
1069 .map_err(|_accumulated_thus_far: Result<C, CollectRoutesUntilIdleError<I>>| {
1070 CollectRoutesUntilIdleError::StreamEnded
1071 })?
1072}
1073
1074#[derive(Clone, Debug, Error)]
1076pub enum WaitForRoutesError<I: FidlRouteIpExt> {
1077 #[error("there was an error in the event stream: {0}")]
1079 ErrorInStream(WatchError),
1080 #[error("observed an added event for an already existing route: {0:?}")]
1082 AddedAlreadyExisting(InstalledRoute<I>),
1083 #[error("observed a removed event for a non-existent route: {0:?}")]
1085 RemovedNonExistent(InstalledRoute<I>),
1086 #[error("observed an unknown event")]
1088 UnknownEvent,
1089 #[error("the event stream unexpectedly ended")]
1091 StreamEnded,
1092}
1093
1094pub async fn wait_for_routes_map<
1102 I: FidlRouteIpExt,
1103 S: futures::Stream<Item = Result<Event<I>, WatchError>> + Unpin,
1104 T,
1105 F: Fn(&HashSet<InstalledRoute<I>>) -> Option<T>,
1106>(
1107 event_stream: S,
1108 initial_state: &mut HashSet<InstalledRoute<I>>,
1109 predicate: F,
1110) -> Result<T, WaitForRoutesError<I>> {
1111 fold::try_fold_while(
1112 event_stream.map_err(WaitForRoutesError::ErrorInStream),
1113 initial_state,
1114 |accumulated_routes, event| {
1115 futures::future::ready({
1116 match event {
1117 Event::Existing(route) | Event::Added(route) => accumulated_routes
1118 .insert(route)
1119 .then_some(())
1120 .ok_or(WaitForRoutesError::AddedAlreadyExisting(route)),
1121 Event::Removed(route) => accumulated_routes
1122 .remove(&route)
1123 .then_some(())
1124 .ok_or(WaitForRoutesError::RemovedNonExistent(route)),
1125 Event::Idle => Ok(()),
1126 Event::Unknown => Err(WaitForRoutesError::UnknownEvent),
1127 }
1128 .map(|()| match predicate(&accumulated_routes) {
1129 Some(t) => fold::FoldWhile::Done(t),
1130 None => fold::FoldWhile::Continue(accumulated_routes),
1131 })
1132 })
1133 },
1134 )
1135 .await?
1136 .short_circuited()
1137 .map_err(|_accumulated_thus_far: &mut HashSet<InstalledRoute<I>>| {
1138 WaitForRoutesError::StreamEnded
1139 })
1140}
1141
1142pub async fn wait_for_routes<
1148 I: FidlRouteIpExt,
1149 S: futures::Stream<Item = Result<Event<I>, WatchError>> + Unpin,
1150 F: Fn(&HashSet<InstalledRoute<I>>) -> bool,
1151>(
1152 event_stream: S,
1153 initial_state: &mut HashSet<InstalledRoute<I>>,
1154 predicate: F,
1155) -> Result<(), WaitForRoutesError<I>> {
1156 wait_for_routes_map::<I, S, (), _>(event_stream, initial_state, |routes| {
1157 predicate(routes).then_some(())
1158 })
1159 .await
1160}
1161
1162#[derive(Debug, Default, Clone)]
1164pub struct ResolveOptions {
1165 pub marks: fnet_ext::Marks,
1167}
1168
1169impl From<fnet_routes::ResolveOptions> for ResolveOptions {
1170 fn from(value: fnet_routes::ResolveOptions) -> Self {
1171 let fnet_routes::ResolveOptions { marks, __source_breaking } = value;
1172 Self { marks: marks.map(fnet_ext::Marks::from).unwrap_or_default() }
1173 }
1174}
1175
1176impl From<ResolveOptions> for fnet_routes::ResolveOptions {
1177 fn from(value: ResolveOptions) -> Self {
1178 let ResolveOptions { marks } = value;
1179 Self { marks: Some(marks.into()), __source_breaking: fidl::marker::SourceBreaking }
1180 }
1181}
1182
1183#[cfg(test)]
1184mod tests {
1185 use super::*;
1186 use crate::testutil::internal as internal_testutil;
1187 use assert_matches::assert_matches;
1188 use futures::{FutureExt as _, StreamExt as _};
1189 use ip_test_macro::ip_test;
1190 use net_declare::{
1191 fidl_ip_v4, fidl_ip_v4_with_prefix, fidl_ip_v6, fidl_ip_v6_with_prefix, net_ip_v4,
1192 net_ip_v6, net_subnet_v4, net_subnet_v6,
1193 };
1194 use test_case::test_case;
1195 use {fidl_fuchsia_net as _, zx_status};
1196
1197 const ARBITRARY_TABLE_ID: TableId = TableId::new(0);
1198
1199 trait ArbitraryTestValue {
1201 fn arbitrary_test_value() -> Self;
1202 }
1203
1204 impl ArbitraryTestValue for fnet_routes::SpecifiedRouteProperties {
1205 fn arbitrary_test_value() -> Self {
1206 fnet_routes::SpecifiedRouteProperties {
1207 metric: Some(fnet_routes::SpecifiedMetric::ExplicitMetric(0)),
1208 ..Default::default()
1209 }
1210 }
1211 }
1212
1213 impl ArbitraryTestValue for fnet_routes::EffectiveRouteProperties {
1214 fn arbitrary_test_value() -> Self {
1215 fnet_routes::EffectiveRouteProperties { metric: Some(0), ..Default::default() }
1216 }
1217 }
1218
1219 impl ArbitraryTestValue for fnet_routes::RoutePropertiesV4 {
1220 fn arbitrary_test_value() -> Self {
1221 fnet_routes::RoutePropertiesV4 {
1222 specified_properties: Some(
1223 fnet_routes::SpecifiedRouteProperties::arbitrary_test_value(),
1224 ),
1225 ..Default::default()
1226 }
1227 }
1228 }
1229
1230 impl ArbitraryTestValue for fnet_routes::RoutePropertiesV6 {
1231 fn arbitrary_test_value() -> Self {
1232 fnet_routes::RoutePropertiesV6 {
1233 specified_properties: Some(
1234 fnet_routes::SpecifiedRouteProperties::arbitrary_test_value(),
1235 ),
1236 ..Default::default()
1237 }
1238 }
1239 }
1240
1241 impl ArbitraryTestValue for fnet_routes::RouteTargetV4 {
1242 fn arbitrary_test_value() -> Self {
1243 fnet_routes::RouteTargetV4 { outbound_interface: 1, next_hop: None }
1244 }
1245 }
1246
1247 impl ArbitraryTestValue for fnet_routes::RouteTargetV6 {
1248 fn arbitrary_test_value() -> Self {
1249 fnet_routes::RouteTargetV6 { outbound_interface: 1, next_hop: None }
1250 }
1251 }
1252
1253 impl ArbitraryTestValue for fnet_routes::RouteActionV4 {
1254 fn arbitrary_test_value() -> Self {
1255 fnet_routes::RouteActionV4::Forward(fnet_routes::RouteTargetV4::arbitrary_test_value())
1256 }
1257 }
1258
1259 impl ArbitraryTestValue for fnet_routes::RouteActionV6 {
1260 fn arbitrary_test_value() -> Self {
1261 fnet_routes::RouteActionV6::Forward(fnet_routes::RouteTargetV6::arbitrary_test_value())
1262 }
1263 }
1264
1265 impl ArbitraryTestValue for fnet_routes::RouteV4 {
1266 fn arbitrary_test_value() -> Self {
1267 fnet_routes::RouteV4 {
1268 destination: fidl_ip_v4_with_prefix!("192.168.0.0/24"),
1269 action: fnet_routes::RouteActionV4::arbitrary_test_value(),
1270 properties: fnet_routes::RoutePropertiesV4::arbitrary_test_value(),
1271 }
1272 }
1273 }
1274
1275 impl ArbitraryTestValue for fnet_routes::RouteV6 {
1276 fn arbitrary_test_value() -> Self {
1277 fnet_routes::RouteV6 {
1278 destination: fidl_ip_v6_with_prefix!("fe80::0/64"),
1279 action: fnet_routes::RouteActionV6::arbitrary_test_value(),
1280 properties: fnet_routes::RoutePropertiesV6::arbitrary_test_value(),
1281 }
1282 }
1283 }
1284
1285 impl ArbitraryTestValue for fnet_routes::InstalledRouteV4 {
1286 fn arbitrary_test_value() -> Self {
1287 fnet_routes::InstalledRouteV4 {
1288 route: Some(fnet_routes::RouteV4::arbitrary_test_value()),
1289 effective_properties: Some(
1290 fnet_routes::EffectiveRouteProperties::arbitrary_test_value(),
1291 ),
1292 table_id: Some(ARBITRARY_TABLE_ID.get()),
1293 ..Default::default()
1294 }
1295 }
1296 }
1297
1298 impl ArbitraryTestValue for fnet_routes::InstalledRouteV6 {
1299 fn arbitrary_test_value() -> Self {
1300 fnet_routes::InstalledRouteV6 {
1301 route: Some(fnet_routes::RouteV6::arbitrary_test_value()),
1302 effective_properties: Some(
1303 fnet_routes::EffectiveRouteProperties::arbitrary_test_value(),
1304 ),
1305 table_id: Some(ARBITRARY_TABLE_ID.get()),
1306 ..Default::default()
1307 }
1308 }
1309 }
1310
1311 #[test]
1312 fn specified_route_properties_try_from_unset_metric() {
1313 assert_eq!(
1314 SpecifiedRouteProperties::try_from(fnet_routes::SpecifiedRouteProperties::default()),
1315 Err(FidlConversionError::RequiredFieldUnset(
1316 SpecifiedRoutePropertiesRequiredFields::Metric
1317 ))
1318 )
1319 }
1320
1321 #[test]
1322 fn specified_route_properties_try_from() {
1323 let fidl_type = fnet_routes::SpecifiedRouteProperties {
1324 metric: Some(fnet_routes::SpecifiedMetric::ExplicitMetric(1)),
1325 ..Default::default()
1326 };
1327 let local_type =
1328 SpecifiedRouteProperties { metric: fnet_routes::SpecifiedMetric::ExplicitMetric(1) };
1329 assert_eq!(fidl_type.clone().try_into(), Ok(local_type));
1330 assert_eq!(
1331 <SpecifiedRouteProperties as std::convert::Into<
1332 fnet_routes::SpecifiedRouteProperties,
1333 >>::into(local_type),
1334 fidl_type.clone()
1335 );
1336 }
1337
1338 #[test]
1339 fn effective_route_properties_try_from_unset_metric() {
1340 assert_eq!(
1341 EffectiveRouteProperties::try_from(fnet_routes::EffectiveRouteProperties::default()),
1342 Err(FidlConversionError::RequiredFieldUnset(
1343 EffectiveRoutePropertiesRequiredFields::Metric
1344 ))
1345 )
1346 }
1347
1348 #[test]
1349 fn effective_route_properties_try_from() {
1350 let fidl_type =
1351 fnet_routes::EffectiveRouteProperties { metric: Some(1), ..Default::default() };
1352 let local_type = EffectiveRouteProperties { metric: 1 };
1353 assert_eq!(fidl_type.clone().try_into(), Ok(EffectiveRouteProperties { metric: 1 }));
1354 assert_eq!(
1355 <EffectiveRouteProperties as std::convert::Into<
1356 fnet_routes::EffectiveRouteProperties,
1357 >>::into(local_type),
1358 fidl_type.clone()
1359 );
1360 }
1361
1362 #[test]
1363 fn route_properties_try_from_unset_specified_properties_v4() {
1364 assert_eq!(
1365 RouteProperties::try_from(fnet_routes::RoutePropertiesV4::default()),
1366 Err(FidlConversionError::RequiredFieldUnset(
1367 RoutePropertiesRequiredFields::SpecifiedProperties
1368 ))
1369 )
1370 }
1371
1372 #[test]
1373 fn route_properties_try_from_unset_specified_properties_v6() {
1374 assert_eq!(
1375 RouteProperties::try_from(fnet_routes::RoutePropertiesV6::default()),
1376 Err(FidlConversionError::RequiredFieldUnset(
1377 RoutePropertiesRequiredFields::SpecifiedProperties
1378 ))
1379 )
1380 }
1381
1382 #[test]
1383 fn route_properties_try_from_v4() {
1384 let fidl_type = fnet_routes::RoutePropertiesV4 {
1385 specified_properties: Some(
1386 fnet_routes::SpecifiedRouteProperties::arbitrary_test_value(),
1387 ),
1388 ..Default::default()
1389 };
1390 let local_type = RouteProperties {
1391 specified_properties: fnet_routes::SpecifiedRouteProperties::arbitrary_test_value()
1392 .try_into()
1393 .unwrap(),
1394 };
1395 assert_eq!(fidl_type.clone().try_into(), Ok(local_type));
1396 assert_eq!(
1397 <RouteProperties as std::convert::Into<fnet_routes::RoutePropertiesV4>>::into(
1398 local_type
1399 ),
1400 fidl_type.clone()
1401 );
1402 }
1403
1404 #[test]
1405 fn route_properties_try_from_v6() {
1406 let fidl_type = fnet_routes::RoutePropertiesV6 {
1407 specified_properties: Some(
1408 fnet_routes::SpecifiedRouteProperties::arbitrary_test_value(),
1409 ),
1410 ..Default::default()
1411 };
1412 let local_type = RouteProperties {
1413 specified_properties: fnet_routes::SpecifiedRouteProperties::arbitrary_test_value()
1414 .try_into()
1415 .unwrap(),
1416 };
1417 assert_eq!(fidl_type.clone().try_into(), Ok(local_type));
1418 assert_eq!(
1419 <RouteProperties as std::convert::Into<fnet_routes::RoutePropertiesV6>>::into(
1420 local_type
1421 ),
1422 fidl_type.clone()
1423 );
1424 }
1425
1426 #[test]
1427 fn route_target_try_from_unspecified_next_hop_v4() {
1428 assert_eq!(
1429 RouteTarget::try_from(fnet_routes::RouteTargetV4 {
1430 outbound_interface: 1,
1431 next_hop: Some(Box::new(fidl_ip_v4!("0.0.0.0"))),
1432 }),
1433 Err(FidlConversionError::UnspecifiedNextHop)
1434 )
1435 }
1436
1437 #[test]
1438 fn route_target_try_from_unspecified_next_hop_v6() {
1439 assert_eq!(
1440 RouteTarget::try_from(fnet_routes::RouteTargetV6 {
1441 outbound_interface: 1,
1442 next_hop: Some(Box::new(fidl_ip_v6!("::"))),
1443 }),
1444 Err(FidlConversionError::UnspecifiedNextHop)
1445 );
1446 }
1447
1448 #[test]
1449 fn route_target_try_from_limited_broadcast_next_hop_v4() {
1450 assert_eq!(
1451 RouteTarget::try_from(fnet_routes::RouteTargetV4 {
1452 outbound_interface: 1,
1453 next_hop: Some(Box::new(fidl_ip_v4!("255.255.255.255"))),
1454 }),
1455 Err(FidlConversionError::NextHopNotUnicast)
1456 )
1457 }
1458
1459 #[test]
1460 fn route_target_try_from_multicast_next_hop_v6() {
1461 assert_eq!(
1462 RouteTarget::try_from(fnet_routes::RouteTargetV6 {
1463 outbound_interface: 1,
1464 next_hop: Some(Box::new(fidl_ip_v6!("ff00::1"))),
1465 }),
1466 Err(FidlConversionError::NextHopNotUnicast)
1467 )
1468 }
1469
1470 #[test]
1471 fn route_target_try_from_v4() {
1472 let fidl_type = fnet_routes::RouteTargetV4 {
1473 outbound_interface: 1,
1474 next_hop: Some(Box::new(fidl_ip_v4!("192.168.0.1"))),
1475 };
1476 let local_type = RouteTarget {
1477 outbound_interface: 1,
1478 next_hop: Some(SpecifiedAddr::new(net_ip_v4!("192.168.0.1")).unwrap()),
1479 };
1480 assert_eq!(fidl_type.clone().try_into(), Ok(local_type));
1481 assert_eq!(
1482 <RouteTarget<Ipv4> as std::convert::Into<fnet_routes::RouteTargetV4>>::into(local_type),
1483 fidl_type
1484 );
1485 }
1486
1487 #[test]
1488 fn route_target_try_from_v6() {
1489 let fidl_type = fnet_routes::RouteTargetV6 {
1490 outbound_interface: 1,
1491 next_hop: Some(Box::new(fidl_ip_v6!("fe80::1"))),
1492 };
1493 let local_type = RouteTarget {
1494 outbound_interface: 1,
1495 next_hop: Some(SpecifiedAddr::new(net_ip_v6!("fe80::1")).unwrap()),
1496 };
1497 assert_eq!(fidl_type.clone().try_into(), Ok(local_type));
1498 assert_eq!(
1499 <RouteTarget<Ipv6> as std::convert::Into<fnet_routes::RouteTargetV6>>::into(local_type),
1500 fidl_type
1501 );
1502 }
1503
1504 #[test]
1505 fn route_action_try_from_forward_v4() {
1506 let fidl_type =
1507 fnet_routes::RouteActionV4::Forward(fnet_routes::RouteTargetV4::arbitrary_test_value());
1508 let local_type = RouteAction::Forward(
1509 fnet_routes::RouteTargetV4::arbitrary_test_value().try_into().unwrap(),
1510 );
1511 assert_eq!(fidl_type.clone().try_into(), Ok(local_type));
1512 assert_eq!(local_type.try_into(), Ok(fidl_type.clone()));
1513 }
1514
1515 #[test]
1516 fn route_action_try_from_forward_v6() {
1517 let fidl_type =
1518 fnet_routes::RouteActionV6::Forward(fnet_routes::RouteTargetV6::arbitrary_test_value());
1519 let local_type = RouteAction::Forward(
1520 fnet_routes::RouteTargetV6::arbitrary_test_value().try_into().unwrap(),
1521 );
1522 assert_eq!(fidl_type.clone().try_into(), Ok(local_type));
1523 assert_eq!(local_type.try_into(), Ok(fidl_type.clone()));
1524 }
1525
1526 #[test]
1527 fn route_action_try_from_unknown_v4() {
1528 let fidl_type = fnet_routes::RouteActionV4::unknown_variant_for_testing();
1529 const LOCAL_TYPE: RouteAction<Ipv4> = RouteAction::Unknown;
1530 assert_eq!(fidl_type.try_into(), Ok(LOCAL_TYPE));
1531 assert_eq!(
1532 LOCAL_TYPE.try_into(),
1533 Err::<fnet_routes::RouteActionV4, _>(NetTypeConversionError::UnknownUnionVariant(
1534 "fuchsia.net.routes/RouteActionV4"
1535 ))
1536 );
1537 }
1538
1539 #[test]
1540 fn route_action_try_from_unknown_v6() {
1541 let fidl_type = fnet_routes::RouteActionV6::unknown_variant_for_testing();
1542 const LOCAL_TYPE: RouteAction<Ipv6> = RouteAction::Unknown;
1543 assert_eq!(fidl_type.try_into(), Ok(LOCAL_TYPE));
1544 assert_eq!(
1545 LOCAL_TYPE.try_into(),
1546 Err::<fnet_routes::RouteActionV6, _>(NetTypeConversionError::UnknownUnionVariant(
1547 "fuchsia.net.routes/RouteActionV6"
1548 ))
1549 );
1550 }
1551
1552 #[test]
1553 fn route_try_from_invalid_destination_v4() {
1554 assert_matches!(
1555 Route::try_from(fnet_routes::RouteV4 {
1556 destination: fidl_ip_v4_with_prefix!("192.168.0.1/24"),
1558 action: fnet_routes::RouteActionV4::arbitrary_test_value(),
1559 properties: fnet_routes::RoutePropertiesV4::arbitrary_test_value(),
1560 }),
1561 Err(FidlConversionError::DestinationSubnet(_))
1562 );
1563 }
1564
1565 #[test]
1566 fn route_try_from_invalid_destination_v6() {
1567 assert_matches!(
1568 Route::try_from(fnet_routes::RouteV6 {
1569 destination: fidl_ip_v6_with_prefix!("fe80::1/64"),
1571 action: fnet_routes::RouteActionV6::arbitrary_test_value(),
1572 properties: fnet_routes::RoutePropertiesV6::arbitrary_test_value(),
1573 }),
1574 Err(FidlConversionError::DestinationSubnet(_))
1575 );
1576 }
1577
1578 #[test]
1579 fn route_try_from_v4() {
1580 let fidl_type = fnet_routes::RouteV4 {
1581 destination: fidl_ip_v4_with_prefix!("192.168.0.0/24"),
1582 action: fnet_routes::RouteActionV4::arbitrary_test_value(),
1583 properties: fnet_routes::RoutePropertiesV4::arbitrary_test_value(),
1584 };
1585 let local_type = Route {
1586 destination: net_subnet_v4!("192.168.0.0/24"),
1587 action: fnet_routes::RouteActionV4::arbitrary_test_value().try_into().unwrap(),
1588 properties: fnet_routes::RoutePropertiesV4::arbitrary_test_value().try_into().unwrap(),
1589 };
1590 assert_eq!(fidl_type.clone().try_into(), Ok(local_type));
1591 assert_eq!(local_type.try_into(), Ok(fidl_type.clone()));
1592 }
1593
1594 #[test]
1595 fn route_try_from_v6() {
1596 let fidl_type = fnet_routes::RouteV6 {
1597 destination: fidl_ip_v6_with_prefix!("fe80::0/64"),
1598 action: fnet_routes::RouteActionV6::arbitrary_test_value(),
1599 properties: fnet_routes::RoutePropertiesV6::arbitrary_test_value(),
1600 };
1601 let local_type = Route {
1602 destination: net_subnet_v6!("fe80::0/64"),
1603 action: fnet_routes::RouteActionV6::arbitrary_test_value().try_into().unwrap(),
1604 properties: fnet_routes::RoutePropertiesV6::arbitrary_test_value().try_into().unwrap(),
1605 };
1606 assert_eq!(fidl_type.clone().try_into(), Ok(local_type));
1607 assert_eq!(local_type.try_into(), Ok(fidl_type.clone()));
1608 }
1609
1610 #[test]
1611 fn installed_route_try_from_unset_route_v4() {
1612 assert_eq!(
1613 InstalledRoute::try_from(fnet_routes::InstalledRouteV4 {
1614 route: None,
1615 effective_properties: Some(
1616 fnet_routes::EffectiveRouteProperties::arbitrary_test_value(),
1617 ),
1618 table_id: Some(ARBITRARY_TABLE_ID.get()),
1619 ..Default::default()
1620 }),
1621 Err(FidlConversionError::RequiredFieldUnset(InstalledRouteRequiredFields::Route))
1622 )
1623 }
1624
1625 #[test]
1626 fn installed_route_try_from_unset_route_v6() {
1627 assert_eq!(
1628 InstalledRoute::try_from(fnet_routes::InstalledRouteV6 {
1629 route: None,
1630 effective_properties: Some(
1631 fnet_routes::EffectiveRouteProperties::arbitrary_test_value(),
1632 ),
1633 table_id: Some(ARBITRARY_TABLE_ID.get()),
1634 ..Default::default()
1635 }),
1636 Err(FidlConversionError::RequiredFieldUnset(InstalledRouteRequiredFields::Route))
1637 )
1638 }
1639
1640 #[test]
1641 fn installed_route_try_from_unset_effective_properties_v4() {
1642 assert_eq!(
1643 InstalledRoute::try_from(fnet_routes::InstalledRouteV4 {
1644 route: Some(fnet_routes::RouteV4::arbitrary_test_value()),
1645 effective_properties: None,
1646 table_id: Some(ARBITRARY_TABLE_ID.get()),
1647 ..Default::default()
1648 }),
1649 Err(FidlConversionError::RequiredFieldUnset(
1650 InstalledRouteRequiredFields::EffectiveProperties
1651 ))
1652 )
1653 }
1654
1655 #[test]
1656 fn installed_route_try_from_unset_effective_properties_v6() {
1657 assert_eq!(
1658 InstalledRoute::try_from(fnet_routes::InstalledRouteV6 {
1659 route: Some(fnet_routes::RouteV6::arbitrary_test_value()),
1660 effective_properties: None,
1661 table_id: Some(ARBITRARY_TABLE_ID.get()),
1662 ..Default::default()
1663 }),
1664 Err(FidlConversionError::RequiredFieldUnset(
1665 InstalledRouteRequiredFields::EffectiveProperties
1666 ))
1667 )
1668 }
1669
1670 #[test]
1671 fn installed_route_try_from_v4() {
1672 let fidl_type = fnet_routes::InstalledRouteV4 {
1673 route: Some(fnet_routes::RouteV4::arbitrary_test_value()),
1674 effective_properties: Some(
1675 fnet_routes::EffectiveRouteProperties::arbitrary_test_value(),
1676 ),
1677 table_id: Some(ARBITRARY_TABLE_ID.get()),
1678 ..Default::default()
1679 };
1680 let local_type = InstalledRoute {
1681 route: fnet_routes::RouteV4::arbitrary_test_value().try_into().unwrap(),
1682 effective_properties: fnet_routes::EffectiveRouteProperties::arbitrary_test_value()
1683 .try_into()
1684 .unwrap(),
1685 table_id: ARBITRARY_TABLE_ID,
1686 };
1687 assert_eq!(fidl_type.clone().try_into(), Ok(local_type));
1688 assert_eq!(local_type.try_into(), Ok(fidl_type.clone()));
1689 }
1690
1691 #[test]
1692 fn installed_route_try_from_v6() {
1693 let fidl_type = fnet_routes::InstalledRouteV6 {
1694 route: Some(fnet_routes::RouteV6::arbitrary_test_value()),
1695 effective_properties: Some(
1696 fnet_routes::EffectiveRouteProperties::arbitrary_test_value(),
1697 ),
1698 table_id: Some(ARBITRARY_TABLE_ID.get()),
1699 ..Default::default()
1700 };
1701 let local_type = InstalledRoute {
1702 route: fnet_routes::RouteV6::arbitrary_test_value().try_into().unwrap(),
1703 effective_properties: fnet_routes::EffectiveRouteProperties::arbitrary_test_value()
1704 .try_into()
1705 .unwrap(),
1706 table_id: ARBITRARY_TABLE_ID,
1707 };
1708 assert_eq!(fidl_type.clone().try_into(), Ok(local_type));
1709 assert_eq!(local_type.try_into(), Ok(fidl_type.clone()));
1710 }
1711
1712 #[test]
1713 fn event_try_from_v4() {
1714 let fidl_route = fnet_routes::InstalledRouteV4::arbitrary_test_value();
1715 let local_route = fidl_route.clone().try_into().unwrap();
1716 assert_eq!(
1717 fnet_routes::EventV4::unknown_variant_for_testing().try_into(),
1718 Ok(Event::Unknown)
1719 );
1720 assert_eq!(
1721 Event::<Ipv4>::Unknown.try_into(),
1722 Err::<fnet_routes::EventV4, _>(NetTypeConversionError::UnknownUnionVariant(
1723 "fuchsia_net_routes.EventV4"
1724 ))
1725 );
1726 assert_eq!(
1727 fnet_routes::EventV4::Existing(fidl_route.clone()).try_into(),
1728 Ok(Event::Existing(local_route))
1729 );
1730 assert_eq!(
1731 Event::Existing(local_route).try_into(),
1732 Ok(fnet_routes::EventV4::Existing(fidl_route.clone()))
1733 );
1734
1735 assert_eq!(fnet_routes::EventV4::Idle(fnet_routes::Empty).try_into(), Ok(Event::Idle));
1736 assert_eq!(Event::Idle.try_into(), Ok(fnet_routes::EventV4::Idle(fnet_routes::Empty)));
1737 assert_eq!(
1738 fnet_routes::EventV4::Added(fidl_route.clone()).try_into(),
1739 Ok(Event::Added(local_route))
1740 );
1741 assert_eq!(
1742 Event::Added(local_route).try_into(),
1743 Ok(fnet_routes::EventV4::Added(fidl_route.clone()))
1744 );
1745 assert_eq!(
1746 fnet_routes::EventV4::Removed(fidl_route.clone()).try_into(),
1747 Ok(Event::Removed(local_route))
1748 );
1749 assert_eq!(
1750 Event::Removed(local_route).try_into(),
1751 Ok(fnet_routes::EventV4::Removed(fidl_route.clone()))
1752 );
1753 }
1754
1755 #[test]
1756 fn event_try_from_v6() {
1757 let fidl_route = fnet_routes::InstalledRouteV6::arbitrary_test_value();
1758 let local_route = fidl_route.clone().try_into().unwrap();
1759 assert_eq!(
1760 fnet_routes::EventV6::unknown_variant_for_testing().try_into(),
1761 Ok(Event::Unknown)
1762 );
1763 assert_eq!(
1764 Event::<Ipv6>::Unknown.try_into(),
1765 Err::<fnet_routes::EventV6, _>(NetTypeConversionError::UnknownUnionVariant(
1766 "fuchsia_net_routes.EventV6"
1767 ))
1768 );
1769 assert_eq!(
1770 fnet_routes::EventV6::Existing(fidl_route.clone()).try_into(),
1771 Ok(Event::Existing(local_route))
1772 );
1773 assert_eq!(
1774 Event::Existing(local_route).try_into(),
1775 Ok(fnet_routes::EventV6::Existing(fidl_route.clone()))
1776 );
1777
1778 assert_eq!(fnet_routes::EventV6::Idle(fnet_routes::Empty).try_into(), Ok(Event::Idle));
1779 assert_eq!(Event::Idle.try_into(), Ok(fnet_routes::EventV6::Idle(fnet_routes::Empty)));
1780 assert_eq!(
1781 fnet_routes::EventV6::Added(fidl_route.clone()).try_into(),
1782 Ok(Event::Added(local_route))
1783 );
1784 assert_eq!(
1785 Event::Added(local_route).try_into(),
1786 Ok(fnet_routes::EventV6::Added(fidl_route.clone()))
1787 );
1788 assert_eq!(
1789 fnet_routes::EventV6::Removed(fidl_route.clone()).try_into(),
1790 Ok(Event::Removed(local_route))
1791 );
1792 assert_eq!(
1793 Event::Removed(local_route).try_into(),
1794 Ok(fnet_routes::EventV6::Removed(fidl_route.clone()))
1795 );
1796 }
1797
1798 #[ip_test(I)]
1802 #[test_case(Vec::new(); "no events")]
1803 #[test_case(vec![0..1]; "single_batch_single_event")]
1804 #[test_case(vec![0..10]; "single_batch_many_events")]
1805 #[test_case(vec![0..10, 10..20, 20..30]; "many_batches_many_events")]
1806 #[fuchsia_async::run_singlethreaded(test)]
1807 async fn event_stream_from_state_against_shape<I: FidlRouteIpExt>(
1808 test_shape: Vec<std::ops::Range<u32>>,
1809 ) {
1810 let (batches_sender, batches_receiver) =
1813 futures::channel::mpsc::unbounded::<Vec<I::WatchEvent>>();
1814 for batch_shape in &test_shape {
1815 batches_sender
1816 .unbounded_send(internal_testutil::generate_events_in_range::<I>(
1817 batch_shape.clone(),
1818 ))
1819 .expect("failed to send event batch");
1820 }
1821
1822 let (state, state_server_end) = fidl::endpoints::create_proxy::<I::StateMarker>();
1824 let (mut state_request_stream, _control_handle) =
1825 state_server_end.into_stream_and_control_handle();
1826 let watcher_fut = state_request_stream
1827 .next()
1828 .then(|req| {
1829 testutil::serve_state_request::<I>(
1830 req.expect("State request_stream unexpectedly ended"),
1831 batches_receiver,
1832 )
1833 })
1834 .fuse();
1835
1836 let event_stream =
1837 event_stream_from_state::<I>(&state).expect("failed to connect to watcher").fuse();
1838
1839 futures::pin_mut!(watcher_fut, event_stream);
1840
1841 for batch_shape in test_shape {
1842 for event_idx in batch_shape.into_iter() {
1843 futures::select! {
1844 () = watcher_fut => panic!("fake watcher implementation unexpectedly finished"),
1845 event = event_stream.next() => {
1846 let actual_event = event
1847 .expect("event stream unexpectedly empty")
1848 .expect("error processing event");
1849 let expected_event = internal_testutil::generate_event::<I>(event_idx)
1850 .try_into()
1851 .expect("test event is unexpectedly invalid");
1852 assert_eq!(actual_event, expected_event);
1853 }
1854 };
1855 }
1856 }
1857
1858 batches_sender.close_channel();
1860 let ((), mut events) = futures::join!(watcher_fut, event_stream.collect::<Vec<_>>());
1861 assert_matches!(
1862 events.pop(),
1863 Some(Err(WatchError::Fidl(fidl::Error::ClientChannelClosed {
1864 status: zx_status::Status::PEER_CLOSED,
1865 ..
1866 })))
1867 );
1868 assert_matches!(events[..], []);
1869 }
1870
1871 #[ip_test(I)]
1874 #[fuchsia_async::run_singlethreaded]
1875 async fn event_stream_from_state_multiple_watchers<I: FidlRouteIpExt>() {
1876 let test_data = vec![
1878 vec![internal_testutil::generate_events_in_range::<I>(0..10)],
1879 vec![internal_testutil::generate_events_in_range::<I>(10..20)],
1880 vec![internal_testutil::generate_events_in_range::<I>(20..30)],
1881 ];
1882
1883 let (state, state_server_end) = fidl::endpoints::create_proxy::<I::StateMarker>();
1885 let (state_request_stream, _control_handle) =
1886 state_server_end.into_stream_and_control_handle();
1887 let watchers_fut = state_request_stream
1888 .zip(futures::stream::iter(test_data.clone()))
1889 .for_each_concurrent(std::usize::MAX, |(request, watcher_data)| {
1890 testutil::serve_state_request::<I>(request, futures::stream::iter(watcher_data))
1891 });
1892
1893 let validate_event_streams_fut =
1894 futures::future::join_all(test_data.into_iter().map(|watcher_data| {
1895 let events_fut = event_stream_from_state::<I>(&state)
1896 .expect("failed to connect to watcher")
1897 .collect::<std::collections::VecDeque<_>>();
1898 events_fut.then(|mut events| {
1899 for expected_event in watcher_data.into_iter().flatten() {
1900 assert_eq!(
1901 events
1902 .pop_front()
1903 .expect("event_stream unexpectedly empty")
1904 .expect("error processing event"),
1905 expected_event.try_into().expect("test event is unexpectedly invalid"),
1906 );
1907 }
1908 assert_matches!(
1909 events.pop_front(),
1910 Some(Err(WatchError::Fidl(fidl::Error::ClientChannelClosed {
1911 status: zx_status::Status::PEER_CLOSED,
1912 ..
1913 })))
1914 );
1915 assert_matches!(events.make_contiguous(), []);
1916 futures::future::ready(())
1917 })
1918 }));
1919
1920 let ((), _): ((), Vec<()>) = futures::join!(watchers_fut, validate_event_streams_fut);
1921 }
1922
1923 #[ip_test(I)]
1929 #[test_case(false, false; "no_trailing")]
1930 #[test_case(true, false; "trailing_event")]
1931 #[test_case(false, true; "trailing_batch")]
1932 #[test_case(true, true; "trailing_event_and_batch")]
1933 #[fuchsia_async::run_singlethreaded(test)]
1934 async fn event_stream_from_state_conversion_error<I: FidlRouteIpExt>(
1935 trailing_event: bool,
1936 trailing_batch: bool,
1937 ) {
1938 #[derive(GenericOverIp)]
1941 #[generic_over_ip(I, Ip)]
1942 struct EventHolder<I: FidlRouteIpExt>(I::WatchEvent);
1943 let EventHolder(bad_event) = I::map_ip(
1944 (),
1945 |()| {
1946 EventHolder(fnet_routes::EventV4::Added(fnet_routes::InstalledRouteV4 {
1947 route: Some(fnet_routes::RouteV4 {
1948 destination: fidl_ip_v4_with_prefix!("192.168.0.1/24"),
1949 ..fnet_routes::RouteV4::arbitrary_test_value()
1950 }),
1951 ..fnet_routes::InstalledRouteV4::arbitrary_test_value()
1952 }))
1953 },
1954 |()| {
1955 EventHolder(fnet_routes::EventV6::Added(fnet_routes::InstalledRouteV6 {
1956 route: Some(fnet_routes::RouteV6 {
1957 destination: fidl_ip_v6_with_prefix!("fe80::1/64"),
1958 ..fnet_routes::RouteV6::arbitrary_test_value()
1959 }),
1960 ..fnet_routes::InstalledRouteV6::arbitrary_test_value()
1961 }))
1962 },
1963 );
1964
1965 let batch = std::iter::once(bad_event)
1966 .chain(trailing_event.then(|| internal_testutil::generate_event::<I>(0)).into_iter())
1968 .collect::<Vec<_>>();
1969 let batches = std::iter::once(batch)
1970 .chain(trailing_batch.then(|| vec![internal_testutil::generate_event::<I>(1)]))
1972 .collect::<Vec<_>>();
1973
1974 let (state, state_server_end) = fidl::endpoints::create_proxy::<I::StateMarker>();
1976 let (mut state_request_stream, _control_handle) =
1977 state_server_end.into_stream_and_control_handle();
1978 let watcher_fut = state_request_stream
1979 .next()
1980 .then(|req| {
1981 testutil::serve_state_request::<I>(
1982 req.expect("State request_stream unexpectedly ended"),
1983 futures::stream::iter(batches),
1984 )
1985 })
1986 .fuse();
1987
1988 let event_stream =
1989 event_stream_from_state::<I>(&state).expect("failed to connect to watcher").fuse();
1990
1991 futures::pin_mut!(watcher_fut, event_stream);
1992 let ((), events) = futures::join!(watcher_fut, event_stream.collect::<Vec<_>>());
1993 assert_matches!(&events[..], &[Err(WatchError::Conversion(_))]);
1994 }
1995
1996 #[ip_test(I)]
2001 #[test_case(false; "no_trailing_batch")]
2002 #[test_case(true; "trailing_batch")]
2003 #[fuchsia_async::run_singlethreaded(test)]
2004 async fn event_stream_from_state_empty_batch_error<I: FidlRouteIpExt>(trailing_batch: bool) {
2005 let batches = std::iter::once(Vec::new())
2006 .chain(trailing_batch.then(|| vec![internal_testutil::generate_event::<I>(0)]))
2008 .collect::<Vec<_>>();
2009
2010 let (state, state_server_end) = fidl::endpoints::create_proxy::<I::StateMarker>();
2012 let (mut state_request_stream, _control_handle) =
2013 state_server_end.into_stream_and_control_handle();
2014 let watcher_fut = state_request_stream
2015 .next()
2016 .then(|req| {
2017 testutil::serve_state_request::<I>(
2018 req.expect("State request_stream unexpectedly ended"),
2019 futures::stream::iter(batches),
2020 )
2021 })
2022 .fuse();
2023
2024 let event_stream =
2025 event_stream_from_state::<I>(&state).expect("failed to connect to watcher").fuse();
2026
2027 futures::pin_mut!(watcher_fut, event_stream);
2028 let ((), events) = futures::join!(watcher_fut, event_stream.collect::<Vec<_>>());
2029 assert_matches!(&events[..], &[Err(WatchError::EmptyEventBatch)]);
2030 }
2031
2032 fn arbitrary_test_route<I: Ip + FidlRouteIpExt>() -> InstalledRoute<I> {
2033 #[derive(GenericOverIp)]
2034 #[generic_over_ip(I, Ip)]
2035 struct RouteHolder<I: FidlRouteIpExt>(InstalledRoute<I>);
2036 let RouteHolder(route) = I::map_ip(
2037 (),
2038 |()| {
2039 RouteHolder(
2040 fnet_routes::InstalledRouteV4::arbitrary_test_value().try_into().unwrap(),
2041 )
2042 },
2043 |()| {
2044 RouteHolder(
2045 fnet_routes::InstalledRouteV6::arbitrary_test_value().try_into().unwrap(),
2046 )
2047 },
2048 );
2049 route
2050 }
2051
2052 enum CollectRoutesUntilIdleErrorTestCase {
2053 ErrorInStream,
2054 UnexpectedEvent,
2055 StreamEnded,
2056 }
2057
2058 #[ip_test(I)]
2059 #[test_case(CollectRoutesUntilIdleErrorTestCase::ErrorInStream; "error_in_stream")]
2060 #[test_case(CollectRoutesUntilIdleErrorTestCase::UnexpectedEvent; "unexpected_event")]
2061 #[test_case(CollectRoutesUntilIdleErrorTestCase::StreamEnded; "stream_ended")]
2062 #[fuchsia_async::run_singlethreaded(test)]
2063 async fn collect_routes_until_idle_error<I: FidlRouteIpExt>(
2064 test_case: CollectRoutesUntilIdleErrorTestCase,
2065 ) {
2066 let route = arbitrary_test_route();
2070 let (event, test_assertion): (_, Box<dyn FnOnce(_)>) = match test_case {
2071 CollectRoutesUntilIdleErrorTestCase::ErrorInStream => (
2072 Err(WatchError::EmptyEventBatch),
2073 Box::new(|result| {
2074 assert_matches!(result, Err(CollectRoutesUntilIdleError::ErrorInStream(_)))
2075 }),
2076 ),
2077 CollectRoutesUntilIdleErrorTestCase::UnexpectedEvent => (
2078 Ok(Event::Added(route)),
2079 Box::new(|result| {
2080 assert_matches!(result, Err(CollectRoutesUntilIdleError::UnexpectedEvent(_)))
2081 }),
2082 ),
2083 CollectRoutesUntilIdleErrorTestCase::StreamEnded => (
2084 Ok(Event::Existing(route)),
2085 Box::new(|result| {
2086 assert_matches!(result, Err(CollectRoutesUntilIdleError::StreamEnded))
2087 }),
2088 ),
2089 };
2090
2091 let event_stream = futures::stream::once(futures::future::ready(event));
2092 futures::pin_mut!(event_stream);
2093 let result = collect_routes_until_idle::<I, Vec<_>>(event_stream).await;
2094 test_assertion(result);
2095 }
2096
2097 #[ip_test(I)]
2100 #[fuchsia_async::run_singlethreaded]
2101 async fn collect_routes_until_idle_success<I: FidlRouteIpExt>() {
2102 let route = arbitrary_test_route();
2103 let event_stream = futures::stream::iter([
2104 Ok(Event::Existing(route)),
2105 Ok(Event::Idle),
2106 Ok(Event::Added(route)),
2107 ]);
2108
2109 futures::pin_mut!(event_stream);
2110 let existing = collect_routes_until_idle::<I, Vec<_>>(event_stream.by_ref())
2111 .await
2112 .expect("failed to collect existing routes");
2113 assert_eq!(&existing, &[route]);
2114
2115 let trailing_events = event_stream.collect::<Vec<_>>().await;
2116 assert_matches!(
2117 &trailing_events[..],
2118 &[Ok(Event::Added(found_route))] if found_route == route
2119 );
2120 }
2121
2122 #[ip_test(I)]
2123 #[fuchsia_async::run_singlethreaded]
2124 async fn wait_for_routes_errors<I: FidlRouteIpExt>() {
2125 let mut state = HashSet::new();
2126 let event_stream =
2127 futures::stream::once(futures::future::ready(Err(WatchError::EmptyEventBatch)));
2128 assert_matches!(
2129 wait_for_routes::<I, _, _>(event_stream, &mut state, |_| true).await,
2130 Err(WaitForRoutesError::ErrorInStream(WatchError::EmptyEventBatch))
2131 );
2132 assert!(state.is_empty());
2133
2134 let event_stream = futures::stream::empty();
2135 assert_matches!(
2136 wait_for_routes::<I, _, _>(event_stream, &mut state, |_| true).await,
2137 Err(WaitForRoutesError::StreamEnded)
2138 );
2139 assert!(state.is_empty());
2140
2141 let event_stream = futures::stream::once(futures::future::ready(Ok(Event::<I>::Unknown)));
2142 assert_matches!(
2143 wait_for_routes::<I, _, _>(event_stream, &mut state, |_| true).await,
2144 Err(WaitForRoutesError::UnknownEvent)
2145 );
2146 assert!(state.is_empty());
2147 }
2148
2149 #[ip_test(I)]
2150 #[fuchsia_async::run_singlethreaded]
2151 async fn wait_for_routes_add_remove<I: FidlRouteIpExt>() {
2152 let into_stream = |t| futures::stream::once(futures::future::ready(t));
2153
2154 let route = arbitrary_test_route::<I>();
2155 let mut state = HashSet::new();
2156
2157 let has_route = |routes: &HashSet<InstalledRoute<I>>| routes.contains(&route);
2160 assert_matches!(
2161 wait_for_routes::<I, _, _>(futures::stream::pending(), &mut state, has_route)
2162 .now_or_never(),
2163 None
2164 );
2165 assert!(state.is_empty());
2166 assert_matches!(
2167 wait_for_routes::<I, _, _>(into_stream(Ok(Event::Added(route))), &mut state, has_route)
2168 .now_or_never(),
2169 Some(Ok(()))
2170 );
2171 assert_eq!(state, HashSet::from_iter([route]));
2172
2173 assert_matches!(
2175 wait_for_routes::<I, _, _>(into_stream(Ok(Event::Added(route))), &mut state, has_route)
2176 .now_or_never(),
2177 Some(Err(WaitForRoutesError::AddedAlreadyExisting(r))) if r == route
2178 );
2179 assert_eq!(state, HashSet::from_iter([route]));
2180
2181 let does_not_have_route = |routes: &HashSet<InstalledRoute<I>>| !routes.contains(&route);
2184 assert_matches!(
2185 wait_for_routes::<I, _, _>(futures::stream::pending(), &mut state, does_not_have_route)
2186 .now_or_never(),
2187 None
2188 );
2189 assert_eq!(state, HashSet::from_iter([route]));
2190 assert_matches!(
2191 wait_for_routes::<I, _, _>(
2192 into_stream(Ok(Event::Removed(route))),
2193 &mut state,
2194 does_not_have_route
2195 )
2196 .now_or_never(),
2197 Some(Ok(()))
2198 );
2199 assert!(state.is_empty());
2200
2201 assert_matches!(
2203 wait_for_routes::<I, _, _>(
2204 into_stream(Ok(Event::Removed(route))),
2205 &mut state,
2206 does_not_have_route
2207 ).now_or_never(),
2208 Some(Err(WaitForRoutesError::RemovedNonExistent(r))) if r == route
2209 );
2210 assert!(state.is_empty());
2211 }
2212}