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