1use std::collections::HashSet;
9use std::fmt::Debug;
10use std::hash::{Hash, Hasher};
11use std::num::{NonZeroU32, NonZeroU64};
12
13use fidl::endpoints::ProtocolMarker;
14use fidl_fuchsia_net_routes_admin::RouteSetError;
15use {
16 fidl_fuchsia_net_interfaces_admin as fnet_interfaces_admin,
17 fidl_fuchsia_net_interfaces_ext as fnet_interfaces_ext,
18 fidl_fuchsia_net_resources as fnet_resources, fidl_fuchsia_net_root as fnet_root,
19 fidl_fuchsia_net_routes as fnet_routes, fidl_fuchsia_net_routes_ext as fnet_routes_ext,
20};
21
22use derivative::Derivative;
23use futures::StreamExt as _;
24use futures::channel::oneshot;
25use linux_uapi::{
26 rt_class_t_RT_TABLE_COMPAT, rt_class_t_RT_TABLE_MAIN, rtnetlink_groups_RTNLGRP_IPV4_ROUTE,
27 rtnetlink_groups_RTNLGRP_IPV6_ROUTE,
28};
29use net_types::ip::{GenericOverIp, Ip, IpAddress, IpVersion, Subnet};
30use net_types::{SpecifiedAddr, SpecifiedAddress, Witness as _};
31use netlink_packet_core::{NLM_F_MULTIPART, NetlinkMessage};
32use netlink_packet_route::route::{
33 RouteAddress, RouteAttribute, RouteHeader, RouteMessage, RouteProtocol, RouteScope, RouteType,
34};
35use netlink_packet_route::{AddressFamily, RouteNetlinkMessage};
36use netlink_packet_utils::DecodeError;
37use netlink_packet_utils::nla::Nla;
38
39use crate::client::{ClientTable, InternalClient};
40use crate::logging::{log_debug, log_error, log_warn};
41use crate::messaging::Sender;
42use crate::multicast_groups::ModernGroup;
43use crate::netlink_packet::UNSPECIFIED_SEQUENCE_NUMBER;
44use crate::netlink_packet::errno::Errno;
45use crate::protocol_family::ProtocolFamily;
46use crate::protocol_family::route::NetlinkRoute;
47use crate::route_tables::{
48 FidlRouteMap, ManagedRouteTable, NetlinkRouteTableIndex, NonZeroNetlinkRouteTableIndex,
49 RouteRemoveResult, RouteTable, RouteTableMap, TableNeedsCleanup, UnmanagedTable,
50};
51use crate::util::respond_to_completer;
52
53const MAIN_ROUTE_TABLE: u32 = rt_class_t_RT_TABLE_MAIN;
54pub(crate) const MAIN_ROUTE_TABLE_INDEX: NetlinkRouteTableIndex =
55 NetlinkRouteTableIndex::new(MAIN_ROUTE_TABLE);
56
57#[derive(Copy, Clone, Debug, PartialEq, Eq)]
59pub(crate) enum GetRouteArgs {
60 Dump,
61}
62
63#[derive(Copy, Clone, Debug, PartialEq, Eq, GenericOverIp)]
65#[generic_over_ip(I, Ip)]
66pub(crate) struct UnicastNewRouteArgs<I: Ip> {
67 pub subnet: Subnet<I::Addr>,
69 pub target: fnet_routes_ext::RouteTarget<I>,
72 pub priority: Option<NonZeroU32>,
75 pub table: NetlinkRouteTableIndex,
77}
78
79#[derive(Copy, Clone, Debug, PartialEq, Eq)]
81pub(crate) enum NewRouteArgs<I: Ip> {
82 Unicast(UnicastNewRouteArgs<I>),
84}
85
86#[derive(Copy, Clone, Debug, PartialEq, Eq, GenericOverIp)]
89#[generic_over_ip(I, Ip)]
90pub(crate) struct UnicastDelRouteArgs<I: Ip> {
91 pub(crate) subnet: Subnet<I::Addr>,
93 pub(crate) outbound_interface: Option<NonZeroU64>,
95 pub(crate) next_hop: Option<SpecifiedAddr<I::Addr>>,
97 pub(crate) priority: Option<NonZeroU32>,
99 pub(crate) table: NonZeroNetlinkRouteTableIndex,
101}
102
103#[derive(Copy, Clone, Debug, PartialEq, Eq)]
105pub(crate) enum DelRouteArgs<I: Ip> {
106 Unicast(UnicastDelRouteArgs<I>),
108}
109
110#[derive(Copy, Clone, Debug, PartialEq, Eq)]
112pub(crate) enum RouteRequestArgs<I: Ip> {
113 Get(GetRouteArgs),
115 New(NewRouteArgs<I>),
117 Del(DelRouteArgs<I>),
119}
120
121#[derive(Copy, Clone, Debug, PartialEq, Eq)]
123pub(crate) enum RequestArgs<I: Ip> {
124 Route(RouteRequestArgs<I>),
125}
126
127#[derive(Copy, Clone, Debug, PartialEq, Eq)]
129pub(crate) enum RequestError {
130 AlreadyExists,
132 DeletionNotAllowed,
135 InvalidRequest,
137 NotFound,
139 UnrecognizedInterface,
141 Unknown,
143}
144
145impl RequestError {
146 pub(crate) fn into_errno(self) -> Errno {
147 match self {
148 RequestError::AlreadyExists => Errno::EEXIST,
149 RequestError::InvalidRequest => Errno::EINVAL,
150 RequestError::NotFound => Errno::ESRCH,
151 RequestError::DeletionNotAllowed | RequestError::Unknown => Errno::ENOTSUP,
152 RequestError::UnrecognizedInterface => Errno::ENODEV,
153 }
154 }
155}
156
157fn map_route_set_error<I: Ip + fnet_routes_ext::FidlRouteIpExt>(
158 e: RouteSetError,
159 route: &I::Route,
160 interface_id: u64,
161) -> RequestError {
162 match e {
163 RouteSetError::Unauthenticated => {
164 panic!(
168 "authenticated for interface {:?}, but received unauthentication error from route set for route ({:?})",
169 interface_id, route,
170 );
171 }
172 RouteSetError::InvalidDestinationSubnet => {
173 log_debug!(
175 "invalid subnet observed from route ({:?}) from interface {:?}",
176 route,
177 interface_id,
178 );
179 return RequestError::InvalidRequest;
180 }
181 RouteSetError::InvalidNextHop => {
182 log_debug!(
184 "invalid next hop observed from route ({:?}) from interface {:?}",
185 route,
186 interface_id,
187 );
188 return RequestError::InvalidRequest;
189 }
190 err => {
191 log_error!(
197 "unrecognized route set error {:?} with route ({:?}) from interface {:?}",
198 err,
199 route,
200 interface_id
201 );
202 return RequestError::Unknown;
203 }
204 }
205}
206
207#[derive(Derivative, GenericOverIp)]
209#[derivative(Debug(bound = ""))]
210#[generic_over_ip(I, Ip)]
211pub(crate) struct Request<S: Sender<<NetlinkRoute as ProtocolFamily>::Response>, I: Ip> {
212 pub args: RequestArgs<I>,
214 pub sequence_number: u32,
219 pub client: InternalClient<NetlinkRoute, S>,
221 pub completer: oneshot::Sender<Result<(), RequestError>>,
223}
224
225#[derive(GenericOverIp)]
229#[generic_over_ip(I, Ip)]
230pub(crate) struct RoutesWorker<
231 I: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
232> {
233 fidl_route_map: FidlRouteMap<I>,
234}
235
236fn get_table_u8_and_nla_from_key(
237 netlink_id: NetlinkRouteTableIndex,
238) -> (u8, Option<RouteAttribute>) {
239 let table_id = netlink_id.get();
240 match u8::try_from(table_id) {
243 Ok(t) => (t, None),
244 Err(_) => (rt_class_t_RT_TABLE_COMPAT as u8, Some(RouteAttribute::Table(table_id))),
246 }
247}
248
249#[derive(Clone, Debug, PartialEq, Eq)]
251pub(crate) enum PendingRouteRequestArgs<I: Ip> {
252 New(NewRouteArgs<I>),
254 Del((NetlinkRouteMessage, NonZeroNetlinkRouteTableIndex)),
256}
257
258#[derive(Derivative)]
259#[derivative(Debug(bound = ""))]
260pub(crate) struct PendingRouteRequest<S: Sender<<NetlinkRoute as ProtocolFamily>::Response>, I: Ip>
261{
262 request_args: PendingRouteRequestArgs<I>,
263 client: InternalClient<NetlinkRoute, S>,
264 completer: oneshot::Sender<Result<(), RequestError>>,
265}
266
267impl<I: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt>
268 RoutesWorker<I>
269{
270 pub(crate) async fn create(
277 main_route_table: &<I::RouteTableMarker as ProtocolMarker>::Proxy,
278 routes_state_proxy: &<I::StateMarker as ProtocolMarker>::Proxy,
279 route_table_provider: <I::RouteTableProviderMarker as ProtocolMarker>::Proxy,
280 ) -> (
281 Self,
282 RouteTableMap<I>,
283 impl futures::Stream<Item = Result<fnet_routes_ext::Event<I>, fnet_routes_ext::WatchError>>
284 + Unpin
285 + 'static,
286 ) {
287 let mut route_event_stream = Box::pin(
288 fnet_routes_ext::event_stream_from_state(routes_state_proxy)
289 .expect("connecting to fuchsia.net.routes.State FIDL should succeed"),
290 );
291 let installed_routes = fnet_routes_ext::collect_routes_until_idle::<_, HashSet<_>>(
292 route_event_stream.by_ref(),
293 )
294 .await
295 .expect("determining already installed routes should succeed");
296
297 let mut fidl_route_map = FidlRouteMap::<I>::default();
298 for fnet_routes_ext::InstalledRoute { route, effective_properties, table_id } in
299 installed_routes
300 {
301 let _: Option<fnet_routes_ext::EffectiveRouteProperties> =
302 fidl_route_map.add(route, table_id, effective_properties);
303 }
304
305 let main_route_table_id = fnet_routes_ext::admin::get_table_id::<I>(main_route_table)
306 .await
307 .expect("getting main route table ID should succeed");
308 let unmanaged_route_set_proxy =
309 fnet_routes_ext::admin::new_route_set::<I>(main_route_table)
310 .expect("getting unmanaged route set should succeed");
311 let route_table_map = RouteTableMap::new(
312 main_route_table.clone(),
313 main_route_table_id,
314 unmanaged_route_set_proxy,
315 route_table_provider,
316 );
317 (Self { fidl_route_map }, route_table_map, route_event_stream)
318 }
319
320 pub(crate) fn handle_route_watcher_event<
328 S: Sender<<NetlinkRoute as ProtocolFamily>::Response>,
329 >(
330 &mut self,
331 route_table_map: &mut RouteTableMap<I>,
332 route_clients: &ClientTable<NetlinkRoute, S>,
333 event: fnet_routes_ext::Event<I>,
334 ) -> Option<TableNeedsCleanup> {
335 handle_route_watcher_event::<I, S>(
336 route_table_map,
337 &mut self.fidl_route_map,
338 route_clients,
339 event,
340 )
341 }
342
343 fn get_interface_control(
344 interfaces_proxy: &fnet_root::InterfacesProxy,
345 interface_id: u64,
346 ) -> fnet_interfaces_ext::admin::Control {
347 let (control, server_end) =
348 fidl::endpoints::create_proxy::<fnet_interfaces_admin::ControlMarker>();
349 interfaces_proxy.get_admin(interface_id, server_end).expect("send get admin request");
350 fnet_interfaces_ext::admin::Control::new(control)
351 }
352
353 async fn authenticate_for_interface(
354 interfaces_proxy: &fnet_root::InterfacesProxy,
355 route_set_proxy: &<I::RouteSetMarker as fidl::endpoints::ProtocolMarker>::Proxy,
356 interface_id: u64,
357 ) -> Result<(), RequestError> {
358 let control = Self::get_interface_control(interfaces_proxy, interface_id);
359
360 let grant = match control.get_authorization_for_interface().await {
361 Ok(grant) => grant,
362 Err(fnet_interfaces_ext::admin::TerminalError::Fidl(
363 fidl::Error::ClientChannelClosed { status, protocol_name, .. },
364 )) => {
365 log_debug!(
366 "{}: netstack dropped the {} channel, interface {} does not exist",
367 status,
368 protocol_name,
369 interface_id
370 );
371 return Err(RequestError::UnrecognizedInterface);
372 }
373 Err(e) => panic!("unexpected error from interface authorization request: {e:?}"),
374 };
375 let proof = fnet_interfaces_ext::admin::proof_from_grant(&grant);
376
377 #[derive(GenericOverIp)]
378 #[generic_over_ip(I, Ip)]
379 struct AuthorizeInputs<'a, I: fnet_routes_ext::admin::FidlRouteAdminIpExt> {
380 route_set_proxy: &'a <I::RouteSetMarker as fidl::endpoints::ProtocolMarker>::Proxy,
381 proof: fnet_resources::ProofOfInterfaceAuthorization,
382 }
383
384 let authorize_fut = I::map_ip_in(
385 AuthorizeInputs::<'_, I> { route_set_proxy, proof },
386 |AuthorizeInputs { route_set_proxy, proof }| {
387 route_set_proxy.authenticate_for_interface(proof)
388 },
389 |AuthorizeInputs { route_set_proxy, proof }| {
390 route_set_proxy.authenticate_for_interface(proof)
391 },
392 );
393
394 authorize_fut.await.expect("sent authorization request").map_err(|e| {
395 log_warn!("error authenticating for interface ({interface_id}): {e:?}");
396 RequestError::UnrecognizedInterface
397 })?;
398
399 Ok(())
400 }
401
402 async fn handle_new_route_request(
408 &self,
409 route_tables: &mut RouteTableMap<I>,
410 interfaces_proxy: &fnet_root::InterfacesProxy,
411 args: NewRouteArgs<I>,
412 ) -> Result<NewRouteArgs<I>, RequestError> {
413 let (interface_id, table) = match args {
414 NewRouteArgs::Unicast(args) => (args.target.outbound_interface, args.table),
415 };
416 let route: fnet_routes_ext::Route<I> = args.into();
417
418 route_tables.create_managed_route_table_if_not_present(table).await;
422
423 let table_id = route_tables.get(&table).expect("should be populated").fidl_table_id();
424
425 let new_route_conflicts_with_existing = self
433 .fidl_route_map
434 .iter_table(route_tables.get(&table).expect("should be populated").fidl_table_id())
435 .any(|(stored_route, stored_props)| {
436 routes_conflict::<I>(
437 fnet_routes_ext::InstalledRoute {
438 route: *stored_route,
439 effective_properties: *stored_props,
440 table_id,
441 },
442 route,
443 table_id,
444 )
445 });
446
447 if new_route_conflicts_with_existing {
448 return Err(RequestError::AlreadyExists);
449 }
450
451 let route_set = match route_tables.get(&table).expect("should have just been populated") {
452 RouteTable::Managed(ManagedRouteTable { route_set_proxy, .. }) => route_set_proxy,
453 RouteTable::Unmanaged(UnmanagedTable { route_set_proxy, .. }) => route_set_proxy,
454 };
455
456 let route: I::Route =
457 route.try_into().expect("should not have constructed unknown route action");
458 let added_to_table: bool = Self::dispatch_route_proxy_fn(
459 &route,
460 interface_id,
461 &interfaces_proxy,
462 route_set,
463 fnet_routes_ext::admin::add_route::<I>,
464 )
465 .await?;
466
467 if !added_to_table {
470 return Err(RequestError::AlreadyExists);
471 };
472
473 Ok(args)
474 }
475
476 async fn handle_del_route_request(
482 &self,
483 interfaces_proxy: &fnet_root::InterfacesProxy,
484 route_tables: &mut RouteTableMap<I>,
485 del_route_args: DelRouteArgs<I>,
486 ) -> Result<(NetlinkRouteMessage, NonZeroNetlinkRouteTableIndex), RequestError> {
487 let table = match del_route_args {
488 DelRouteArgs::Unicast(args) => args.table,
489 };
490
491 let route_to_delete = &self
492 .select_route_for_deletion(route_tables, del_route_args)
493 .ok_or(RequestError::NotFound)?;
494 let NetlinkRouteMessage(route) = route_to_delete;
495 let interface_id = route
496 .attributes
497 .iter()
498 .filter_map(|nla| match nla {
499 RouteAttribute::Oif(interface) => Some(*interface as u64),
500 _nla => None,
501 })
502 .next()
503 .expect("there should be exactly one Oif NLA present");
504
505 let route_set = match route_tables.get(&table.into()) {
506 None => return Err(RequestError::NotFound),
507 Some(lookup) => match lookup {
508 RouteTable::Managed(ManagedRouteTable { route_set_proxy, .. }) => route_set_proxy,
509 RouteTable::Unmanaged(UnmanagedTable { route_set_proxy, .. }) => route_set_proxy,
510 },
511 };
512
513 let route: fnet_routes_ext::Route<I> = route_to_delete.to_owned().into();
514
515 let route: I::Route = route.try_into().expect("route should be converted");
516 let removed: bool = Self::dispatch_route_proxy_fn(
517 &route,
518 interface_id,
519 &interfaces_proxy,
520 route_set,
521 fnet_routes_ext::admin::remove_route::<I>,
522 )
523 .await?;
524
525 if !removed {
526 log_error!(
527 "Route was not removed as a result of this call. Likely Linux wanted \
528 to remove a route from the global route set which is not supported \
529 by this API, route: {:?}",
530 route_to_delete
531 );
532 return Err(RequestError::DeletionNotAllowed);
533 }
534
535 Ok((route_to_delete.to_owned(), table))
536 }
537
538 fn select_route_for_deletion(
548 &self,
549 route_tables: &RouteTableMap<I>,
550 deletion_args: DelRouteArgs<I>,
551 ) -> Option<NetlinkRouteMessage> {
552 select_route_for_deletion(&self.fidl_route_map, route_tables, deletion_args)
553 }
554
555 async fn dispatch_route_proxy_fn<'a, Fut>(
563 route: &'a I::Route,
564 interface_id: u64,
565 interfaces_proxy: &'a fnet_root::InterfacesProxy,
566 route_set_proxy: &'a <I::RouteSetMarker as ProtocolMarker>::Proxy,
567 dispatch_fn: impl Fn(&'a <I::RouteSetMarker as ProtocolMarker>::Proxy, &'a I::Route) -> Fut,
568 ) -> Result<bool, RequestError>
569 where
570 Fut: futures::Future<Output = Result<Result<bool, RouteSetError>, fidl::Error>>,
571 {
572 match dispatch_fn(route_set_proxy, &route).await.expect("sent route proxy request") {
573 Ok(made_change) => return Ok(made_change),
574 Err(RouteSetError::Unauthenticated) => {}
575 Err(e) => {
576 log_warn!("error altering route on interface ({interface_id}): {e:?}");
577 return Err(map_route_set_error::<I>(e, route, interface_id));
578 }
579 };
580
581 Self::authenticate_for_interface(interfaces_proxy, route_set_proxy, interface_id).await?;
584
585 dispatch_fn(route_set_proxy, &route).await.expect("sent route proxy request").map_err(|e| {
589 log_warn!(
590 "error altering route after authenticating for \
591 interface ({interface_id}): {e:?}"
592 );
593 map_route_set_error::<I>(e, route, interface_id)
594 })
595 }
596
597 pub(crate) async fn handle_request<S: Sender<<NetlinkRoute as ProtocolFamily>::Response>>(
603 &mut self,
604 route_tables: &mut RouteTableMap<I>,
605 interfaces_proxy: &fnet_root::InterfacesProxy,
606 Request { args, sequence_number, mut client, completer }: Request<S, I>,
607 ) -> Option<PendingRouteRequest<S, I>> {
608 log_debug!("handling request {args:?} from {client}");
609
610 #[derive(Derivative)]
611 #[derivative(Debug(bound = ""))]
612 enum RequestHandled<S, I>
613 where
614 S: Sender<<NetlinkRoute as ProtocolFamily>::Response>,
615 I: Ip,
616 {
617 Pending(PendingRouteRequest<S, I>),
618 Done(
619 Result<(), RequestError>,
620 InternalClient<NetlinkRoute, S>,
621 oneshot::Sender<Result<(), RequestError>>,
622 ),
623 }
624
625 let request_handled = match args {
626 RequestArgs::Route(args) => match args {
627 RouteRequestArgs::Get(args) => match args {
628 GetRouteArgs::Dump => {
629 self.fidl_route_map
630 .iter()
631 .flat_map(|(route, tables)| {
632 tables.iter().map(move |(fidl_table_id, props)| {
633 fnet_routes_ext::InstalledRoute {
634 route: *route,
635 table_id: *fidl_table_id,
636 effective_properties: *props,
637 }
638 })
639 })
640 .filter_map(|installed_route| {
641 let table_index =
642 route_tables.get_netlink_id(&installed_route.table_id)?;
643 NetlinkRouteMessage::optionally_from(installed_route, table_index)
644 })
645 .for_each(|message| {
646 client.send_unicast(
647 message.into_rtnl_new_route(sequence_number, true),
648 )
649 });
650 RequestHandled::Done(Ok(()), client, completer)
651 }
652 },
653 RouteRequestArgs::New(args) => {
654 match self.handle_new_route_request(route_tables, interfaces_proxy, args).await
655 {
656 Ok(args) => {
657 RequestHandled::Pending(PendingRouteRequest {
660 request_args: PendingRouteRequestArgs::New(args),
661 client,
662 completer,
663 })
664 }
665 Err(err) => RequestHandled::Done(Err(err), client, completer),
666 }
667 }
668 RouteRequestArgs::Del(args) => {
669 match self.handle_del_route_request(interfaces_proxy, route_tables, args).await
670 {
671 Ok(del_route) => {
672 RequestHandled::Pending(PendingRouteRequest {
676 request_args: PendingRouteRequestArgs::Del(del_route),
677 client,
678 completer,
679 })
680 }
681 Err(e) => RequestHandled::Done(Err(e), client, completer),
682 }
683 }
684 },
685 };
686
687 match request_handled {
688 RequestHandled::Done(result, client, completer) => {
689 log_debug!("handled request {args:?} from {client} with result = {result:?}");
690
691 respond_to_completer(client, completer, result, args);
692 None
693 }
694 RequestHandled::Pending(pending) => Some(pending),
695 }
696 }
697
698 pub(crate) fn handle_pending_request<S: Sender<<NetlinkRoute as ProtocolFamily>::Response>>(
705 &self,
706 route_tables: &mut RouteTableMap<I>,
707 pending_route_request: PendingRouteRequest<S, I>,
708 ) -> Option<PendingRouteRequest<S, I>> {
709 let PendingRouteRequest { request_args, client: _, completer: _ } = &pending_route_request;
710
711 let done = match request_args {
712 PendingRouteRequestArgs::New(args) => {
713 let netlink_table_id = match args {
714 NewRouteArgs::Unicast(args) => &args.table,
715 };
716
717 let own_fidl_table_id = route_tables
718 .get(netlink_table_id)
719 .expect("should recognize table referenced in pending new route request")
720 .fidl_table_id();
721
722 self.fidl_route_map
723 .route_is_installed_in_tables(&(*args).into(), [&own_fidl_table_id])
724 }
725 PendingRouteRequestArgs::Del((route_msg, pending_table)) => {
728 let netlink_table_id: NetlinkRouteTableIndex = (*pending_table).into();
729 let own_fidl_table_id: Option<fnet_routes_ext::TableId> =
732 route_tables.get(&netlink_table_id).map(|table| table.fidl_table_id());
733
734 if let Some(own_fidl_table_id) = own_fidl_table_id {
735 self.fidl_route_map.route_is_uninstalled_in_tables(
736 &route_msg.clone().into(),
737 [&own_fidl_table_id],
738 )
739 } else {
740 true
741 }
742 }
743 };
744
745 if done {
746 log_debug!("completed pending request; req = {pending_route_request:?}");
747 let PendingRouteRequest { request_args, client, completer } = pending_route_request;
748
749 respond_to_completer(client, completer, Ok(()), request_args);
750 None
751 } else {
752 log_debug!("pending request not done yet; req = {pending_route_request:?}");
754 Some(pending_route_request)
755 }
756 }
757
758 pub(crate) fn any_routes_reference_table(
759 &self,
760 TableNeedsCleanup(table_id, _table_index): TableNeedsCleanup,
761 ) -> bool {
762 self.fidl_route_map.table_is_present(table_id)
763 }
764}
765
766fn routes_conflict<I: Ip>(
774 stored_route: fnet_routes_ext::InstalledRoute<I>,
775 incoming_route: fnet_routes_ext::Route<I>,
776 incoming_table: fnet_routes_ext::TableId,
777) -> bool {
778 let fnet_routes_ext::InstalledRoute {
779 route:
780 fnet_routes_ext::Route {
781 destination: stored_destination,
782 action: _,
783 properties: stored_properties,
784 },
785 effective_properties: _,
786 table_id: stored_table_id,
787 } = stored_route;
788
789 let destinations_match = stored_destination == incoming_route.destination;
790 let specified_metrics_match = stored_properties.specified_properties.metric
791 == incoming_route.properties.specified_properties.metric;
792 let tables_match = stored_table_id == incoming_table;
793
794 destinations_match && specified_metrics_match && tables_match
795}
796
797fn handle_route_watcher_event<
798 I: Ip + fnet_routes_ext::admin::FidlRouteAdminIpExt + fnet_routes_ext::FidlRouteIpExt,
799 S: Sender<<NetlinkRoute as ProtocolFamily>::Response>,
800>(
801 route_table_map: &mut RouteTableMap<I>,
802 fidl_route_map: &mut FidlRouteMap<I>,
803 route_clients: &ClientTable<NetlinkRoute, S>,
804 event: fnet_routes_ext::Event<I>,
805) -> Option<TableNeedsCleanup> {
806 let (message_for_clients, table_no_routes) = match event {
807 fnet_routes_ext::Event::Added(added_installed_route) => {
808 let fnet_routes_ext::InstalledRoute { route, table_id, effective_properties } =
809 added_installed_route;
810
811 match fidl_route_map.add(route, table_id, effective_properties) {
812 None => (),
813 Some(_properties) => {
814 panic!(
815 "Netstack reported the addition of an existing route: \
816 route={route:?}, table={table_id:?}"
817 );
818 }
819 }
820
821 match route_table_map.get_netlink_id(&table_id) {
822 None => {
823 crate::logging::log_warn!(
826 "Observed an added route via the routes watcher that is installed in a \
827 non-main FIDL table not managed by netlink: {added_installed_route:?}"
828 );
829 (None, None)
832 }
833 Some(table) => (
834 NetlinkRouteMessage::optionally_from(added_installed_route, table).map(
835 |route_message| {
836 route_message.into_rtnl_new_route(UNSPECIFIED_SEQUENCE_NUMBER, false)
837 },
838 ),
839 None,
840 ),
841 }
842 }
843 fnet_routes_ext::Event::Removed(removed_installed_route) => {
844 let fnet_routes_ext::InstalledRoute { route, table_id, effective_properties: _ } =
845 removed_installed_route;
846
847 let need_clean_up_empty_table = match fidl_route_map.remove(route, table_id) {
848 RouteRemoveResult::DidNotExist => {
849 panic!(
850 "Netstack reported the removal of an unknown route: \
851 route={route:?}, table={table_id:?}"
852 );
853 }
854 RouteRemoveResult::RemovedButTableNotEmpty(_properties) => false,
855 RouteRemoveResult::RemovedAndTableNewlyEmpty(_properties) => true,
856 };
857
858 let (notify_message, table_index) = match route_table_map.get_netlink_id(&table_id) {
859 None => {
860 crate::logging::log_warn!(
863 "Observed a removed route via the routes watcher that is installed in a \
864 non-main FIDL table not managed by netlink: {removed_installed_route:?}"
865 );
866 (None, None)
869 }
870 Some(table) => (
871 NetlinkRouteMessage::optionally_from(removed_installed_route, table)
872 .map(|route_message| route_message.into_rtnl_del_route()),
873 Some(table),
874 ),
875 };
876
877 let table_cleanup = match (need_clean_up_empty_table, table_index) {
878 (true, Some(table_index)) => Some(TableNeedsCleanup(table_id, table_index)),
879 _ => None,
880 };
881
882 (notify_message, table_cleanup)
883 }
884 e @ fnet_routes_ext::Event::Existing(_)
887 | e @ fnet_routes_ext::Event::Idle
888 | e @ fnet_routes_ext::Event::Unknown => {
889 panic!("Netstack reported an unexpected route event: {e:?}");
890 }
891 };
892 if let Some(message_for_clients) = message_for_clients {
893 let route_group = match I::VERSION {
894 IpVersion::V4 => ModernGroup(rtnetlink_groups_RTNLGRP_IPV4_ROUTE),
895 IpVersion::V6 => ModernGroup(rtnetlink_groups_RTNLGRP_IPV6_ROUTE),
896 };
897 route_clients.send_message_to_group(message_for_clients, route_group);
898 }
899
900 table_no_routes
901}
902
903#[derive(Clone, Debug, Eq, PartialEq)]
906pub(crate) struct NetlinkRouteMessage(pub(crate) RouteMessage);
907
908impl NetlinkRouteMessage {
909 pub(crate) fn optionally_from<I: Ip>(
913 route: fnet_routes_ext::InstalledRoute<I>,
914 table: NetlinkRouteTableIndex,
915 ) -> Option<NetlinkRouteMessage> {
916 match NetlinkRouteMessage::try_from_installed_route::<I>(route, table) {
917 Ok(route) => Some(route),
918 Err(NetlinkRouteMessageConversionError::RouteActionNotForwarding) => {
919 log_warn!("Unexpected non-forwarding route in routing table: {:?}", route);
920 None
921 }
922 Err(NetlinkRouteMessageConversionError::InvalidInterfaceId(id)) => {
923 log_warn!("Invalid interface id found in routing table route: {:?}", id);
924 None
925 }
926 Err(NetlinkRouteMessageConversionError::FailedToDecode(err)) => {
927 log_warn!("Unable to decode route address: {:?}", err);
928 None
929 }
930 }
931 }
932
933 pub(crate) fn into_rtnl_new_route(
935 self,
936 sequence_number: u32,
937 is_dump: bool,
938 ) -> NetlinkMessage<RouteNetlinkMessage> {
939 let NetlinkRouteMessage(message) = self;
940 let mut msg: NetlinkMessage<RouteNetlinkMessage> =
941 RouteNetlinkMessage::NewRoute(message).into();
942 msg.header.sequence_number = sequence_number;
943 if is_dump {
944 msg.header.flags |= NLM_F_MULTIPART;
945 }
946 msg.finalize();
947 msg
948 }
949
950 fn into_rtnl_del_route(self) -> NetlinkMessage<RouteNetlinkMessage> {
952 let NetlinkRouteMessage(message) = self;
953 let mut msg: NetlinkMessage<RouteNetlinkMessage> =
954 RouteNetlinkMessage::DelRoute(message).into();
955 msg.finalize();
956 msg
957 }
958
959 fn try_from_installed_route<I: Ip>(
964 fnet_routes_ext::InstalledRoute {
965 route: fnet_routes_ext::Route { destination, action, properties: _ },
966 effective_properties: fnet_routes_ext::EffectiveRouteProperties { metric },
967 table_id: _,
969 }: fnet_routes_ext::InstalledRoute<I>,
970 table: NetlinkRouteTableIndex,
971 ) -> Result<Self, NetlinkRouteMessageConversionError> {
972 let fnet_routes_ext::RouteTarget { outbound_interface, next_hop } = match action {
973 fnet_routes_ext::RouteAction::Unknown => {
974 return Err(NetlinkRouteMessageConversionError::RouteActionNotForwarding);
975 }
976 fnet_routes_ext::RouteAction::Forward(target) => target,
977 };
978
979 let mut route_header = RouteHeader::default();
980 route_header.address_family = match I::VERSION {
983 IpVersion::V4 => AddressFamily::Inet,
984 IpVersion::V6 => AddressFamily::Inet6,
985 }
986 .try_into()
987 .expect("should fit into u8");
988 route_header.destination_prefix_length = destination.prefix();
989
990 let (table_u8, table_nla) = get_table_u8_and_nla_from_key(table);
991 route_header.table = table_u8;
992
993 route_header.protocol = RouteProtocol::Kernel;
1001 route_header.scope = RouteScope::Universe;
1004 route_header.kind = RouteType::Unicast;
1005
1006 let mut nlas = vec![];
1027
1028 if route_header.destination_prefix_length > 0 {
1031 let destination_nla = RouteAttribute::Destination(RouteAddress::parse(
1032 route_header.address_family,
1033 destination.network().bytes(),
1034 )?);
1035 nlas.push(destination_nla);
1036 }
1037
1038 let outbound_id: u32 = match outbound_interface.try_into() {
1040 Err(std::num::TryFromIntError { .. }) => {
1041 return Err(NetlinkRouteMessageConversionError::InvalidInterfaceId(
1042 outbound_interface,
1043 ));
1044 }
1045 Ok(id) => id,
1046 };
1047 let oif_nla = RouteAttribute::Oif(outbound_id);
1048 nlas.push(oif_nla);
1049
1050 if let Some(next_hop) = next_hop {
1051 let bytes = RouteAddress::parse(route_header.address_family, next_hop.bytes())?;
1052 let gateway_nla = RouteAttribute::Gateway(bytes);
1053 nlas.push(gateway_nla);
1054 }
1055
1056 let priority_nla = RouteAttribute::Priority(metric);
1057 nlas.push(priority_nla);
1058
1059 if let Some(nla) = table_nla {
1061 nlas.push(nla);
1062 }
1063
1064 let mut route_message = RouteMessage::default();
1065 route_message.header = route_header;
1066 route_message.attributes = nlas;
1067 Ok(NetlinkRouteMessage(route_message))
1068 }
1069}
1070
1071impl Hash for NetlinkRouteMessage {
1072 fn hash<H: Hasher>(&self, state: &mut H) {
1073 let NetlinkRouteMessage(message) = self;
1074 message.header.hash(state);
1075
1076 let mut buffer = vec![];
1077 message.attributes.iter().for_each(|nla| {
1078 buffer.resize(nla.value_len(), 0u8);
1079 nla.emit_value(&mut buffer);
1080 buffer.hash(state);
1081 });
1082 }
1083}
1084
1085#[derive(Debug, PartialEq)]
1087pub(crate) enum NetlinkRouteMessageConversionError {
1088 RouteActionNotForwarding,
1090 InvalidInterfaceId(u64),
1092 FailedToDecode(DecodeErrorWrapper),
1094}
1095
1096#[derive(Debug)]
1097pub(crate) struct DecodeErrorWrapper(DecodeError);
1098
1099impl PartialEq for DecodeErrorWrapper {
1100 fn eq(&self, other: &Self) -> bool {
1101 return format!("{:?}", self.0) == format!("{:?}", other.0);
1104 }
1105}
1106
1107impl From<DecodeError> for NetlinkRouteMessageConversionError {
1108 fn from(err: DecodeError) -> Self {
1109 NetlinkRouteMessageConversionError::FailedToDecode(DecodeErrorWrapper(err))
1110 }
1111}
1112
1113pub const DEFAULT_IPV4_ROUTE_PRIORITY: u32 = 0;
1116pub const DEFAULT_IPV6_ROUTE_PRIORITY: u32 = 1024;
1119
1120fn netlink_priority_to_specified_metric(
1121 prio: Option<NonZeroU32>,
1122 v: IpVersion,
1123) -> fnet_routes::SpecifiedMetric {
1124 fnet_routes::SpecifiedMetric::ExplicitMetric(match (prio, v) {
1127 (Some(prio), IpVersion::V4 | IpVersion::V6) => prio.get(),
1128 (None, IpVersion::V4) => DEFAULT_IPV4_ROUTE_PRIORITY,
1129 (None, IpVersion::V6) => DEFAULT_IPV6_ROUTE_PRIORITY,
1130 })
1131}
1132
1133impl<I: Ip> From<NewRouteArgs<I>> for fnet_routes_ext::Route<I> {
1134 fn from(new_route_args: NewRouteArgs<I>) -> Self {
1135 match new_route_args {
1136 NewRouteArgs::Unicast(args) => {
1137 let UnicastNewRouteArgs { subnet, target, priority, table: _ } = args;
1138 let metric = netlink_priority_to_specified_metric(priority, I::VERSION);
1139 fnet_routes_ext::Route {
1140 destination: subnet,
1141 action: fnet_routes_ext::RouteAction::Forward(target),
1142 properties: fnet_routes_ext::RouteProperties {
1143 specified_properties: fnet_routes_ext::SpecifiedRouteProperties { metric },
1144 },
1145 }
1146 }
1147 }
1148 }
1149}
1150
1151impl<I: Ip> From<NetlinkRouteMessage> for fnet_routes_ext::Route<I> {
1156 fn from(netlink_route_message: NetlinkRouteMessage) -> Self {
1157 let NetlinkRouteMessage(route_message) = netlink_route_message;
1158 let RouteNlaView { subnet, metric, interface_id, next_hop } =
1159 view_existing_route_nlas(&route_message);
1160 let subnet = match subnet {
1161 Some(subnet) => crate::netlink_packet::ip_addr_from_route::<I>(&subnet)
1162 .expect("should be valid addr"),
1163 None => I::UNSPECIFIED_ADDRESS,
1164 };
1165
1166 let subnet = Subnet::new(subnet, route_message.header.destination_prefix_length)
1167 .expect("should be valid subnet");
1168
1169 let next_hop = match next_hop {
1170 Some(next_hop) => crate::netlink_packet::ip_addr_from_route::<I>(&next_hop)
1171 .map(SpecifiedAddr::new)
1172 .expect("should be valid addr"),
1173 None => None,
1174 };
1175
1176 fnet_routes_ext::Route {
1177 destination: subnet,
1178 action: fnet_routes_ext::RouteAction::Forward(fnet_routes_ext::RouteTarget {
1179 outbound_interface: *interface_id as u64,
1180 next_hop,
1181 }),
1182 properties: fnet_routes_ext::RouteProperties {
1183 specified_properties: fnet_routes_ext::SpecifiedRouteProperties {
1184 metric: fnet_routes::SpecifiedMetric::ExplicitMetric(*metric),
1185 },
1186 },
1187 }
1188 }
1189}
1190
1191struct RouteNlaView<'a> {
1193 subnet: Option<&'a RouteAddress>,
1194 metric: &'a u32,
1195 interface_id: &'a u32,
1196 next_hop: Option<&'a RouteAddress>,
1197}
1198
1199fn view_existing_route_nlas(route: &RouteMessage) -> RouteNlaView<'_> {
1213 let mut subnet = None;
1214 let mut metric = None;
1215 let mut interface_id = None;
1216 let mut next_hop = None;
1217 let mut table = None;
1218 route.attributes.iter().for_each(|nla| match nla {
1219 RouteAttribute::Destination(dst) => {
1220 assert_eq!(subnet, None, "existing route has multiple `Destination` NLAs");
1221 subnet = Some(dst)
1222 }
1223 RouteAttribute::Priority(p) => {
1224 assert_eq!(metric, None, "existing route has multiple `Priority` NLAs");
1225 metric = Some(p)
1226 }
1227 RouteAttribute::Oif(interface) => {
1228 assert_eq!(interface_id, None, "existing route has multiple `Oif` NLAs");
1229 interface_id = Some(interface)
1230 }
1231 RouteAttribute::Gateway(gateway) => {
1232 assert_eq!(next_hop, None, "existing route has multiple `Gateway` NLAs");
1233 next_hop = Some(gateway)
1234 }
1235 RouteAttribute::Table(t) => {
1236 assert_eq!(table, None, "existing route has multiple `Table` NLAs");
1237 table = Some(t)
1238 }
1239 nla => panic!("existing route has unexpected NLA: {:?}", nla),
1240 });
1241 if subnet.is_none() {
1242 assert_eq!(
1243 route.header.destination_prefix_length, 0,
1244 "existing route without `Destination` NLA must be a default route"
1245 );
1246 }
1247
1248 RouteNlaView {
1249 subnet,
1250 metric: metric.expect("existing routes must have a `Priority` NLA"),
1251 interface_id: interface_id.expect("existing routes must have an `Oif` NLA"),
1252 next_hop,
1253 }
1254}
1255
1256fn select_route_for_deletion<
1266 I: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
1267>(
1268 fidl_route_map: &FidlRouteMap<I>,
1269 route_tables: &RouteTableMap<I>,
1270 deletion_args: DelRouteArgs<I>,
1271) -> Option<NetlinkRouteMessage> {
1272 fidl_route_map
1274 .iter_messages(
1275 route_tables,
1276 match deletion_args {
1277 DelRouteArgs::Unicast(args) => args.table.into(),
1278 },
1279 )
1280 .filter_map(|route: NetlinkRouteMessage| {
1281 let NetlinkRouteMessage(existing_route) = &route;
1282 let UnicastDelRouteArgs { subnet, outbound_interface, next_hop, priority, table: _ } =
1283 match deletion_args {
1284 DelRouteArgs::Unicast(args) => args,
1285 };
1286 if subnet.prefix() != existing_route.header.destination_prefix_length {
1287 return None;
1288 }
1289 let RouteNlaView {
1290 subnet: existing_subnet,
1291 metric: existing_metric,
1292 interface_id: existing_interface,
1293 next_hop: existing_next_hop,
1294 } = view_existing_route_nlas(existing_route);
1295 let subnet_matches = existing_subnet.map_or_else(
1296 || !subnet.network().is_specified(),
1297 |dst| {
1298 crate::netlink_packet::ip_addr_from_route::<I>(&dst)
1299 .is_ok_and(|dst: I::Addr| dst == subnet.network())
1300 },
1301 );
1302 let metric_matches = priority.map_or(true, |p| p.get() == *existing_metric);
1303 let interface_matches =
1304 outbound_interface.map_or(true, |i| i.get() == (*existing_interface) as u64);
1305 let next_hop_matches = next_hop.map_or(true, |n| {
1306 existing_next_hop.map_or(false, |e| {
1307 crate::netlink_packet::ip_addr_from_route::<I>(&e)
1308 .is_ok_and(|e: I::Addr| e == n.get())
1309 })
1310 });
1311
1312 let existing_metric = *existing_metric;
1313
1314 if subnet_matches && metric_matches && interface_matches && next_hop_matches {
1315 Some((route, existing_metric))
1316 } else {
1317 None
1318 }
1319 })
1320 .min_by(|(_route1, metric1), (_route2, metric2)| metric1.cmp(metric2))
1322 .map(|(route, _metric)| route)
1323}
1324
1325#[cfg(test)]
1326mod tests {
1327 use super::*;
1328
1329 use std::collections::{HashMap, VecDeque};
1330 use std::convert::Infallible as Never;
1331 use std::pin::pin;
1332 use std::sync::atomic::{AtomicU32, Ordering};
1333
1334 use fidl::endpoints::{ControlHandle, RequestStream, ServerEnd};
1335 use fidl_fuchsia_net_routes_ext::Responder as _;
1336 use fidl_fuchsia_net_routes_ext::admin::{RouteSetRequest, RouteTableRequest};
1337 use {
1338 fidl_fuchsia_net_interfaces_admin as fnet_interfaces_admin,
1339 fidl_fuchsia_net_routes as fnet_routes, fidl_fuchsia_net_routes_admin as fnet_routes_admin,
1340 };
1341
1342 use assert_matches::assert_matches;
1343 use fuchsia_async as fasync;
1344 use futures::channel::mpsc;
1345 use futures::future::{Future, FutureExt as _};
1346 use futures::stream::TryStreamExt as _;
1347 use futures::{SinkExt as _, Stream};
1348 use ip_test_macro::ip_test;
1349 use linux_uapi::rtnetlink_groups_RTNLGRP_LINK;
1350 use net_declare::{net_ip_v4, net_ip_v6, net_subnet_v4, net_subnet_v6};
1351 use net_types::SpecifiedAddr;
1352 use net_types::ip::{GenericOverIp, IpInvariant, IpVersion, Ipv4, Ipv4Addr, Ipv6, Ipv6Addr};
1353 use netlink_packet_core::NetlinkPayload;
1354 use test_case::test_case;
1355
1356 use crate::client::AsyncWorkItem;
1357 use crate::interfaces::testutil::FakeInterfacesHandler;
1358 use crate::messaging::testutil::{FakeSender, SentMessage};
1359 use crate::route_eventloop::{EventLoopComponent, Optional, Required};
1360
1361 const V4_SUB1: Subnet<Ipv4Addr> = net_subnet_v4!("192.0.2.0/32");
1362 const V4_SUB2: Subnet<Ipv4Addr> = net_subnet_v4!("192.0.2.1/32");
1363 const V4_SUB3: Subnet<Ipv4Addr> = net_subnet_v4!("192.0.2.0/24");
1364 const V4_DFLT: Subnet<Ipv4Addr> = net_subnet_v4!("0.0.0.0/0");
1365 const V4_NEXTHOP1: Ipv4Addr = net_ip_v4!("192.0.2.1");
1366 const V4_NEXTHOP2: Ipv4Addr = net_ip_v4!("192.0.2.2");
1367
1368 const V6_SUB1: Subnet<Ipv6Addr> = net_subnet_v6!("2001:db8::/128");
1369 const V6_SUB2: Subnet<Ipv6Addr> = net_subnet_v6!("2001:db8::1/128");
1370 const V6_SUB3: Subnet<Ipv6Addr> = net_subnet_v6!("2001:db8::/64");
1371 const V6_DFLT: Subnet<Ipv6Addr> = net_subnet_v6!("::/0");
1372 const V6_NEXTHOP1: Ipv6Addr = net_ip_v6!("2001:db8::1");
1373 const V6_NEXTHOP2: Ipv6Addr = net_ip_v6!("2001:db8::2");
1374
1375 const DEV1: u32 = 1;
1376 const DEV2: u32 = 2;
1377
1378 const METRIC1: u32 = 1;
1379 const METRIC2: u32 = 100;
1380 const METRIC3: u32 = 9999;
1381 const TEST_SEQUENCE_NUMBER: u32 = 1234;
1382 const MANAGED_ROUTE_TABLE_ID: u32 = 5678;
1383 const MANAGED_ROUTE_TABLE_INDEX: NetlinkRouteTableIndex =
1384 NetlinkRouteTableIndex::new(MANAGED_ROUTE_TABLE_ID);
1385 const MAIN_FIDL_TABLE_ID: fnet_routes_ext::TableId = fnet_routes_ext::TableId::new(0);
1386 const OTHER_FIDL_TABLE_ID: fnet_routes_ext::TableId = fnet_routes_ext::TableId::new(1);
1387
1388 fn create_installed_route<I: Ip>(
1389 subnet: Subnet<I::Addr>,
1390 next_hop: Option<I::Addr>,
1391 interface_id: u64,
1392 metric: u32,
1393 table_id: fnet_routes_ext::TableId,
1394 ) -> fnet_routes_ext::InstalledRoute<I> {
1395 fnet_routes_ext::InstalledRoute::<I> {
1396 route: fnet_routes_ext::Route {
1397 destination: subnet,
1398 action: fnet_routes_ext::RouteAction::Forward(fnet_routes_ext::RouteTarget::<I> {
1399 outbound_interface: interface_id,
1400 next_hop: next_hop.map(|next_hop| SpecifiedAddr::new(next_hop)).flatten(),
1401 }),
1402 properties: fnet_routes_ext::RouteProperties {
1403 specified_properties: fnet_routes_ext::SpecifiedRouteProperties {
1404 metric: fnet_routes::SpecifiedMetric::ExplicitMetric(metric),
1405 },
1406 },
1407 },
1408 effective_properties: fnet_routes_ext::EffectiveRouteProperties { metric },
1409 table_id,
1410 }
1411 }
1412
1413 fn create_netlink_route_message<I: Ip>(
1414 destination_prefix_length: u8,
1415 table: NetlinkRouteTableIndex,
1416 nlas: Vec<RouteAttribute>,
1417 ) -> NetlinkRouteMessage {
1418 let mut route_header = RouteHeader::default();
1419 let address_family = match I::VERSION {
1420 IpVersion::V4 => AddressFamily::Inet,
1421 IpVersion::V6 => AddressFamily::Inet6,
1422 }
1423 .try_into()
1424 .expect("should fit into u8");
1425 route_header.address_family = address_family;
1426 route_header.destination_prefix_length = destination_prefix_length;
1427 route_header.kind = RouteType::Unicast;
1428 route_header.protocol = RouteProtocol::Kernel;
1429
1430 let (table_u8, _) = get_table_u8_and_nla_from_key(table);
1431 route_header.table = table_u8;
1432
1433 let mut route_message = RouteMessage::default();
1434 route_message.header = route_header;
1435 route_message.attributes = nlas;
1436
1437 NetlinkRouteMessage(route_message)
1438 }
1439
1440 fn create_nlas<I: Ip>(
1441 destination: Option<Subnet<I::Addr>>,
1442 next_hop: Option<I::Addr>,
1443 outgoing_interface_id: u32,
1444 metric: u32,
1445 table: Option<u32>,
1446 ) -> Vec<RouteAttribute> {
1447 let mut nlas = vec![];
1448
1449 let family = match I::VERSION {
1450 IpVersion::V4 => AddressFamily::Inet,
1451 IpVersion::V6 => AddressFamily::Inet6,
1452 };
1453
1454 if let Some(destination) = destination {
1455 let destination_nla = RouteAttribute::Destination(
1456 RouteAddress::parse(family, destination.network().bytes()).unwrap(),
1457 );
1458 nlas.push(destination_nla);
1459 }
1460
1461 let oif_nla = RouteAttribute::Oif(outgoing_interface_id);
1462 nlas.push(oif_nla);
1463
1464 if let Some(next_hop) = next_hop {
1465 let bytes = RouteAddress::parse(family, next_hop.bytes()).unwrap();
1466 let gateway_nla = RouteAttribute::Gateway(bytes);
1467 nlas.push(gateway_nla);
1468 }
1469
1470 let priority_nla = RouteAttribute::Priority(metric);
1471 nlas.push(priority_nla);
1472
1473 if let Some(t) = table {
1474 let table_nla = RouteAttribute::Table(t);
1475 nlas.push(table_nla);
1476 }
1477 nlas
1478 }
1479
1480 #[ip_test(I)]
1481 #[test_case(MAIN_ROUTE_TABLE_INDEX, MAIN_FIDL_TABLE_ID)]
1482 #[test_case(MANAGED_ROUTE_TABLE_INDEX, OTHER_FIDL_TABLE_ID)]
1483 #[fuchsia::test]
1484 async fn handles_route_watcher_event<
1485 I: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
1486 >(
1487 netlink_id: NetlinkRouteTableIndex,
1488 fidl_id: fnet_routes_ext::TableId,
1489 ) {
1490 let scope = fasync::Scope::new();
1491 let (subnet, next_hop) =
1492 I::map_ip((), |()| (V4_SUB1, V4_NEXTHOP1), |()| (V6_SUB1, V6_NEXTHOP1));
1493 let installed_route1: fnet_routes_ext::InstalledRoute<I> =
1494 create_installed_route(subnet, Some(next_hop), DEV1.into(), METRIC1, fidl_id);
1495 let installed_route2: fnet_routes_ext::InstalledRoute<I> =
1496 create_installed_route(subnet, Some(next_hop), DEV2.into(), METRIC2, fidl_id);
1497
1498 let add_event1 = fnet_routes_ext::Event::Added(installed_route1);
1499 let add_event2 = fnet_routes_ext::Event::Added(installed_route2);
1500 let remove_event = fnet_routes_ext::Event::Removed(installed_route1);
1501
1502 let expected_route_message1: NetlinkRouteMessage =
1503 NetlinkRouteMessage::try_from_installed_route(installed_route1, netlink_id).unwrap();
1504 let expected_route_message2: NetlinkRouteMessage =
1505 NetlinkRouteMessage::try_from_installed_route(installed_route2, netlink_id).unwrap();
1506
1507 let (right_group, wrong_group) = match I::VERSION {
1509 IpVersion::V4 => (
1510 ModernGroup(rtnetlink_groups_RTNLGRP_IPV4_ROUTE),
1511 ModernGroup(rtnetlink_groups_RTNLGRP_IPV6_ROUTE),
1512 ),
1513 IpVersion::V6 => (
1514 ModernGroup(rtnetlink_groups_RTNLGRP_IPV6_ROUTE),
1515 ModernGroup(rtnetlink_groups_RTNLGRP_IPV4_ROUTE),
1516 ),
1517 };
1518
1519 let (mut right_sink, right_client, async_work_drain_task) =
1520 crate::client::testutil::new_fake_client::<NetlinkRoute>(
1521 crate::client::testutil::CLIENT_ID_1,
1522 [right_group],
1523 );
1524 let _join_handle = scope.spawn(async_work_drain_task);
1525 let (mut wrong_sink, wrong_client, async_work_drain_task) =
1526 crate::client::testutil::new_fake_client::<NetlinkRoute>(
1527 crate::client::testutil::CLIENT_ID_2,
1528 [wrong_group],
1529 );
1530 let _join_handle = scope.spawn(async_work_drain_task);
1531 let route_clients: ClientTable<NetlinkRoute, FakeSender<_>> = ClientTable::default();
1532 route_clients.add_client(right_client);
1533 route_clients.add_client(wrong_client);
1534
1535 let (route_set_proxy, _route_set_server_end) =
1536 fidl::endpoints::create_proxy::<I::RouteSetMarker>();
1537 let (route_table_proxy, _route_table_server_end) =
1538 fidl::endpoints::create_proxy::<I::RouteTableMarker>();
1539 let (unmanaged_route_set_proxy, _server_end) =
1540 fidl::endpoints::create_proxy::<I::RouteSetMarker>();
1541 let (route_table_provider, _server_end) =
1542 fidl::endpoints::create_proxy::<I::RouteTableProviderMarker>();
1543
1544 let mut route_table = RouteTableMap::new(
1545 route_table_proxy.clone(),
1546 MAIN_FIDL_TABLE_ID,
1547 unmanaged_route_set_proxy,
1548 route_table_provider,
1549 );
1550 let mut fidl_route_map = FidlRouteMap::<I>::default();
1551
1552 match netlink_id {
1553 MAIN_ROUTE_TABLE_INDEX => {}
1554 MANAGED_ROUTE_TABLE_INDEX => {
1555 route_table.insert(
1556 netlink_id,
1557 RouteTable::Managed(ManagedRouteTable {
1558 route_table_proxy,
1559 route_set_proxy,
1560 fidl_table_id: OTHER_FIDL_TABLE_ID,
1561 rule_set_authenticated: false,
1562 }),
1563 );
1564 }
1565 _ => panic!("unexpected netlink id: {netlink_id:?}"),
1566 }
1567
1568 assert_eq!(fidl_route_map.iter_messages(&route_table, netlink_id).count(), 0);
1569 assert_eq!(&right_sink.take_messages()[..], &[]);
1570 assert_eq!(&wrong_sink.take_messages()[..], &[]);
1571
1572 assert_eq!(
1573 handle_route_watcher_event(
1574 &mut route_table,
1575 &mut fidl_route_map,
1576 &route_clients,
1577 add_event1,
1578 ),
1579 None
1580 );
1581 assert_eq!(
1582 fidl_route_map.iter_messages(&route_table, netlink_id).collect::<HashSet<_>>(),
1583 HashSet::from_iter([expected_route_message1.clone()])
1584 );
1585 assert_eq!(
1586 &right_sink.take_messages()[..],
1587 &[SentMessage::multicast(
1588 expected_route_message1
1589 .clone()
1590 .into_rtnl_new_route(UNSPECIFIED_SEQUENCE_NUMBER, false),
1591 right_group
1592 )]
1593 );
1594 assert_eq!(&wrong_sink.take_messages()[..], &[]);
1595
1596 assert_eq!(
1597 fidl_route_map.iter_messages(&route_table, netlink_id).collect::<HashSet<_>>(),
1598 HashSet::from_iter([expected_route_message1.clone()])
1599 );
1600 assert_eq!(&right_sink.take_messages()[..], &[]);
1601 assert_eq!(&wrong_sink.take_messages()[..], &[]);
1602
1603 assert_eq!(
1605 handle_route_watcher_event(
1606 &mut route_table,
1607 &mut fidl_route_map,
1608 &route_clients,
1609 add_event2,
1610 ),
1611 None
1612 );
1613 assert_eq!(
1614 fidl_route_map.iter_messages(&route_table, netlink_id).collect::<HashSet<_>>(),
1615 HashSet::from_iter([expected_route_message1.clone(), expected_route_message2.clone()])
1616 );
1617 assert_eq!(
1618 &right_sink.take_messages()[..],
1619 &[SentMessage::multicast(
1620 expected_route_message2
1621 .clone()
1622 .into_rtnl_new_route(UNSPECIFIED_SEQUENCE_NUMBER, false),
1623 right_group
1624 )]
1625 );
1626 assert_eq!(&wrong_sink.take_messages()[..], &[]);
1627
1628 assert_eq!(
1629 handle_route_watcher_event(
1630 &mut route_table,
1631 &mut fidl_route_map,
1632 &route_clients,
1633 remove_event,
1634 ),
1635 None
1636 );
1637 assert_eq!(
1638 fidl_route_map.iter_messages(&route_table, netlink_id).collect::<HashSet<_>>(),
1639 HashSet::from_iter([expected_route_message2.clone()])
1640 );
1641 assert_eq!(
1642 &right_sink.take_messages()[..],
1643 &[SentMessage::multicast(
1644 expected_route_message1.clone().into_rtnl_del_route(),
1645 right_group
1646 )]
1647 );
1648 assert_eq!(&wrong_sink.take_messages()[..], &[]);
1649
1650 assert_eq!(
1651 fidl_route_map.iter_messages(&route_table, netlink_id).collect::<HashSet<_>>(),
1652 HashSet::from_iter([expected_route_message2.clone()])
1653 );
1654 assert_eq!(&right_sink.take_messages()[..], &[]);
1655 assert_eq!(&wrong_sink.take_messages()[..], &[]);
1656 drop(route_clients);
1657 scope.join().await;
1658 }
1659
1660 #[ip_test(I)]
1662 #[fuchsia::test]
1663 async fn handles_route_watcher_event_unmanaged_route_table<
1664 I: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
1665 >() {
1666 let _scope = fasync::Scope::new();
1667 let (subnet, next_hop) =
1668 I::map_ip((), |()| (V4_SUB1, V4_NEXTHOP1), |()| (V6_SUB1, V6_NEXTHOP1));
1669 let installed_route: fnet_routes_ext::InstalledRoute<I> = create_installed_route(
1670 subnet,
1671 Some(next_hop),
1672 DEV1.into(),
1673 METRIC1,
1674 OTHER_FIDL_TABLE_ID,
1675 );
1676 let add_event = fnet_routes_ext::Event::Added(installed_route);
1677 let remove_event = fnet_routes_ext::Event::Removed(installed_route);
1678
1679 let route_clients: ClientTable<NetlinkRoute, FakeSender<_>> = ClientTable::default();
1680 let (route_table_proxy, _route_table_server_end) =
1681 fidl::endpoints::create_proxy::<I::RouteTableMarker>();
1682 let (unmanaged_route_set_proxy, _server_end) =
1683 fidl::endpoints::create_proxy::<I::RouteSetMarker>();
1684 let (route_table_provider, _server_end) =
1685 fidl::endpoints::create_proxy::<I::RouteTableProviderMarker>();
1686
1687 let mut route_table = RouteTableMap::new(
1688 route_table_proxy.clone(),
1689 MAIN_FIDL_TABLE_ID,
1690 unmanaged_route_set_proxy,
1691 route_table_provider,
1692 );
1693 let mut fidl_route_map = FidlRouteMap::<I>::default();
1694
1695 assert_eq!(
1697 handle_route_watcher_event(
1698 &mut route_table,
1699 &mut fidl_route_map,
1700 &route_clients,
1701 add_event,
1702 ),
1703 None
1704 );
1705
1706 assert_eq!(
1708 handle_route_watcher_event(
1709 &mut route_table,
1710 &mut fidl_route_map,
1711 &route_clients,
1712 remove_event,
1713 ),
1714 None
1715 );
1716 }
1717
1718 #[ip_test(I)]
1719 #[fuchsia::test]
1720 #[should_panic(expected = "Netstack reported an unexpected route event")]
1721 async fn handles_unknown_route_watcher_event<
1722 I: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
1723 >() {
1724 let (route_table_proxy, _route_table_server_end) =
1725 fidl::endpoints::create_proxy::<I::RouteTableMarker>();
1726 let (unmanaged_route_set_proxy, _server_end) =
1727 fidl::endpoints::create_proxy::<I::RouteSetMarker>();
1728 let (route_table_provider, _server_end) =
1729 fidl::endpoints::create_proxy::<I::RouteTableProviderMarker>();
1730 let mut route_table = RouteTableMap::new(
1731 route_table_proxy.clone(),
1732 MAIN_FIDL_TABLE_ID,
1733 unmanaged_route_set_proxy,
1734 route_table_provider,
1735 );
1736 let mut fidl_route_map = FidlRouteMap::<I>::default();
1737 let route_clients: ClientTable<NetlinkRoute, FakeSender<_>> = ClientTable::default();
1738
1739 let _ = handle_route_watcher_event(
1741 &mut route_table,
1742 &mut fidl_route_map,
1743 &route_clients,
1744 fnet_routes_ext::Event::Unknown,
1745 );
1746 }
1747
1748 #[ip_test(I)]
1749 #[fuchsia::test]
1750 #[should_panic(expected = "Netstack reported the addition of an existing route")]
1751 async fn handles_duplicate_route_watcher_event<
1752 I: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
1753 >() {
1754 let (subnet, next_hop) =
1755 I::map_ip((), |()| (V4_SUB1, V4_NEXTHOP1), |()| (V6_SUB1, V6_NEXTHOP1));
1756 let table_id = MAIN_FIDL_TABLE_ID;
1757 let installed_route: fnet_routes_ext::InstalledRoute<I> =
1758 create_installed_route(subnet, Some(next_hop), DEV1.into(), METRIC1, table_id);
1759
1760 let (route_table_proxy, _route_table_server_end) =
1761 fidl::endpoints::create_proxy::<I::RouteTableMarker>();
1762 let (unmanaged_route_set_proxy, _server_end) =
1763 fidl::endpoints::create_proxy::<I::RouteSetMarker>();
1764 let (route_table_provider, _server_end) =
1765 fidl::endpoints::create_proxy::<I::RouteTableProviderMarker>();
1766 let mut route_table = RouteTableMap::new(
1767 route_table_proxy.clone(),
1768 MAIN_FIDL_TABLE_ID,
1769 unmanaged_route_set_proxy,
1770 route_table_provider,
1771 );
1772 let mut fidl_route_map = FidlRouteMap::<I>::default();
1773 let route_clients: ClientTable<NetlinkRoute, FakeSender<_>> = ClientTable::default();
1774
1775 for _ in 0..2 {
1777 assert_eq!(
1778 handle_route_watcher_event(
1779 &mut route_table,
1780 &mut fidl_route_map,
1781 &route_clients,
1782 fnet_routes_ext::Event::Added(installed_route),
1783 ),
1784 None
1785 );
1786 }
1787 }
1788
1789 #[ip_test(I)]
1790 #[fuchsia::test]
1791 #[should_panic(expected = "Netstack reported the removal of an unknown route")]
1792 async fn handles_remove_nonexisting_route_watcher_event<
1793 I: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
1794 >() {
1795 let (subnet, next_hop) =
1796 I::map_ip((), |()| (V4_SUB1, V4_NEXTHOP1), |()| (V6_SUB1, V6_NEXTHOP1));
1797 let table_id = MAIN_FIDL_TABLE_ID;
1798 let installed_route: fnet_routes_ext::InstalledRoute<I> =
1799 create_installed_route(subnet, Some(next_hop), DEV1.into(), METRIC1, table_id);
1800
1801 let (route_table_proxy, _route_table_server_end) =
1802 fidl::endpoints::create_proxy::<I::RouteTableMarker>();
1803 let (unmanaged_route_set_proxy, _server_end) =
1804 fidl::endpoints::create_proxy::<I::RouteSetMarker>();
1805 let (route_table_provider, _server_end) =
1806 fidl::endpoints::create_proxy::<I::RouteTableProviderMarker>();
1807 let mut route_table = RouteTableMap::new(
1808 route_table_proxy.clone(),
1809 MAIN_FIDL_TABLE_ID,
1810 unmanaged_route_set_proxy,
1811 route_table_provider,
1812 );
1813 let mut fidl_route_map = FidlRouteMap::<I>::default();
1814 let route_clients: ClientTable<NetlinkRoute, FakeSender<_>> = ClientTable::default();
1815
1816 let _ = handle_route_watcher_event(
1818 &mut route_table,
1819 &mut fidl_route_map,
1820 &route_clients,
1821 fnet_routes_ext::Event::Removed(installed_route),
1822 );
1823 }
1824
1825 #[ip_test(I, test = false)]
1826 #[fuchsia::test]
1827 async fn handle_route_watcher_event_two_routesets<
1828 I: Ip + fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
1829 >() {
1830 let scope = fasync::Scope::new();
1831 let (subnet, next_hop) =
1832 I::map_ip((), |()| (V4_SUB1, V4_NEXTHOP1), |()| (V6_SUB1, V6_NEXTHOP1));
1833
1834 let installed_route1: fnet_routes_ext::InstalledRoute<I> = create_installed_route(
1835 subnet,
1836 Some(next_hop),
1837 DEV1.into(),
1838 METRIC1,
1839 OTHER_FIDL_TABLE_ID,
1840 );
1841 let installed_route2: fnet_routes_ext::InstalledRoute<I> = create_installed_route(
1842 subnet,
1843 Some(next_hop),
1844 DEV2.into(),
1845 METRIC2,
1846 MAIN_FIDL_TABLE_ID,
1847 );
1848
1849 let add_events1 = [
1850 fnet_routes_ext::Event::Added(fnet_routes_ext::InstalledRoute {
1851 table_id: MAIN_FIDL_TABLE_ID,
1852 ..installed_route1
1853 }),
1854 fnet_routes_ext::Event::Added(installed_route1),
1855 ];
1856 let add_event2 = fnet_routes_ext::Event::Added(installed_route2);
1857 let remove_event = fnet_routes_ext::Event::Removed(installed_route1);
1858
1859 let expected_route_message1_unmanaged =
1862 NetlinkRouteMessage::try_from_installed_route(installed_route1, MAIN_ROUTE_TABLE_INDEX)
1863 .unwrap();
1864 let expected_route_message1_managed = NetlinkRouteMessage::try_from_installed_route(
1865 installed_route1,
1866 MANAGED_ROUTE_TABLE_INDEX,
1867 )
1868 .unwrap();
1869 let expected_route_message2: NetlinkRouteMessage =
1870 NetlinkRouteMessage::try_from_installed_route(installed_route2, MAIN_ROUTE_TABLE_INDEX)
1871 .unwrap();
1872
1873 let (right_group, wrong_group) = match I::VERSION {
1875 IpVersion::V4 => (
1876 ModernGroup(rtnetlink_groups_RTNLGRP_IPV4_ROUTE),
1877 ModernGroup(rtnetlink_groups_RTNLGRP_IPV6_ROUTE),
1878 ),
1879 IpVersion::V6 => (
1880 ModernGroup(rtnetlink_groups_RTNLGRP_IPV6_ROUTE),
1881 ModernGroup(rtnetlink_groups_RTNLGRP_IPV4_ROUTE),
1882 ),
1883 };
1884 let (mut right_sink, right_client, async_work_drain_task) =
1885 crate::client::testutil::new_fake_client::<NetlinkRoute>(
1886 crate::client::testutil::CLIENT_ID_1,
1887 [right_group],
1888 );
1889 let _join_handle = scope.spawn(async_work_drain_task);
1890 let (mut wrong_sink, wrong_client, async_work_drain_task) =
1891 crate::client::testutil::new_fake_client::<NetlinkRoute>(
1892 crate::client::testutil::CLIENT_ID_2,
1893 [wrong_group],
1894 );
1895 let _join_handle = scope.spawn(async_work_drain_task);
1896 let route_clients: ClientTable<NetlinkRoute, FakeSender<_>> = ClientTable::default();
1897 route_clients.add_client(right_client);
1898 route_clients.add_client(wrong_client);
1899
1900 let (main_route_table_proxy, _route_table_server_end) =
1901 fidl::endpoints::create_proxy::<I::RouteTableMarker>();
1902 let (unmanaged_route_set_proxy, _unmanaged_route_set_server_end) =
1903 fidl::endpoints::create_proxy::<I::RouteSetMarker>();
1904 let (route_table_proxy, _route_table_server_end) =
1905 fidl::endpoints::create_proxy::<I::RouteTableMarker>();
1906 let (route_set_proxy, _server_end) = fidl::endpoints::create_proxy::<I::RouteSetMarker>();
1907 let (route_table_provider, _server_end) =
1908 fidl::endpoints::create_proxy::<I::RouteTableProviderMarker>();
1909
1910 let mut route_table = RouteTableMap::new(
1911 main_route_table_proxy,
1912 MAIN_FIDL_TABLE_ID,
1913 unmanaged_route_set_proxy,
1914 route_table_provider,
1915 );
1916 route_table.insert(
1917 MANAGED_ROUTE_TABLE_INDEX,
1918 RouteTable::Managed(ManagedRouteTable {
1919 route_set_proxy,
1920 route_table_proxy,
1921 fidl_table_id: OTHER_FIDL_TABLE_ID,
1922 rule_set_authenticated: false,
1923 }),
1924 );
1925
1926 let mut fidl_route_map = FidlRouteMap::<I>::default();
1927
1928 assert_eq!(
1931 handle_route_watcher_event(
1932 &mut route_table,
1933 &mut fidl_route_map,
1934 &route_clients,
1935 add_events1[0],
1936 ),
1937 None
1938 );
1939
1940 assert_eq!(
1942 &fidl_route_map
1943 .iter_messages(&route_table, MANAGED_ROUTE_TABLE_INDEX)
1944 .collect::<HashSet<_>>(),
1945 &HashSet::new()
1946 );
1947
1948 assert_eq!(
1951 handle_route_watcher_event(
1952 &mut route_table,
1953 &mut fidl_route_map,
1954 &route_clients,
1955 add_events1[1],
1956 ),
1957 None
1958 );
1959
1960 assert_eq!(
1962 &fidl_route_map
1963 .iter_messages(&route_table, MANAGED_ROUTE_TABLE_INDEX)
1964 .chain(fidl_route_map.iter_messages(&route_table, MAIN_ROUTE_TABLE_INDEX))
1965 .collect::<HashSet<_>>(),
1966 &HashSet::from_iter([
1967 expected_route_message1_unmanaged.clone(),
1968 expected_route_message1_managed.clone()
1969 ])
1970 );
1971 assert_eq!(
1972 &right_sink.take_messages()[..],
1973 &[expected_route_message1_unmanaged.clone(), expected_route_message1_managed.clone()]
1974 .map(|message| SentMessage::multicast(
1975 message.clone().into_rtnl_new_route(UNSPECIFIED_SEQUENCE_NUMBER, false),
1976 right_group
1977 ))
1978 );
1979 assert_eq!(&wrong_sink.take_messages()[..], &[]);
1980
1981 assert_eq!(
1984 handle_route_watcher_event(
1985 &mut route_table,
1986 &mut fidl_route_map,
1987 &route_clients,
1988 add_event2,
1989 ),
1990 None
1991 );
1992
1993 assert_eq!(
1995 &fidl_route_map
1996 .iter_messages(&route_table, MANAGED_ROUTE_TABLE_INDEX)
1997 .chain(fidl_route_map.iter_messages(&route_table, MAIN_ROUTE_TABLE_INDEX))
1998 .collect::<HashSet<_>>(),
1999 &HashSet::from_iter([
2000 expected_route_message1_unmanaged.clone(),
2001 expected_route_message1_managed.clone(),
2002 expected_route_message2.clone()
2003 ])
2004 );
2005
2006 assert_eq!(
2008 &right_sink.take_messages()[..],
2009 &[SentMessage::multicast(
2010 expected_route_message2
2011 .clone()
2012 .into_rtnl_new_route(UNSPECIFIED_SEQUENCE_NUMBER, false),
2013 right_group
2014 )]
2015 );
2016 assert_eq!(&wrong_sink.take_messages()[..], &[]);
2017
2018 assert_eq!(
2020 handle_route_watcher_event(
2021 &mut route_table,
2022 &mut fidl_route_map,
2023 &route_clients,
2024 remove_event,
2025 ),
2026 Some(TableNeedsCleanup(OTHER_FIDL_TABLE_ID, MANAGED_ROUTE_TABLE_INDEX))
2027 );
2028 assert_eq!(
2029 &fidl_route_map
2030 .iter_messages(&route_table, MAIN_ROUTE_TABLE_INDEX)
2031 .collect::<HashSet<_>>(),
2032 &HashSet::from_iter([
2033 expected_route_message1_unmanaged.clone(),
2034 expected_route_message2.clone()
2035 ])
2036 );
2037 assert_eq!(
2038 fidl_route_map
2039 .iter_messages(&route_table, MANAGED_ROUTE_TABLE_INDEX)
2040 .collect::<HashSet<_>>(),
2041 HashSet::new()
2042 );
2043 assert_eq!(
2044 &right_sink.take_messages()[..],
2045 &[SentMessage::multicast(
2046 expected_route_message1_managed.clone().into_rtnl_del_route(),
2047 right_group
2048 )]
2049 );
2050 assert_eq!(&wrong_sink.take_messages()[..], &[]);
2051 drop(route_clients);
2052 scope.join().await;
2053 }
2054
2055 #[test_case(V4_SUB1, V4_NEXTHOP1)]
2056 #[test_case(V6_SUB1, V6_NEXTHOP1)]
2057 #[test_case(net_subnet_v4!("0.0.0.0/0"), net_ip_v4!("0.0.0.1"))]
2058 #[test_case(net_subnet_v6!("::/0"), net_ip_v6!("::1"))]
2059 fn test_netlink_route_message_try_from_installed_route<A: IpAddress>(
2060 subnet: Subnet<A>,
2061 next_hop: A,
2062 ) {
2063 netlink_route_message_conversion_helper::<A::Version>(subnet, next_hop);
2064 }
2065
2066 fn netlink_route_message_conversion_helper<I: Ip>(subnet: Subnet<I::Addr>, next_hop: I::Addr) {
2067 let installed_route = create_installed_route::<I>(
2068 subnet,
2069 Some(next_hop),
2070 DEV1.into(),
2071 METRIC1,
2072 MAIN_FIDL_TABLE_ID,
2073 );
2074 let prefix_length = subnet.prefix();
2075 let subnet = if prefix_length > 0 { Some(subnet) } else { None };
2076 let nlas = create_nlas::<I>(subnet, Some(next_hop), DEV1, METRIC1, None);
2077 let expected =
2078 create_netlink_route_message::<I>(prefix_length, MAIN_ROUTE_TABLE_INDEX, nlas);
2079
2080 let actual =
2081 NetlinkRouteMessage::try_from_installed_route(installed_route, MAIN_ROUTE_TABLE_INDEX)
2082 .unwrap();
2083 assert_eq!(actual, expected);
2084 }
2085
2086 #[test_case(V4_SUB1)]
2087 #[test_case(V6_SUB1)]
2088 fn test_non_forward_route_conversion<A: IpAddress>(subnet: Subnet<A>) {
2089 let installed_route = fnet_routes_ext::InstalledRoute::<A::Version> {
2090 route: fnet_routes_ext::Route {
2091 destination: subnet,
2092 action: fnet_routes_ext::RouteAction::Unknown,
2093 properties: fnet_routes_ext::RouteProperties {
2094 specified_properties: fnet_routes_ext::SpecifiedRouteProperties {
2095 metric: fnet_routes::SpecifiedMetric::ExplicitMetric(METRIC1),
2096 },
2097 },
2098 },
2099 effective_properties: fnet_routes_ext::EffectiveRouteProperties { metric: METRIC1 },
2100 table_id: MAIN_FIDL_TABLE_ID,
2102 };
2103
2104 let actual: Result<NetlinkRouteMessage, NetlinkRouteMessageConversionError> =
2105 NetlinkRouteMessage::try_from_installed_route(installed_route, MAIN_ROUTE_TABLE_INDEX);
2106 assert_eq!(actual, Err(NetlinkRouteMessageConversionError::RouteActionNotForwarding));
2107 }
2108
2109 #[fuchsia::test]
2110 fn test_oversized_interface_id_route_conversion() {
2111 let invalid_interface_id = (u32::MAX as u64) + 1;
2112 let installed_route: fnet_routes_ext::InstalledRoute<Ipv4> = create_installed_route(
2113 V4_SUB1,
2114 Some(V4_NEXTHOP1),
2115 invalid_interface_id,
2116 Default::default(),
2117 MAIN_FIDL_TABLE_ID,
2118 );
2119
2120 let actual: Result<NetlinkRouteMessage, NetlinkRouteMessageConversionError> =
2121 NetlinkRouteMessage::try_from_installed_route(installed_route, MAIN_ROUTE_TABLE_INDEX);
2122 assert_eq!(
2123 actual,
2124 Err(NetlinkRouteMessageConversionError::InvalidInterfaceId(invalid_interface_id))
2125 );
2126 }
2127
2128 #[test]
2129 fn test_into_rtnl_new_route_is_serializable() {
2130 let route = create_netlink_route_message::<Ipv4>(0, MAIN_ROUTE_TABLE_INDEX, vec![]);
2131 let new_route_message = route.into_rtnl_new_route(UNSPECIFIED_SEQUENCE_NUMBER, false);
2132 let mut buf = vec![0; new_route_message.buffer_len()];
2133 new_route_message.serialize(&mut buf);
2135 }
2136
2137 #[test]
2138 fn test_into_rtnl_del_route_is_serializable() {
2139 let route = create_netlink_route_message::<Ipv6>(0, MAIN_ROUTE_TABLE_INDEX, vec![]);
2140 let del_route_message = route.into_rtnl_del_route();
2141 let mut buf = vec![0; del_route_message.buffer_len()];
2142 del_route_message.serialize(&mut buf);
2144 }
2145
2146 enum OnlyRoutes {}
2147 impl crate::route_eventloop::EventLoopSpec for OnlyRoutes {
2148 type InterfacesProxy = Required;
2149 type InterfacesHandler = Required;
2150 type RouteClients = Required;
2151
2152 type V4RoutesState = Optional;
2155 type V6RoutesState = Optional;
2156 type V4RoutesSetProvider = Optional;
2157 type V6RoutesSetProvider = Optional;
2158 type V4RouteTableProvider = Optional;
2159 type V6RouteTableProvider = Optional;
2160 type InterfacesStateProxy = Optional;
2161
2162 type InterfacesWorker = Optional;
2163 type RoutesV4Worker = Optional;
2164 type RoutesV6Worker = Optional;
2165 type RuleV4Worker = Optional;
2166 type RuleV6Worker = Optional;
2167 type NduseroptWorker = Optional;
2168 type NeighborWorker = Optional;
2169 }
2170
2171 struct Setup<W, R> {
2172 pub event_loop_inputs: crate::route_eventloop::EventLoopInputs<
2173 FakeInterfacesHandler,
2174 FakeSender<RouteNetlinkMessage>,
2175 OnlyRoutes,
2176 >,
2177 pub watcher_stream: W,
2178 pub route_sets: R,
2179 pub interfaces_request_stream: fnet_root::InterfacesRequestStream,
2180 pub request_sink:
2181 mpsc::Sender<crate::route_eventloop::UnifiedRequest<FakeSender<RouteNetlinkMessage>>>,
2182 pub async_work_sink: mpsc::UnboundedSender<AsyncWorkItem<NetlinkRoute>>,
2183 }
2184
2185 fn setup_with_route_clients_yielding_admin_server_ends<
2186 I: Ip + fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
2187 >(
2188 route_clients: ClientTable<NetlinkRoute, FakeSender<RouteNetlinkMessage>>,
2189 ) -> Setup<
2190 impl Stream<Item = <<I::WatcherMarker as ProtocolMarker>::RequestStream as Stream>::Item>,
2191 (ServerEnd<I::RouteTableMarker>, ServerEnd<I::RouteTableProviderMarker>),
2192 > {
2193 let (interfaces_handler, _interfaces_handler_sink) = FakeInterfacesHandler::new();
2194 let (request_sink, request_stream) = mpsc::channel(1);
2195 let (interfaces_proxy, interfaces) =
2196 fidl::endpoints::create_proxy::<fnet_root::InterfacesMarker>();
2197 let (async_work_sink, async_work_receiver) = mpsc::unbounded();
2198
2199 #[derive(GenericOverIp)]
2200 #[generic_over_ip(I, Ip)]
2201 struct ServerEnds<
2202 I: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
2203 > {
2204 routes_state: ServerEnd<I::StateMarker>,
2205 routes_set_provider: ServerEnd<I::RouteTableMarker>,
2206 route_table_provider: ServerEnd<I::RouteTableProviderMarker>,
2207 }
2208
2209 let base_inputs = crate::route_eventloop::EventLoopInputs {
2210 interfaces_handler: EventLoopComponent::Present(interfaces_handler),
2211 route_clients: EventLoopComponent::Present(route_clients),
2212 interfaces_proxy: EventLoopComponent::Present(interfaces_proxy),
2213 async_work_receiver,
2214
2215 interfaces_state_proxy: EventLoopComponent::Absent(Optional),
2216 v4_routes_state: EventLoopComponent::Absent(Optional),
2217 v6_routes_state: EventLoopComponent::Absent(Optional),
2218 v4_main_route_table: EventLoopComponent::Absent(Optional),
2219 v6_main_route_table: EventLoopComponent::Absent(Optional),
2220 v4_route_table_provider: EventLoopComponent::Absent(Optional),
2221 v6_route_table_provider: EventLoopComponent::Absent(Optional),
2222 v4_rule_table: EventLoopComponent::Absent(Optional),
2223 v6_rule_table: EventLoopComponent::Absent(Optional),
2224 ndp_option_watcher_provider: EventLoopComponent::Absent(Optional),
2225 neighbors_view: EventLoopComponent::Absent(Optional),
2226 neighbors_controller: EventLoopComponent::Absent(Optional),
2227
2228 unified_request_stream: request_stream,
2229 };
2230
2231 let (IpInvariant(inputs), server_ends) = I::map_ip_out(
2232 base_inputs,
2233 |base_inputs| {
2234 let (v4_routes_state, routes_state) =
2235 fidl::endpoints::create_proxy::<fnet_routes::StateV4Marker>();
2236 let (v4_main_route_table, routes_set_provider) =
2237 fidl::endpoints::create_proxy::<fnet_routes_admin::RouteTableV4Marker>();
2238 let (v4_route_table_provider, route_table_provider) = fidl::endpoints::create_proxy::<
2239 fnet_routes_admin::RouteTableProviderV4Marker,
2240 >();
2241 let inputs = crate::route_eventloop::EventLoopInputs {
2242 v4_routes_state: EventLoopComponent::Present(v4_routes_state),
2243 v4_main_route_table: EventLoopComponent::Present(v4_main_route_table),
2244 v4_route_table_provider: EventLoopComponent::Present(v4_route_table_provider),
2245 ..base_inputs
2246 };
2247 let server_ends =
2248 ServerEnds::<Ipv4> { routes_state, routes_set_provider, route_table_provider };
2249 (IpInvariant(inputs), server_ends)
2250 },
2251 |base_inputs| {
2252 let (v6_routes_state, routes_state) =
2253 fidl::endpoints::create_proxy::<fnet_routes::StateV6Marker>();
2254 let (v6_main_route_table, routes_set_provider) =
2255 fidl::endpoints::create_proxy::<fnet_routes_admin::RouteTableV6Marker>();
2256 let (v6_route_table_provider, route_table_provider) = fidl::endpoints::create_proxy::<
2257 fnet_routes_admin::RouteTableProviderV6Marker,
2258 >();
2259 let inputs = crate::route_eventloop::EventLoopInputs {
2260 v6_routes_state: EventLoopComponent::Present(v6_routes_state),
2261 v6_main_route_table: EventLoopComponent::Present(v6_main_route_table),
2262 v6_route_table_provider: EventLoopComponent::Present(v6_route_table_provider),
2263 ..base_inputs
2264 };
2265 let server_ends =
2266 ServerEnds::<Ipv6> { routes_state, routes_set_provider, route_table_provider };
2267 (IpInvariant(inputs), server_ends)
2268 },
2269 );
2270
2271 let ServerEnds { routes_state, routes_set_provider, route_table_provider } = server_ends;
2272
2273 let state_stream = routes_state.into_stream().boxed_local();
2274
2275 let interfaces_request_stream = interfaces.into_stream();
2276
2277 #[derive(GenericOverIp)]
2278 #[generic_over_ip(I, Ip)]
2279 struct StateRequestWrapper<I: fnet_routes_ext::FidlRouteIpExt> {
2280 request: <<I::StateMarker as ProtocolMarker>::RequestStream as futures::Stream>::Item,
2281 }
2282
2283 #[derive(GenericOverIp)]
2284 #[generic_over_ip(I, Ip)]
2285 struct WatcherRequestWrapper<I: fnet_routes_ext::FidlRouteIpExt> {
2286 watcher: <I::WatcherMarker as ProtocolMarker>::RequestStream,
2287 }
2288
2289 let watcher_stream = state_stream
2290 .map(|request| {
2291 let wrapper = I::map_ip(
2292 StateRequestWrapper { request },
2293 |StateRequestWrapper { request }| match request.expect("watcher stream error") {
2294 fnet_routes::StateV4Request::GetWatcherV4 {
2295 options: _,
2296 watcher,
2297 control_handle: _,
2298 } => WatcherRequestWrapper { watcher: watcher.into_stream() },
2299 fnet_routes::StateV4Request::GetRuleWatcherV4 {
2300 options: _,
2301 watcher: _,
2302 control_handle: _,
2303 } => todo!("TODO(https://fxbug.dev/336204757): Implement rules watcher"),
2304 },
2305 |StateRequestWrapper { request }| match request.expect("watcher stream error") {
2306 fnet_routes::StateV6Request::GetWatcherV6 {
2307 options: _,
2308 watcher,
2309 control_handle: _,
2310 } => WatcherRequestWrapper { watcher: watcher.into_stream() },
2311 fnet_routes::StateV6Request::GetRuleWatcherV6 {
2312 options: _,
2313 watcher: _,
2314 control_handle: _,
2315 } => todo!("TODO(https://fxbug.dev/336204757): Implement rules watcher"),
2316 },
2317 );
2318 wrapper
2319 })
2320 .map(|WatcherRequestWrapper { watcher }| watcher)
2321 .flatten()
2324 .fuse();
2325
2326 Setup {
2327 event_loop_inputs: inputs,
2328 watcher_stream,
2329 route_sets: (routes_set_provider, route_table_provider),
2330 interfaces_request_stream,
2331 request_sink,
2332 async_work_sink,
2333 }
2334 }
2335
2336 fn setup_with_route_clients<
2337 I: Ip + fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
2338 >(
2339 route_clients: ClientTable<NetlinkRoute, FakeSender<RouteNetlinkMessage>>,
2340 ) -> Setup<
2341 impl Stream<Item = <<I::WatcherMarker as ProtocolMarker>::RequestStream as Stream>::Item>,
2342 impl Stream<
2343 Item = (
2344 fnet_routes_ext::TableId,
2345 <<I::RouteSetMarker as ProtocolMarker>::RequestStream as Stream>::Item,
2346 ),
2347 >,
2348 > {
2349 let Setup {
2350 event_loop_inputs,
2351 watcher_stream,
2352 route_sets: (routes_set_provider, route_table_provider),
2353 interfaces_request_stream,
2354 request_sink,
2355 async_work_sink,
2356 } = setup_with_route_clients_yielding_admin_server_ends::<I>(route_clients);
2357 let route_set_stream =
2358 fnet_routes_ext::testutil::admin::serve_all_route_sets_with_table_id::<I>(
2359 routes_set_provider,
2360 Some(MAIN_FIDL_TABLE_ID),
2361 )
2362 .map(|item| (MAIN_FIDL_TABLE_ID, item));
2363
2364 let route_table_provider_request_stream = route_table_provider.into_stream();
2365
2366 let table_id = AtomicU32::new(OTHER_FIDL_TABLE_ID.get());
2367
2368 let route_sets_from_route_table_provider =
2369 futures::TryStreamExt::map_ok(route_table_provider_request_stream, move |request| {
2370 match I::into_route_table_provider_request(request) {
2371 fnet_routes_ext::admin::RouteTableProviderRequest::NewRouteTable {
2372 provider,
2373 options: _,
2374 control_handle: _,
2375 } => {
2376 let table_id =
2377 fnet_routes_ext::TableId::new(table_id.fetch_add(1, Ordering::SeqCst));
2378 fnet_routes_ext::testutil::admin::serve_all_route_sets_with_table_id::<I>(
2379 provider,
2380 Some(table_id),
2381 )
2382 .map(move |route_set_request| (table_id, route_set_request))
2383 }
2384 r => panic!("unexpected request {r:?}"),
2385 }
2386 })
2387 .map(|result| result.expect("should not get FIDL error"))
2388 .flatten_unordered(None)
2389 .fuse();
2390 let route_set_stream = futures::stream::select_all([
2391 route_set_stream.left_stream(),
2392 route_sets_from_route_table_provider.right_stream(),
2393 ])
2394 .fuse();
2395
2396 Setup {
2397 event_loop_inputs,
2398 watcher_stream,
2399 route_sets: route_set_stream,
2400 interfaces_request_stream,
2401 request_sink,
2402 async_work_sink,
2403 }
2404 }
2405
2406 async fn respond_to_watcher<
2407 I: fnet_routes_ext::FidlRouteIpExt,
2408 S: Stream<Item = <<I::WatcherMarker as ProtocolMarker>::RequestStream as Stream>::Item>,
2409 >(
2410 stream: S,
2411 updates: impl IntoIterator<Item = I::WatchEvent>,
2412 ) {
2413 #[derive(GenericOverIp)]
2414 #[generic_over_ip(I, Ip)]
2415 struct HandleInputs<I: fnet_routes_ext::FidlRouteIpExt> {
2416 request: <<I::WatcherMarker as ProtocolMarker>::RequestStream as Stream>::Item,
2417 update: I::WatchEvent,
2418 }
2419 stream
2420 .zip(futures::stream::iter(updates.into_iter()))
2421 .for_each(|(request, update)| async move {
2422 I::map_ip_in(
2423 HandleInputs { request, update },
2424 |HandleInputs { request, update }| match request
2425 .expect("failed to receive `Watch` request")
2426 {
2427 fnet_routes::WatcherV4Request::Watch { responder } => {
2428 responder.send(&[update]).expect("failed to respond to `Watch`")
2429 }
2430 },
2431 |HandleInputs { request, update }| match request
2432 .expect("failed to receive `Watch` request")
2433 {
2434 fnet_routes::WatcherV6Request::Watch { responder } => {
2435 responder.send(&[update]).expect("failed to respond to `Watch`")
2436 }
2437 },
2438 );
2439 })
2440 .await;
2441 }
2442
2443 async fn run_event_loop<I: Ip>(
2444 inputs: crate::route_eventloop::EventLoopInputs<
2445 FakeInterfacesHandler,
2446 FakeSender<RouteNetlinkMessage>,
2447 OnlyRoutes,
2448 >,
2449 ) -> Never {
2450 let included_workers = match I::VERSION {
2451 IpVersion::V4 => crate::route_eventloop::IncludedWorkers {
2452 routes_v4: EventLoopComponent::Present(()),
2453 routes_v6: EventLoopComponent::Absent(Optional),
2454 interfaces: EventLoopComponent::Absent(Optional),
2455 rules_v4: EventLoopComponent::Absent(Optional),
2456 rules_v6: EventLoopComponent::Absent(Optional),
2457 nduseropt: EventLoopComponent::Absent(Optional),
2458 neighbors: EventLoopComponent::Absent(Optional),
2459 },
2460 IpVersion::V6 => crate::route_eventloop::IncludedWorkers {
2461 routes_v4: EventLoopComponent::Absent(Optional),
2462 routes_v6: EventLoopComponent::Present(()),
2463 interfaces: EventLoopComponent::Absent(Optional),
2464 rules_v4: EventLoopComponent::Absent(Optional),
2465 rules_v6: EventLoopComponent::Absent(Optional),
2466 nduseropt: EventLoopComponent::Absent(Optional),
2467 neighbors: EventLoopComponent::Absent(Optional),
2468 },
2469 };
2470
2471 let event_loop = inputs.initialize(included_workers).await;
2472 event_loop.run().await
2473 }
2474
2475 fn get_test_route_events_new_route_args<A: IpAddress>(
2476 subnet: Subnet<A>,
2477 next_hop1: A,
2478 next_hop2: A,
2479 ) -> [RequestArgs<A::Version>; 2]
2480 where
2481 A::Version: fnet_routes_ext::FidlRouteIpExt,
2482 {
2483 [
2484 RequestArgs::Route(RouteRequestArgs::New(NewRouteArgs::Unicast(
2485 create_unicast_new_route_args(
2486 subnet,
2487 next_hop1,
2488 DEV1.into(),
2489 METRIC1,
2490 MANAGED_ROUTE_TABLE_INDEX,
2491 ),
2492 ))),
2493 RequestArgs::Route(RouteRequestArgs::New(NewRouteArgs::Unicast(
2494 create_unicast_new_route_args(
2495 subnet,
2496 next_hop2,
2497 DEV2.into(),
2498 METRIC2,
2499 MANAGED_ROUTE_TABLE_INDEX,
2500 ),
2501 ))),
2502 ]
2503 }
2504
2505 fn create_unicast_new_route_args<A: IpAddress>(
2506 subnet: Subnet<A>,
2507 next_hop: A,
2508 interface_id: u64,
2509 priority: u32,
2510 table: NetlinkRouteTableIndex,
2511 ) -> UnicastNewRouteArgs<A::Version> {
2512 UnicastNewRouteArgs {
2513 subnet,
2514 target: fnet_routes_ext::RouteTarget {
2515 outbound_interface: interface_id,
2516 next_hop: SpecifiedAddr::new(next_hop),
2517 },
2518 priority: NonZeroU32::new(priority),
2519 table,
2520 }
2521 }
2522
2523 fn create_unicast_del_route_args<A: IpAddress>(
2524 subnet: Subnet<A>,
2525 next_hop: Option<A>,
2526 interface_id: Option<u64>,
2527 priority: Option<u32>,
2528 table: NetlinkRouteTableIndex,
2529 ) -> UnicastDelRouteArgs<A::Version> {
2530 UnicastDelRouteArgs {
2531 subnet,
2532 outbound_interface: interface_id.map(NonZeroU64::new).flatten(),
2533 next_hop: next_hop.map(SpecifiedAddr::new).flatten(),
2534 priority: priority.map(NonZeroU32::new).flatten(),
2535 table: NonZeroNetlinkRouteTableIndex::new(table).unwrap(),
2536 }
2537 }
2538
2539 #[derive(Debug, PartialEq)]
2540 struct TestRequestResult {
2541 messages: Vec<SentMessage<RouteNetlinkMessage>>,
2542 waiter_results: Vec<Result<(), RequestError>>,
2543 }
2544
2545 async fn test_requests<
2551 A: IpAddress,
2552 Fut: Future<Output = ()>,
2553 F: FnOnce(fnet_root::InterfacesRequestStream) -> Fut,
2554 >(
2555 args: impl IntoIterator<Item = RequestArgs<A::Version>>,
2556 root_handler: F,
2557 route_set_results: HashMap<fnet_routes_ext::TableId, VecDeque<RouteSetResult>>,
2558 subnet: Subnet<A>,
2559 next_hop1: A,
2560 next_hop2: A,
2561 num_sink_messages: usize,
2562 ) -> TestRequestResult
2563 where
2564 A::Version: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
2565 {
2566 let scope = fasync::Scope::new();
2567 let result = {
2568 let (mut route_sink, route_client, async_work_drain_task) =
2569 crate::client::testutil::new_fake_client::<NetlinkRoute>(
2570 crate::client::testutil::CLIENT_ID_1,
2571 [ModernGroup(match A::Version::VERSION {
2572 IpVersion::V4 => rtnetlink_groups_RTNLGRP_IPV4_ROUTE,
2573 IpVersion::V6 => rtnetlink_groups_RTNLGRP_IPV6_ROUTE,
2574 })],
2575 );
2576 let _join_handle = scope.spawn(async_work_drain_task);
2577 let (mut other_sink, other_client, async_work_drain_task) =
2578 crate::client::testutil::new_fake_client::<NetlinkRoute>(
2579 crate::client::testutil::CLIENT_ID_2,
2580 [ModernGroup(rtnetlink_groups_RTNLGRP_LINK)],
2581 );
2582 let _join_handle = scope.spawn(async_work_drain_task);
2583 let Setup {
2584 event_loop_inputs,
2585 mut watcher_stream,
2586 route_sets: mut route_set_stream,
2587 interfaces_request_stream,
2588 request_sink,
2589 async_work_sink: _,
2590 } = setup_with_route_clients::<A::Version>({
2591 let route_clients = ClientTable::default();
2592 route_clients.add_client(route_client.clone());
2593 route_clients.add_client(other_client);
2594 route_clients
2595 });
2596
2597 let mut event_loop_fut = pin!(run_event_loop::<A::Version>(event_loop_inputs).fuse());
2598
2599 let watcher_stream_fut = respond_to_watcher::<A::Version, _>(
2600 watcher_stream.by_ref(),
2601 std::iter::once(fnet_routes_ext::Event::<A::Version>::Idle.try_into().unwrap()),
2602 );
2603 futures::select! {
2604 () = watcher_stream_fut.fuse() => {},
2605 err = event_loop_fut => unreachable!("eventloop should not return: {err:?}"),
2606 }
2607 assert_eq!(&route_sink.take_messages()[..], &[]);
2608 assert_eq!(&other_sink.take_messages()[..], &[]);
2609
2610 let route_client = &route_client;
2611 let fut = async {
2612 let initial_new_routes =
2614 get_test_route_events_new_route_args(subnet, next_hop1, next_hop2);
2615 let count_initial_new_routes = initial_new_routes.len();
2616
2617 let request_sink = futures::stream::iter(initial_new_routes)
2618 .fold(request_sink, |mut request_sink, args| async move {
2619 let (completer, waiter) = oneshot::channel();
2620 request_sink
2621 .send(
2622 Request {
2623 args,
2624 sequence_number: TEST_SEQUENCE_NUMBER,
2625 client: route_client.clone(),
2626 completer,
2627 }
2628 .into(),
2629 )
2630 .await
2631 .unwrap();
2632 assert_matches!(waiter.await.unwrap(), Ok(()));
2633 request_sink
2634 })
2635 .await;
2636
2637 for _ in 0..count_initial_new_routes {
2641 let _ = route_sink.next_message().await;
2642 }
2643 assert_eq!(route_sink.next_message().now_or_never(), None);
2644
2645 let (results, _request_sink) = futures::stream::iter(args)
2646 .fold(
2647 (Vec::new(), request_sink),
2648 |(mut results, mut request_sink), args| async move {
2649 let (completer, waiter) = oneshot::channel();
2650 request_sink
2651 .send(
2652 Request {
2653 args,
2654 sequence_number: TEST_SEQUENCE_NUMBER,
2655 client: route_client.clone(),
2656 completer,
2657 }
2658 .into(),
2659 )
2660 .await
2661 .unwrap();
2662 results.push(waiter.await.unwrap());
2663 (results, request_sink)
2664 },
2665 )
2666 .await;
2667
2668 let messages = {
2669 assert_eq!(&other_sink.take_messages()[..], &[]);
2670 let mut messages = Vec::new();
2671 while messages.len() < num_sink_messages {
2672 messages.push(route_sink.next_message().await);
2673 }
2674 assert_eq!(route_sink.next_message().now_or_never(), None);
2675 messages
2676 };
2677
2678 (messages, results)
2679 };
2680
2681 let route_set_fut = respond_to_route_set_modifications::<A::Version, _, _>(
2682 route_set_stream.by_ref(),
2683 watcher_stream.by_ref(),
2684 route_set_results,
2685 )
2686 .fuse();
2687
2688 let root_interfaces_fut = root_handler(interfaces_request_stream).fuse();
2689
2690 let (messages, results) = futures::select! {
2691 (messages, results) = fut.fuse() => (messages, results),
2692 res = futures::future::join3(
2693 route_set_fut,
2694 root_interfaces_fut,
2695 event_loop_fut,
2696 ) => {
2697 unreachable!("eventloop/stream handlers should not return: {res:?}")
2698 }
2699 };
2700
2701 TestRequestResult { messages, waiter_results: results }
2702 };
2703 scope.join().await;
2704 result
2705 }
2706
2707 #[test_case(V4_SUB1, V4_NEXTHOP1, V4_NEXTHOP2; "v4_route_dump")]
2708 #[test_case(V6_SUB1, V6_NEXTHOP1, V6_NEXTHOP2; "v6_route_dump")]
2709 #[fuchsia::test]
2710 async fn test_get_route<A: IpAddress>(subnet: Subnet<A>, next_hop1: A, next_hop2: A)
2711 where
2712 A::Version: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
2713 {
2714 let expected_messages = vec![
2715 SentMessage::unicast(
2716 create_netlink_route_message::<A::Version>(
2717 subnet.prefix(),
2718 MANAGED_ROUTE_TABLE_INDEX,
2719 create_nlas::<A::Version>(
2720 Some(subnet),
2721 Some(next_hop1),
2722 DEV1,
2723 METRIC1,
2724 Some(MANAGED_ROUTE_TABLE_ID),
2725 ),
2726 )
2727 .into_rtnl_new_route(TEST_SEQUENCE_NUMBER, true),
2728 ),
2729 SentMessage::unicast(
2730 create_netlink_route_message::<A::Version>(
2731 subnet.prefix(),
2732 MANAGED_ROUTE_TABLE_INDEX,
2733 create_nlas::<A::Version>(
2734 Some(subnet),
2735 Some(next_hop2),
2736 DEV2,
2737 METRIC2,
2738 Some(MANAGED_ROUTE_TABLE_ID),
2739 ),
2740 )
2741 .into_rtnl_new_route(TEST_SEQUENCE_NUMBER, true),
2742 ),
2743 ];
2744
2745 pretty_assertions::assert_eq!(
2746 {
2747 let mut test_request_result = test_requests(
2748 [RequestArgs::Route(RouteRequestArgs::Get(GetRouteArgs::Dump))],
2749 |interfaces_request_stream| async {
2750 interfaces_request_stream
2751 .for_each(|req| async move {
2752 panic!("unexpected InterfacesRequest: {req:?}")
2753 })
2754 .await;
2755 },
2756 HashMap::new(),
2757 subnet,
2758 next_hop1,
2759 next_hop2,
2760 expected_messages.len(),
2761 )
2762 .await;
2763 test_request_result.messages.sort_by_key(|message| {
2764 assert_matches!(
2765 &message.message.payload,
2766 NetlinkPayload::InnerMessage(RouteNetlinkMessage::NewRoute(m)) => {
2767 m.attributes.clone().into_iter().filter_map(|nla|
2770 match nla {
2771 RouteAttribute::Oif(interface_id) =>
2772 Some((m.header.address_family, interface_id)),
2773 RouteAttribute::Destination(_)
2774 | RouteAttribute::Gateway(_)
2775 | RouteAttribute::Priority(_)
2776 | RouteAttribute::Table(_) => None,
2777 _ => panic!("unexpected NLA {nla:?} present in payload"),
2778 }
2779 ).next()
2780 }
2781 )
2782 });
2783 test_request_result
2784 },
2785 TestRequestResult { messages: expected_messages, waiter_results: vec![Ok(())] },
2786 )
2787 }
2788
2789 #[derive(Debug, Clone, Copy)]
2790 enum RouteSetResult {
2791 AddResult(Result<bool, fnet_routes_admin::RouteSetError>),
2792 DelResult(Result<bool, fnet_routes_admin::RouteSetError>),
2793 AuthenticationResult(Result<(), fnet_routes_admin::AuthenticateForInterfaceError>),
2794 }
2795
2796 fn route_event_from_route<
2797 I: Ip + fnet_routes_ext::FidlRouteIpExt,
2798 F: FnOnce(fnet_routes_ext::InstalledRoute<I>) -> fnet_routes_ext::Event<I>,
2799 >(
2800 route: I::Route,
2801 table_id: fnet_routes_ext::TableId,
2802 event_fn: F,
2803 ) -> I::WatchEvent {
2804 let route: fnet_routes_ext::Route<I> = route.try_into().unwrap();
2805
2806 let metric = match route.properties.specified_properties.metric {
2807 fnet_routes::SpecifiedMetric::ExplicitMetric(metric) => metric,
2808 fnet_routes::SpecifiedMetric::InheritedFromInterface(fnet_routes::Empty) => {
2809 panic!("metric should be explicit")
2810 }
2811 };
2812
2813 event_fn(fnet_routes_ext::InstalledRoute {
2814 route,
2815 effective_properties: fnet_routes_ext::EffectiveRouteProperties { metric },
2816 table_id,
2818 })
2819 .try_into()
2820 .unwrap()
2821 }
2822
2823 async fn respond_to_route_set_modifications<
2826 I: Ip + fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
2827 RS: Stream<
2828 Item = (
2829 fnet_routes_ext::TableId,
2830 <<I::RouteSetMarker as ProtocolMarker>::RequestStream as Stream>::Item,
2831 ),
2832 >,
2833 WS: Stream<Item = <<I::WatcherMarker as ProtocolMarker>::RequestStream as Stream>::Item>
2834 + std::marker::Unpin,
2835 >(
2836 route_stream: RS,
2837 watcher_stream: WS,
2838 mut route_set_results: HashMap<fnet_routes_ext::TableId, VecDeque<RouteSetResult>>,
2839 ) {
2840 #[derive(GenericOverIp)]
2841 #[generic_over_ip(I, Ip)]
2842 struct RouteSetInputs<I: fnet_routes_ext::admin::FidlRouteAdminIpExt> {
2843 request: <<I::RouteSetMarker as ProtocolMarker>::RequestStream as Stream>::Item,
2844 route_set_result: RouteSetResult,
2845 }
2846 #[derive(GenericOverIp)]
2847 #[generic_over_ip(I, Ip)]
2848 struct RouteSetOutputs<I: fnet_routes_ext::FidlRouteIpExt> {
2849 event: Option<I::WatchEvent>,
2850 }
2851
2852 let mut route_stream = std::pin::pin!(route_stream);
2853 let mut watcher_stream = std::pin::pin!(watcher_stream);
2854
2855 {
2856 let queue = route_set_results.entry(OTHER_FIDL_TABLE_ID).or_default();
2857 queue.push_front(RouteSetResult::AddResult(Ok(true)));
2858 queue.push_front(RouteSetResult::AddResult(Ok(true)));
2859 }
2860
2861 while let Some((table_id, request)) = route_stream.next().await {
2862 let route_set_result = route_set_results
2863 .get_mut(&table_id)
2864 .unwrap_or_else(|| panic!("missing result for {table_id:?}"))
2865 .pop_front()
2866 .unwrap_or_else(|| panic!("missing result for {table_id:?}"));
2867 let RouteSetOutputs { event } = I::map_ip(
2868 RouteSetInputs { request, route_set_result },
2869 |RouteSetInputs { request, route_set_result }| {
2870 let request = request.expect("failed to receive request");
2871 crate::logging::log_debug!(
2872 "responding on {table_id:?} to route set request {request:?} \
2873 with result {route_set_result:?}"
2874 );
2875 match request {
2876 fnet_routes_admin::RouteSetV4Request::AddRoute { route, responder } => {
2877 let route_set_result = assert_matches!(
2878 route_set_result,
2879 RouteSetResult::AddResult(res) => res
2880 );
2881
2882 responder
2883 .send(route_set_result)
2884 .expect("failed to respond to `AddRoute`");
2885
2886 RouteSetOutputs {
2887 event: match route_set_result {
2888 Ok(true) => Some(route_event_from_route::<Ipv4, _>(
2889 route,
2890 table_id,
2891 fnet_routes_ext::Event::<Ipv4>::Added,
2892 )),
2893 _ => None,
2894 },
2895 }
2896 }
2897 fnet_routes_admin::RouteSetV4Request::RemoveRoute { route, responder } => {
2898 let route_set_result = assert_matches!(
2899 route_set_result,
2900 RouteSetResult::DelResult(res) => res
2901 );
2902
2903 responder
2904 .send(route_set_result)
2905 .expect("failed to respond to `RemoveRoute`");
2906
2907 RouteSetOutputs {
2908 event: match route_set_result {
2909 Ok(true) => Some(route_event_from_route::<Ipv4, _>(
2910 route,
2911 table_id,
2912 fnet_routes_ext::Event::<Ipv4>::Removed,
2913 )),
2914 _ => None,
2915 },
2916 }
2917 }
2918 fnet_routes_admin::RouteSetV4Request::AuthenticateForInterface {
2919 credential: _,
2920 responder,
2921 } => {
2922 let route_set_result = assert_matches!(
2923 route_set_result,
2924 RouteSetResult::AuthenticationResult(res) => res
2925 );
2926
2927 responder
2928 .send(route_set_result)
2929 .expect("failed to respond to `AuthenticateForInterface`");
2930 RouteSetOutputs { event: None }
2931 }
2932 }
2933 },
2934 |RouteSetInputs { request, route_set_result }| {
2935 let request = request.expect("failed to receive request");
2936 crate::logging::log_debug!(
2937 "responding on {table_id:?} to route set request {request:?} \
2938 with result {route_set_result:?}"
2939 );
2940 match request {
2941 fnet_routes_admin::RouteSetV6Request::AddRoute { route, responder } => {
2942 let route_set_result = assert_matches!(
2943 route_set_result,
2944 RouteSetResult::AddResult(res) => res
2945 );
2946
2947 responder
2948 .send(route_set_result)
2949 .expect("failed to respond to `AddRoute`");
2950
2951 RouteSetOutputs {
2952 event: match route_set_result {
2953 Ok(true) => Some(route_event_from_route::<Ipv6, _>(
2954 route,
2955 table_id,
2956 fnet_routes_ext::Event::<Ipv6>::Added,
2957 )),
2958 _ => None,
2959 },
2960 }
2961 }
2962 fnet_routes_admin::RouteSetV6Request::RemoveRoute { route, responder } => {
2963 let route_set_result = assert_matches!(
2964 route_set_result,
2965 RouteSetResult::DelResult(res) => res
2966 );
2967
2968 responder
2969 .send(route_set_result)
2970 .expect("failed to respond to `RemoveRoute`");
2971
2972 RouteSetOutputs {
2973 event: match route_set_result {
2974 Ok(true) => Some(route_event_from_route::<Ipv6, _>(
2975 route,
2976 table_id,
2977 fnet_routes_ext::Event::<Ipv6>::Removed,
2978 )),
2979 _ => None,
2980 },
2981 }
2982 }
2983 fnet_routes_admin::RouteSetV6Request::AuthenticateForInterface {
2984 credential: _,
2985 responder,
2986 } => {
2987 let route_set_result = assert_matches!(
2988 route_set_result,
2989 RouteSetResult::AuthenticationResult(res) => res
2990 );
2991
2992 responder
2993 .send(route_set_result)
2994 .expect("failed to respond to `AuthenticateForInterface`");
2995 RouteSetOutputs { event: None }
2996 }
2997 }
2998 },
2999 );
3000
3001 if let Some(update) = event {
3002 let request = watcher_stream.next().await.expect("watcher stream should not end");
3003
3004 #[derive(GenericOverIp)]
3005 #[generic_over_ip(I, Ip)]
3006 struct HandleInputs<I: fnet_routes_ext::FidlRouteIpExt> {
3007 request: <<I::WatcherMarker as ProtocolMarker>::RequestStream as Stream>::Item,
3008 update: I::WatchEvent,
3009 }
3010
3011 I::map_ip_in(
3012 HandleInputs { request, update },
3013 |HandleInputs { request, update }| match request
3014 .expect("failed to receive `Watch` request")
3015 {
3016 fnet_routes::WatcherV4Request::Watch { responder } => {
3017 responder.send(&[update]).expect("failed to respond to `Watch`")
3018 }
3019 },
3020 |HandleInputs { request, update }| match request
3021 .expect("failed to receive `Watch` request")
3022 {
3023 fnet_routes::WatcherV6Request::Watch { responder } => {
3024 responder.send(&[update]).expect("failed to respond to `Watch`")
3025 }
3026 },
3027 );
3028 }
3029 }
3030
3031 if route_set_results.values().any(|value| !value.is_empty()) {
3032 panic!("unused route_set_results entries: {route_set_results:?}");
3033 }
3034 }
3035
3036 async fn test_route_requests<
3041 A: IpAddress,
3042 Fut: Future<Output = ()>,
3043 F: FnMut(fnet_interfaces_admin::ControlRequest) -> Fut,
3044 >(
3045 args: impl IntoIterator<Item = RequestArgs<A::Version>>,
3046 mut control_request_handler: F,
3047 route_set_results: HashMap<fnet_routes_ext::TableId, VecDeque<RouteSetResult>>,
3048 subnet: Subnet<A>,
3049 next_hop1: A,
3050 next_hop2: A,
3051 num_sink_messages: usize,
3052 ) -> TestRequestResult
3053 where
3054 A::Version: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
3055 {
3056 test_requests(
3057 args,
3058 |interfaces_request_stream| async move {
3059 interfaces_request_stream
3060 .filter_map(|req| {
3061 futures::future::ready(match req.unwrap() {
3062 fnet_root::InterfacesRequest::GetAdmin {
3063 id,
3064 control,
3065 control_handle: _,
3066 } => {
3067 pretty_assertions::assert_eq!(id, DEV1 as u64);
3068 Some(control.into_stream())
3069 }
3070 req => unreachable!("unexpected interfaces request: {req:?}"),
3071 })
3072 })
3073 .flatten()
3074 .next()
3075 .then(|req| control_request_handler(req.unwrap().unwrap()))
3076 .await
3077 },
3078 route_set_results,
3079 subnet,
3080 next_hop1,
3081 next_hop2,
3082 num_sink_messages,
3083 )
3084 .await
3085 }
3086
3087 async fn test_route_requests_helper<A: IpAddress>(
3090 args: impl IntoIterator<Item = RequestArgs<A::Version>>,
3091 expected_messages: Vec<SentMessage<RouteNetlinkMessage>>,
3092 route_set_results: HashMap<fnet_routes_ext::TableId, VecDeque<RouteSetResult>>,
3093 waiter_results: Vec<Result<(), RequestError>>,
3094 subnet: Subnet<A>,
3095 ) where
3096 A::Version: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
3097 {
3098 let (next_hop1, next_hop2): (A, A) = A::Version::map_ip(
3099 (),
3100 |()| (V4_NEXTHOP1, V4_NEXTHOP2),
3101 |()| (V6_NEXTHOP1, V6_NEXTHOP2),
3102 );
3103
3104 pretty_assertions::assert_eq!(
3105 {
3106 let mut test_request_result = test_route_requests(
3107 args,
3108 |req| async {
3109 match req {
3110 fnet_interfaces_admin::ControlRequest::GetAuthorizationForInterface {
3111 responder,
3112 } => {
3113 let token = fidl::Event::create();
3114 let grant = fnet_resources::GrantForInterfaceAuthorization {
3115 interface_id: DEV1 as u64,
3116 token,
3117 };
3118 responder.send(grant).unwrap();
3119 }
3120 req => panic!("unexpected request {req:?}"),
3121 }
3122 },
3123 route_set_results,
3124 subnet,
3125 next_hop1,
3126 next_hop2,
3127 expected_messages.len(),
3128 )
3129 .await;
3130 test_request_result.messages.sort_by_key(|message| {
3131 let sequence_number = message.message.header.sequence_number;
3134 assert_matches!(
3135 &message.message.payload,
3136 NetlinkPayload::InnerMessage(RouteNetlinkMessage::NewRoute(m))
3137 | NetlinkPayload::InnerMessage(RouteNetlinkMessage::DelRoute(m)) => {
3138 m.attributes.clone().into_iter().filter_map(|nla|
3141 match nla {
3142 RouteAttribute::Priority(priority) =>
3143 Some((sequence_number, priority)),
3144 RouteAttribute::Destination(_)
3145 | RouteAttribute::Gateway(_)
3146 | RouteAttribute::Oif(_)
3147 | RouteAttribute::Table(_) => None,
3148 _ => panic!("unexpected NLA {nla:?} present in payload"),
3149 }
3150 ).next()
3151 }
3152 )
3153 });
3154 test_request_result
3155 },
3156 TestRequestResult { messages: expected_messages, waiter_results },
3157 )
3158 }
3159
3160 enum RouteRequestKind {
3161 New,
3162 Del,
3163 }
3164
3165 fn route_set_for_table_id(
3166 results: Vec<RouteSetResult>,
3167 table_id: fnet_routes_ext::TableId,
3168 ) -> HashMap<fnet_routes_ext::TableId, VecDeque<RouteSetResult>> {
3169 HashMap::from_iter([(table_id, results.into())])
3170 }
3171
3172 fn route_set_for_first_new_table(
3173 results: Vec<RouteSetResult>,
3174 ) -> HashMap<fnet_routes_ext::TableId, VecDeque<RouteSetResult>> {
3175 route_set_for_table_id(results, OTHER_FIDL_TABLE_ID)
3176 }
3177
3178 #[test_case(
3180 RouteRequestKind::New,
3181 vec![
3182 RouteSetResult::AddResult(Ok(true))
3183 ],
3184 Ok(()),
3185 V4_SUB1,
3186 Some(METRIC3),
3187 DEV1;
3188 "v4_new_success")]
3189 #[test_case(
3190 RouteRequestKind::New,
3191 vec![
3192 RouteSetResult::AddResult(Ok(true))
3193 ],
3194 Ok(()),
3195 V6_SUB1,
3196 Some(METRIC3),
3197 DEV1;
3198 "v6_new_success")]
3199 #[test_case(
3200 RouteRequestKind::New,
3201 vec![
3202 RouteSetResult::AddResult(Err(RouteSetError::Unauthenticated)),
3203 RouteSetResult::AuthenticationResult(Err(
3204 fnet_routes_admin::AuthenticateForInterfaceError::InvalidAuthentication
3205 )),
3206 ],
3207 Err(RequestError::UnrecognizedInterface),
3208 V4_SUB1,
3209 Some(METRIC3),
3210 DEV1;
3211 "v4_new_failed_auth")]
3212 #[test_case(
3213 RouteRequestKind::New,
3214 vec![
3215 RouteSetResult::AddResult(Err(RouteSetError::Unauthenticated)),
3216 RouteSetResult::AuthenticationResult(Err(
3217 fnet_routes_admin::AuthenticateForInterfaceError::InvalidAuthentication
3218 )),
3219 ],
3220 Err(RequestError::UnrecognizedInterface),
3221 V6_SUB1,
3222 Some(METRIC3),
3223 DEV1;
3224 "v6_new_failed_auth")]
3225 #[test_case(
3226 RouteRequestKind::New,
3227 vec![
3228 RouteSetResult::AddResult(Ok(false))
3229 ],
3230 Err(RequestError::AlreadyExists),
3231 V4_SUB1,
3232 Some(METRIC3),
3233 DEV1;
3234 "v4_new_failed_netstack_reports_exists")]
3235 #[test_case(
3236 RouteRequestKind::New,
3237 vec![
3238 RouteSetResult::AddResult(Ok(false))
3239 ],
3240 Err(RequestError::AlreadyExists),
3241 V6_SUB1,
3242 Some(METRIC3),
3243 DEV1;
3244 "v6_new_failed_netstack_reports_exists")]
3245 #[test_case(
3246 RouteRequestKind::New,
3247 vec![],
3248 Err(RequestError::AlreadyExists),
3249 V4_SUB1,
3250 Some(METRIC1),
3251 DEV1;
3252 "v4_new_failed_netlink_reports_exists")]
3253 #[test_case(
3254 RouteRequestKind::New,
3255 vec![],
3256 Err(RequestError::AlreadyExists),
3257 V4_SUB1,
3258 Some(METRIC1),
3259 DEV2;
3260 "v4_new_failed_netlink_reports_exists_different_interface")]
3261 #[test_case(
3262 RouteRequestKind::New,
3263 vec![],
3264 Err(RequestError::AlreadyExists),
3265 V6_SUB1,
3266 Some(METRIC1),
3267 DEV1;
3268 "v6_new_failed_netlink_reports_exists")]
3269 #[test_case(
3270 RouteRequestKind::New,
3271 vec![],
3272 Err(RequestError::AlreadyExists),
3273 V6_SUB1,
3274 Some(METRIC1),
3275 DEV2;
3276 "v6_new_failed_netlink_reports_exists_different_interface")]
3277 #[test_case(
3278 RouteRequestKind::New,
3279 vec![
3280 RouteSetResult::AddResult(Err(RouteSetError::InvalidDestinationSubnet))
3281 ],
3282 Err(RequestError::InvalidRequest),
3283 V4_SUB1,
3284 Some(METRIC3),
3285 DEV1;
3286 "v4_new_invalid_dest")]
3287 #[test_case(
3288 RouteRequestKind::New,
3289 vec![
3290 RouteSetResult::AddResult(Err(RouteSetError::InvalidDestinationSubnet))
3291 ],
3292 Err(RequestError::InvalidRequest),
3293 V6_SUB1,
3294 Some(METRIC3),
3295 DEV1;
3296 "v6_new_invalid_dest")]
3297 #[test_case(
3298 RouteRequestKind::New,
3299 vec![
3300 RouteSetResult::AddResult(Err(RouteSetError::InvalidNextHop))
3301 ],
3302 Err(RequestError::InvalidRequest),
3303 V4_SUB1,
3304 Some(METRIC3),
3305 DEV1;
3306 "v4_new_invalid_hop")]
3307 #[test_case(
3308 RouteRequestKind::New,
3309 vec![
3310 RouteSetResult::AddResult(Err(RouteSetError::InvalidNextHop))
3311 ],
3312 Err(RequestError::InvalidRequest),
3313 V6_SUB1,
3314 Some(METRIC3),
3315 DEV1;
3316 "v6_new_invalid_hop")]
3317 #[test_case(
3319 RouteRequestKind::Del,
3320 vec![
3321 RouteSetResult::DelResult(Ok(true))
3322 ],
3323 Ok(()),
3324 V4_SUB1,
3325 None,
3326 DEV1;
3327 "v4_del_success_only_subnet")]
3328 #[test_case(
3329 RouteRequestKind::Del,
3330 vec![
3331 RouteSetResult::DelResult(Ok(true))
3332 ],
3333 Ok(()),
3334 V4_SUB1,
3335 Some(METRIC1),
3336 DEV1;
3337 "v4_del_success_only_subnet_metric")]
3338 #[test_case(
3339 RouteRequestKind::Del,
3340 vec![
3341 RouteSetResult::DelResult(Ok(true))
3342 ],
3343 Ok(()),
3344 V6_SUB1,
3345 None,
3346 DEV1;
3347 "v6_del_success_only_subnet")]
3348 #[test_case(
3349 RouteRequestKind::Del,
3350 vec![
3351 RouteSetResult::DelResult(Ok(true))
3352 ],
3353 Ok(()),
3354 V6_SUB1,
3355 Some(METRIC1),
3356 DEV1;
3357 "v6_del_success_only_subnet_metric")]
3358 #[test_case(
3359 RouteRequestKind::Del,
3360 vec![
3361 RouteSetResult::DelResult(Err(RouteSetError::Unauthenticated)),
3362 RouteSetResult::AuthenticationResult(Err(
3363 fnet_routes_admin::AuthenticateForInterfaceError::InvalidAuthentication
3364 )),
3365 ],
3366 Err(RequestError::UnrecognizedInterface),
3367 V4_SUB1,
3368 None,
3369 DEV1;
3370 "v4_del_failed_auth")]
3371 #[test_case(
3372 RouteRequestKind::Del,
3373 vec![
3374 RouteSetResult::DelResult(Err(RouteSetError::Unauthenticated)),
3375 RouteSetResult::AuthenticationResult(Err(
3376 fnet_routes_admin::AuthenticateForInterfaceError::InvalidAuthentication
3377 )),
3378 ],
3379 Err(RequestError::UnrecognizedInterface),
3380 V6_SUB1,
3381 None,
3382 DEV1;
3383 "v6_del_failed_auth")]
3384 #[test_case(
3385 RouteRequestKind::Del,
3386 vec![
3387 RouteSetResult::DelResult(Ok(false))
3388 ],
3389 Err(RequestError::DeletionNotAllowed),
3390 V4_SUB1,
3391 None,
3392 DEV1;
3393 "v4_del_failed_attempt_to_delete_route_from_global_set")]
3394 #[test_case(
3395 RouteRequestKind::Del,
3396 vec![
3397 RouteSetResult::DelResult(Ok(false))
3398 ],
3399 Err(RequestError::DeletionNotAllowed),
3400 V6_SUB1,
3401 None,
3402 DEV1;
3403 "v6_del_failed_attempt_to_delete_route_from_global_set")]
3404 #[test_case(
3410 RouteRequestKind::Del,
3411 vec![],
3412 Err(RequestError::NotFound),
3413 V4_SUB1,
3414 Some(METRIC3),
3415 DEV1;
3416 "v4_del_no_matching_route")]
3417 #[test_case(
3418 RouteRequestKind::Del,
3419 vec![],
3420 Err(RequestError::NotFound),
3421 V6_SUB1,
3422 Some(METRIC3),
3423 DEV1;
3424 "v6_del_no_matching_route")]
3425 #[test_case(
3426 RouteRequestKind::Del,
3427 vec![
3428 RouteSetResult::DelResult(Err(RouteSetError::InvalidDestinationSubnet))
3429 ],
3430 Err(RequestError::InvalidRequest),
3431 V4_SUB1,
3432 None,
3433 DEV1;
3434 "v4_del_invalid_dest")]
3435 #[test_case(
3436 RouteRequestKind::Del,
3437 vec![
3438 RouteSetResult::DelResult(Err(RouteSetError::InvalidDestinationSubnet))
3439 ],
3440 Err(RequestError::InvalidRequest),
3441 V6_SUB1,
3442 None,
3443 DEV1;
3444 "v6_del_invalid_dest")]
3445 #[test_case(
3446 RouteRequestKind::Del,
3447 vec![
3448 RouteSetResult::DelResult(Err(RouteSetError::InvalidNextHop))
3449 ],
3450 Err(RequestError::InvalidRequest),
3451 V4_SUB1,
3452 None,
3453 DEV1;
3454 "v4_del_invalid_hop")]
3455 #[test_case(
3456 RouteRequestKind::Del,
3457 vec![
3458 RouteSetResult::DelResult(Err(RouteSetError::InvalidNextHop))
3459 ],
3460 Err(RequestError::InvalidRequest),
3461 V6_SUB1,
3462 None,
3463 DEV1;
3464 "v6_del_invalid_hop")]
3465 #[fuchsia::test]
3466 async fn test_new_del_route<A: IpAddress>(
3467 kind: RouteRequestKind,
3468 route_set_results: Vec<RouteSetResult>,
3469 waiter_result: Result<(), RequestError>,
3470 subnet: Subnet<A>,
3471 metric: Option<u32>,
3472 interface_id: u32,
3473 ) where
3474 A::Version: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
3475 {
3476 let route_group = match A::Version::VERSION {
3477 IpVersion::V4 => ModernGroup(rtnetlink_groups_RTNLGRP_IPV4_ROUTE),
3478 IpVersion::V6 => ModernGroup(rtnetlink_groups_RTNLGRP_IPV6_ROUTE),
3479 };
3480
3481 let next_hop: A = A::Version::map_ip((), |()| V4_NEXTHOP1, |()| V6_NEXTHOP1);
3482
3483 let route_req_args = match kind {
3487 RouteRequestKind::New => {
3488 RouteRequestArgs::New(NewRouteArgs::Unicast(create_unicast_new_route_args(
3490 subnet,
3491 next_hop,
3492 interface_id.into(),
3493 metric.expect("add cases should be Some"),
3494 MANAGED_ROUTE_TABLE_INDEX,
3495 )))
3496 }
3497 RouteRequestKind::Del => {
3498 RouteRequestArgs::Del(DelRouteArgs::Unicast(create_unicast_del_route_args(
3500 subnet,
3501 None,
3502 None,
3503 metric,
3504 MANAGED_ROUTE_TABLE_INDEX,
3505 )))
3506 }
3507 };
3508
3509 let messages = match waiter_result {
3512 Ok(()) => {
3513 let build_message = |table| {
3514 let route_message = create_netlink_route_message::<A::Version>(
3515 subnet.prefix(),
3516 table,
3517 create_nlas::<A::Version>(
3518 Some(subnet),
3519 Some(next_hop),
3520 DEV1,
3521 match kind {
3522 RouteRequestKind::New => metric.expect("add cases should be some"),
3523 RouteRequestKind::Del => METRIC1,
3528 },
3529 (table != MAIN_ROUTE_TABLE_INDEX).then_some(table.get()),
3530 ),
3531 );
3532 let netlink_message = match kind {
3533 RouteRequestKind::New => {
3534 route_message.into_rtnl_new_route(UNSPECIFIED_SEQUENCE_NUMBER, false)
3535 }
3536 RouteRequestKind::Del => route_message.into_rtnl_del_route(),
3537 };
3538 SentMessage::multicast(netlink_message, route_group)
3539 };
3540
3541 let route_message_in_managed_table = build_message(MANAGED_ROUTE_TABLE_INDEX);
3542
3543 vec![route_message_in_managed_table]
3544 }
3545 Err(_) => Vec::new(),
3546 };
3547
3548 test_route_requests_helper(
3549 [RequestArgs::Route(route_req_args)],
3550 messages,
3551 route_set_for_first_new_table(route_set_results),
3552 vec![waiter_result],
3553 subnet,
3554 )
3555 .await;
3556 }
3557
3558 #[test_case(
3561 RouteRequestKind::New,
3562 vec![
3563 RouteSetResult::AddResult(Err(RouteSetError::Unauthenticated)),
3564 RouteSetResult::AuthenticationResult(Ok(())),
3565 RouteSetResult::AddResult(Err(RouteSetError::Unauthenticated)),
3566 ],
3567 Err(RequestError::InvalidRequest),
3568 V4_SUB1;
3569 "v4_new_unauthenticated")]
3570 #[test_case(
3571 RouteRequestKind::New,
3572 vec![
3573 RouteSetResult::AddResult(Err(RouteSetError::Unauthenticated)),
3574 RouteSetResult::AuthenticationResult(Ok(())),
3575 RouteSetResult::AddResult(Err(RouteSetError::Unauthenticated)),
3576 ],
3577 Err(RequestError::InvalidRequest),
3578 V6_SUB1;
3579 "v6_new_unauthenticated")]
3580 #[test_case(
3581 RouteRequestKind::Del,
3582 vec![
3583 RouteSetResult::DelResult(Err(RouteSetError::Unauthenticated)),
3584 RouteSetResult::AuthenticationResult(Ok(())),
3585 RouteSetResult::DelResult(Err(RouteSetError::Unauthenticated)),
3586 ],
3587 Err(RequestError::InvalidRequest),
3588 V4_SUB1;
3589 "v4_del_unauthenticated")]
3590 #[test_case(
3591 RouteRequestKind::Del,
3592 vec![
3593 RouteSetResult::DelResult(Err(RouteSetError::Unauthenticated)),
3594 RouteSetResult::AuthenticationResult(Ok(())),
3595 RouteSetResult::DelResult(Err(RouteSetError::Unauthenticated)),
3596 ],
3597 Err(RequestError::InvalidRequest),
3598 V6_SUB1;
3599 "v6_del_unauthenticated")]
3600 #[should_panic(expected = "received unauthentication error from route set for route")]
3601 #[fuchsia::test]
3602 async fn test_new_del_route_failed<A: IpAddress>(
3603 kind: RouteRequestKind,
3604 route_set_results: Vec<RouteSetResult>,
3605 waiter_result: Result<(), RequestError>,
3606 subnet: Subnet<A>,
3607 ) where
3608 A::Version: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
3609 {
3610 let route_req_args = match kind {
3611 RouteRequestKind::New => {
3612 let next_hop: A = A::Version::map_ip((), |()| V4_NEXTHOP1, |()| V6_NEXTHOP1);
3613 RouteRequestArgs::New(NewRouteArgs::Unicast(create_unicast_new_route_args(
3615 subnet,
3616 next_hop,
3617 DEV1.into(),
3618 METRIC3,
3619 MANAGED_ROUTE_TABLE_INDEX,
3620 )))
3621 }
3622 RouteRequestKind::Del => {
3623 RouteRequestArgs::Del(DelRouteArgs::Unicast(create_unicast_del_route_args(
3625 subnet,
3626 None,
3627 None,
3628 None,
3629 MANAGED_ROUTE_TABLE_INDEX,
3630 )))
3631 }
3632 };
3633 test_route_requests_helper(
3634 [RequestArgs::Route(route_req_args)],
3635 Vec::new(),
3636 route_set_for_first_new_table(route_set_results),
3637 vec![waiter_result],
3638 subnet,
3639 )
3640 .await;
3641 }
3642
3643 #[test_case(
3644 Err(RequestError::NotFound),
3645 V4_SUB1; "v4_del")]
3646 #[test_case(
3647 Err(RequestError::NotFound),
3648 V6_SUB1; "v6_del")]
3649 #[fuchsia::test]
3650 async fn test_del_route_nonexistent_table<A: IpAddress>(
3651 waiter_result: Result<(), RequestError>,
3652 subnet: Subnet<A>,
3653 ) where
3654 A::Version: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
3655 {
3656 let route_req_args =
3658 RouteRequestArgs::Del(DelRouteArgs::Unicast(create_unicast_del_route_args(
3659 subnet,
3660 None,
3661 None,
3662 None,
3663 NetlinkRouteTableIndex::new(1234),
3664 )));
3665 test_route_requests_helper(
3666 [RequestArgs::Route(route_req_args)],
3667 Vec::new(),
3668 HashMap::new(),
3669 vec![waiter_result],
3670 subnet,
3671 )
3672 .await;
3673 }
3674
3675 #[test_case(
3679 V4_SUB1,
3680 ModernGroup(rtnetlink_groups_RTNLGRP_IPV4_ROUTE),
3681 MANAGED_ROUTE_TABLE_INDEX;
3682 "v4_new_same_table_dump")]
3683 #[test_case(
3684 V6_SUB1,
3685 ModernGroup(rtnetlink_groups_RTNLGRP_IPV6_ROUTE),
3686 MANAGED_ROUTE_TABLE_INDEX;
3687 "v6_new_same_table_dump")]
3688 #[test_case(
3689 V4_SUB1,
3690 ModernGroup(rtnetlink_groups_RTNLGRP_IPV4_ROUTE),
3691 NetlinkRouteTableIndex::new(1234);
3692 "v4_new_different_table_dump")]
3693 #[test_case(
3694 V6_SUB1,
3695 ModernGroup(rtnetlink_groups_RTNLGRP_IPV6_ROUTE),
3696 NetlinkRouteTableIndex::new(1234);
3697 "v6_new_different_table_dump")]
3698 #[fuchsia::test]
3699 async fn test_new_then_get_dump_request<A: IpAddress>(
3700 subnet: Subnet<A>,
3701 group: ModernGroup,
3702 table: NetlinkRouteTableIndex,
3703 ) where
3704 A::Version: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
3705 {
3706 let (next_hop1, next_hop2): (A, A) = A::Version::map_ip(
3707 (),
3708 |()| (V4_NEXTHOP1, V4_NEXTHOP2),
3709 |()| (V6_NEXTHOP1, V6_NEXTHOP2),
3710 );
3711
3712 let unicast_route_args =
3718 create_unicast_new_route_args(subnet, next_hop1, DEV1.into(), METRIC3, table);
3719
3720 let messages = vec![
3725 SentMessage::multicast(
3726 create_netlink_route_message::<A::Version>(
3727 subnet.prefix(),
3728 table,
3729 create_nlas::<A::Version>(
3730 Some(subnet),
3731 Some(next_hop1),
3732 DEV1,
3733 METRIC3,
3734 Some(table.get()),
3735 ),
3736 )
3737 .into_rtnl_new_route(UNSPECIFIED_SEQUENCE_NUMBER, false),
3738 group,
3739 ),
3740 SentMessage::unicast(
3741 create_netlink_route_message::<A::Version>(
3742 subnet.prefix(),
3743 MANAGED_ROUTE_TABLE_INDEX,
3744 create_nlas::<A::Version>(
3745 Some(subnet),
3746 Some(next_hop1),
3747 DEV1,
3748 METRIC1,
3749 Some(MANAGED_ROUTE_TABLE_ID),
3750 ),
3751 )
3752 .into_rtnl_new_route(TEST_SEQUENCE_NUMBER, true),
3753 ),
3754 SentMessage::unicast(
3755 create_netlink_route_message::<A::Version>(
3756 subnet.prefix(),
3757 MANAGED_ROUTE_TABLE_INDEX,
3758 create_nlas::<A::Version>(
3759 Some(subnet),
3760 Some(next_hop2),
3761 DEV2,
3762 METRIC2,
3763 Some(MANAGED_ROUTE_TABLE_ID),
3764 ),
3765 )
3766 .into_rtnl_new_route(TEST_SEQUENCE_NUMBER, true),
3767 ),
3768 SentMessage::unicast(
3769 create_netlink_route_message::<A::Version>(
3770 subnet.prefix(),
3771 table,
3772 create_nlas::<A::Version>(
3773 Some(subnet),
3774 Some(next_hop1),
3775 DEV1,
3776 METRIC3,
3777 Some(table.get()),
3778 ),
3779 )
3780 .into_rtnl_new_route(TEST_SEQUENCE_NUMBER, true),
3781 ),
3782 ];
3783
3784 test_route_requests_helper(
3785 [
3786 RequestArgs::Route(RouteRequestArgs::New(NewRouteArgs::Unicast(
3787 unicast_route_args,
3788 ))),
3789 RequestArgs::Route(RouteRequestArgs::Get(GetRouteArgs::Dump)),
3790 ],
3791 messages,
3792 route_set_for_table_id(
3793 vec![RouteSetResult::AddResult(Ok(true))],
3794 if table == MANAGED_ROUTE_TABLE_INDEX {
3795 OTHER_FIDL_TABLE_ID
3796 } else {
3797 fnet_routes_ext::TableId::new(OTHER_FIDL_TABLE_ID.get() + 1)
3798 },
3799 ),
3800 vec![Ok(()), Ok(())],
3801 subnet,
3802 )
3803 .await;
3804 }
3805
3806 #[test_case(V4_SUB1; "v4_new_dump")]
3815 #[test_case(V6_SUB1; "v6_new_dump")]
3816 #[fuchsia::test]
3817 async fn test_new_route_different_table_then_get_dump_request<A: IpAddress>(subnet: Subnet<A>)
3818 where
3819 A::Version: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
3820 {
3821 let (next_hop1, next_hop2, IpInvariant(group)): (A, A, IpInvariant<ModernGroup>) =
3822 A::Version::map_ip(
3823 (),
3824 |()| {
3825 (
3826 V4_NEXTHOP1,
3827 V4_NEXTHOP2,
3828 IpInvariant(ModernGroup(rtnetlink_groups_RTNLGRP_IPV4_ROUTE)),
3829 )
3830 },
3831 |()| {
3832 (
3833 V6_NEXTHOP1,
3834 V6_NEXTHOP2,
3835 IpInvariant(ModernGroup(rtnetlink_groups_RTNLGRP_IPV6_ROUTE)),
3836 )
3837 },
3838 );
3839
3840 const ALTERNATIVE_ROUTE_TABLE: NetlinkRouteTableIndex = NetlinkRouteTableIndex::new(1337);
3841
3842 let unicast_route_args = create_unicast_new_route_args(
3849 subnet,
3850 next_hop1,
3851 DEV1.into(),
3852 METRIC1,
3853 ALTERNATIVE_ROUTE_TABLE,
3854 );
3855
3856 let messages = vec![
3860 SentMessage::multicast(
3861 create_netlink_route_message::<A::Version>(
3862 subnet.prefix(),
3863 ALTERNATIVE_ROUTE_TABLE,
3864 create_nlas::<A::Version>(
3865 Some(subnet),
3866 Some(next_hop1),
3867 DEV1,
3868 METRIC1,
3869 Some(ALTERNATIVE_ROUTE_TABLE.get()),
3870 ),
3871 )
3872 .into_rtnl_new_route(UNSPECIFIED_SEQUENCE_NUMBER, false),
3873 group,
3874 ),
3875 SentMessage::unicast(
3876 create_netlink_route_message::<A::Version>(
3877 subnet.prefix(),
3878 MANAGED_ROUTE_TABLE_INDEX,
3879 create_nlas::<A::Version>(
3880 Some(subnet),
3881 Some(next_hop1),
3882 DEV1,
3883 METRIC1,
3884 Some(MANAGED_ROUTE_TABLE_ID),
3885 ),
3886 )
3887 .into_rtnl_new_route(TEST_SEQUENCE_NUMBER, true),
3888 ),
3889 SentMessage::unicast(
3890 create_netlink_route_message::<A::Version>(
3891 subnet.prefix(),
3892 ALTERNATIVE_ROUTE_TABLE,
3893 create_nlas::<A::Version>(
3894 Some(subnet),
3895 Some(next_hop1),
3896 DEV1,
3897 METRIC1,
3898 Some(ALTERNATIVE_ROUTE_TABLE.get()),
3899 ),
3900 )
3901 .into_rtnl_new_route(TEST_SEQUENCE_NUMBER, true),
3902 ),
3903 SentMessage::unicast(
3904 create_netlink_route_message::<A::Version>(
3905 subnet.prefix(),
3906 MANAGED_ROUTE_TABLE_INDEX,
3907 create_nlas::<A::Version>(
3908 Some(subnet),
3909 Some(next_hop2),
3910 DEV2,
3911 METRIC2,
3912 Some(MANAGED_ROUTE_TABLE_ID),
3913 ),
3914 )
3915 .into_rtnl_new_route(TEST_SEQUENCE_NUMBER, true),
3916 ),
3917 ];
3918
3919 test_route_requests_helper(
3920 [
3921 RequestArgs::Route(RouteRequestArgs::New(NewRouteArgs::Unicast(
3922 unicast_route_args,
3923 ))),
3924 RequestArgs::Route(RouteRequestArgs::Get(GetRouteArgs::Dump)),
3925 ],
3926 messages,
3927 HashMap::from_iter([
3928 (MAIN_FIDL_TABLE_ID, vec![RouteSetResult::AddResult(Ok(false))].into()),
3930 (
3932 fnet_routes_ext::TableId::new(OTHER_FIDL_TABLE_ID.get() + 1),
3933 vec![RouteSetResult::AddResult(Ok(true))].into(),
3934 ),
3935 ]),
3936 vec![Ok(()), Ok(())],
3937 subnet,
3938 )
3939 .await;
3940 }
3941
3942 #[test_case(V4_SUB1, ModernGroup(rtnetlink_groups_RTNLGRP_IPV4_ROUTE); "v4_new_del_dump")]
3946 #[test_case(V6_SUB1, ModernGroup(rtnetlink_groups_RTNLGRP_IPV6_ROUTE); "v6_new_del_dump")]
3947 #[fuchsia::test]
3948 async fn test_new_then_del_then_get_dump_request<A: IpAddress>(
3949 subnet: Subnet<A>,
3950 group: ModernGroup,
3951 ) where
3952 A::Version: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
3953 {
3954 let (next_hop1, next_hop2): (A, A) = A::Version::map_ip(
3955 (),
3956 |()| (V4_NEXTHOP1, V4_NEXTHOP2),
3957 |()| (V6_NEXTHOP1, V6_NEXTHOP2),
3958 );
3959
3960 let new_route_args = create_unicast_new_route_args(
3966 subnet,
3967 next_hop1,
3968 DEV1.into(),
3969 METRIC3,
3970 MANAGED_ROUTE_TABLE_INDEX,
3971 );
3972
3973 let del_route_args = create_unicast_del_route_args(
3975 subnet,
3976 None,
3977 None,
3978 Some(METRIC3),
3979 MANAGED_ROUTE_TABLE_INDEX,
3980 );
3981
3982 let messages = vec![
3986 SentMessage::multicast(
3987 create_netlink_route_message::<A::Version>(
3988 subnet.prefix(),
3989 MANAGED_ROUTE_TABLE_INDEX,
3990 create_nlas::<A::Version>(
3991 Some(subnet),
3992 Some(next_hop1),
3993 DEV1,
3994 METRIC3,
3995 Some(MANAGED_ROUTE_TABLE_ID),
3996 ),
3997 )
3998 .into_rtnl_new_route(UNSPECIFIED_SEQUENCE_NUMBER, false),
3999 group,
4000 ),
4001 SentMessage::multicast(
4002 create_netlink_route_message::<A::Version>(
4003 subnet.prefix(),
4004 MANAGED_ROUTE_TABLE_INDEX,
4005 create_nlas::<A::Version>(
4006 Some(subnet),
4007 Some(next_hop1),
4008 DEV1,
4009 METRIC3,
4010 Some(MANAGED_ROUTE_TABLE_ID),
4011 ),
4012 )
4013 .into_rtnl_del_route(),
4014 group,
4015 ),
4016 SentMessage::unicast(
4017 create_netlink_route_message::<A::Version>(
4018 subnet.prefix(),
4019 MANAGED_ROUTE_TABLE_INDEX,
4020 create_nlas::<A::Version>(
4021 Some(subnet),
4022 Some(next_hop1),
4023 DEV1,
4024 METRIC1,
4025 Some(MANAGED_ROUTE_TABLE_ID),
4026 ),
4027 )
4028 .into_rtnl_new_route(TEST_SEQUENCE_NUMBER, true),
4029 ),
4030 SentMessage::unicast(
4031 create_netlink_route_message::<A::Version>(
4032 subnet.prefix(),
4033 MANAGED_ROUTE_TABLE_INDEX,
4034 create_nlas::<A::Version>(
4035 Some(subnet),
4036 Some(next_hop2),
4037 DEV2,
4038 METRIC2,
4039 Some(MANAGED_ROUTE_TABLE_ID),
4040 ),
4041 )
4042 .into_rtnl_new_route(TEST_SEQUENCE_NUMBER, true),
4043 ),
4044 ];
4045
4046 test_route_requests_helper(
4047 [
4048 RequestArgs::Route(RouteRequestArgs::New(NewRouteArgs::Unicast(new_route_args))),
4049 RequestArgs::Route(RouteRequestArgs::Del(DelRouteArgs::Unicast(del_route_args))),
4050 RequestArgs::Route(RouteRequestArgs::Get(GetRouteArgs::Dump)),
4051 ],
4052 messages,
4053 route_set_for_first_new_table(vec![
4054 RouteSetResult::AddResult(Ok(true)),
4055 RouteSetResult::DelResult(Ok(true)),
4056 ]),
4057 vec![Ok(()), Ok(()), Ok(())],
4058 subnet,
4059 )
4060 .await;
4061 }
4062
4063 #[test_case(RouteRequestKind::New, V4_SUB1; "v4_new_if_removed")]
4068 #[test_case(RouteRequestKind::New, V6_SUB1; "v6_new_if_removed")]
4069 #[test_case(RouteRequestKind::Del, V4_SUB1; "v4_del_if_removed")]
4070 #[test_case(RouteRequestKind::Del, V6_SUB1; "v6_del_if_removed")]
4071 #[fuchsia::test]
4072 async fn test_new_del_route_interface_removed<A: IpAddress>(
4073 kind: RouteRequestKind,
4074 subnet: Subnet<A>,
4075 ) where
4076 A::Version: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
4077 {
4078 let (next_hop1, next_hop2): (A, A) = A::Version::map_ip(
4079 (),
4080 |()| (V4_NEXTHOP1, V4_NEXTHOP2),
4081 |()| (V6_NEXTHOP1, V6_NEXTHOP2),
4082 );
4083
4084 let (route_req_args, route_set_result) = match kind {
4088 RouteRequestKind::New => {
4089 let args =
4091 RouteRequestArgs::New(NewRouteArgs::Unicast(create_unicast_new_route_args(
4092 subnet,
4093 next_hop1,
4094 DEV1.into(),
4095 METRIC3,
4096 MANAGED_ROUTE_TABLE_INDEX,
4097 )));
4098 let res = RouteSetResult::AddResult(Err(RouteSetError::Unauthenticated));
4099 (args, res)
4100 }
4101 RouteRequestKind::Del => {
4102 let args =
4104 RouteRequestArgs::Del(DelRouteArgs::Unicast(create_unicast_del_route_args(
4105 subnet,
4106 None,
4107 None,
4108 None,
4109 MANAGED_ROUTE_TABLE_INDEX,
4110 )));
4111 let res = RouteSetResult::DelResult(Err(RouteSetError::Unauthenticated));
4112 (args, res)
4113 }
4114 };
4115
4116 let expected_messages = Vec::new();
4118
4119 pretty_assertions::assert_eq!(
4120 test_requests(
4121 [RequestArgs::Route(route_req_args)],
4122 |interfaces_request_stream| async move {
4123 interfaces_request_stream
4124 .for_each(|req| {
4125 futures::future::ready(match req.unwrap() {
4126 fnet_root::InterfacesRequest::GetAdmin {
4127 id,
4128 control,
4129 control_handle: _,
4130 } => {
4131 pretty_assertions::assert_eq!(id, DEV1 as u64);
4132 let control = control.into_stream();
4133 let control = control.control_handle();
4134 control.shutdown();
4135 }
4136 req => unreachable!("unexpected interfaces request: {req:?}"),
4137 })
4138 })
4139 .await
4140 },
4141 route_set_for_first_new_table(vec![route_set_result]),
4142 subnet,
4143 next_hop1,
4144 next_hop2,
4145 expected_messages.len(),
4146 )
4147 .await,
4148 TestRequestResult {
4149 messages: expected_messages,
4150 waiter_results: vec![Err(RequestError::UnrecognizedInterface)],
4151 },
4152 )
4153 }
4154
4155 #[derive(Clone)]
4157 struct Route<I: Ip> {
4158 subnet: Subnet<I::Addr>,
4159 device: u32,
4160 nexthop: Option<I::Addr>,
4161 metric: Option<NonZeroU32>,
4162 }
4163
4164 impl<I: Ip> Route<I> {
4165 fn to_route(self) -> fnet_routes_ext::Route<I> {
4166 let Self { subnet, device, nexthop, metric } = self;
4167 fnet_routes_ext::Route {
4168 destination: subnet,
4169 action: fnet_routes_ext::RouteAction::Forward(fnet_routes_ext::RouteTarget {
4170 outbound_interface: device.into(),
4171 next_hop: nexthop
4172 .map(|a| SpecifiedAddr::new(a).expect("nexthop should be specified")),
4173 }),
4174 properties: fnet_routes_ext::RouteProperties {
4175 specified_properties: fnet_routes_ext::SpecifiedRouteProperties {
4176 metric: netlink_priority_to_specified_metric(metric, I::VERSION),
4177 },
4178 },
4179 }
4180 }
4181
4182 fn to_installed_route(
4183 self,
4184 table_id: fnet_routes_ext::TableId,
4185 ) -> fnet_routes_ext::InstalledRoute<I> {
4186 const DEFAULT_INTERFACE_METRIC: u32 = 100000;
4187 let effective_metric = match self.metric {
4188 None => DEFAULT_INTERFACE_METRIC,
4189 Some(metric) => metric.into(),
4190 };
4191 let route = self.to_route();
4192 fnet_routes_ext::InstalledRoute {
4193 route,
4194 effective_properties: fnet_routes_ext::EffectiveRouteProperties {
4195 metric: effective_metric,
4196 },
4197 table_id,
4198 }
4199 }
4200 }
4201
4202 const ROUTE_METRIC1: NonZeroU32 = NonZeroU32::new(METRIC1).unwrap();
4203 const ROUTE_METRIC2: NonZeroU32 = NonZeroU32::new(METRIC2).unwrap();
4204 const ROUTE_METRIC3: NonZeroU32 = NonZeroU32::new(METRIC3).unwrap();
4205
4206 #[test_case(
4207 Route::<Ipv4>{
4208 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4209 },
4210 Route::<Ipv4>{
4211 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4212 },
4213 true; "all_fields_the_same_v4_should_match")]
4214 #[test_case(
4215 Route::<Ipv6>{
4216 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4217 },
4218 Route::<Ipv6>{
4219 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4220 },
4221 true; "all_fields_the_same_v6_should_match")]
4222 #[test_case(
4223 Route::<Ipv4>{
4224 subnet: V4_DFLT, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4225 },
4226 Route::<Ipv4>{
4227 subnet: V4_DFLT, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4228 },
4229 true; "default_route_v4_should_match")]
4230 #[test_case(
4231 Route::<Ipv6>{
4232 subnet: V6_DFLT, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4233 },
4234 Route::<Ipv6>{
4235 subnet: V6_DFLT, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4236 },
4237 true; "default_route_v6_should_match")]
4238 #[test_case(
4239 Route::<Ipv4>{
4240 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4241 },
4242 Route::<Ipv4>{
4243 subnet: V4_SUB1, device: DEV2, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4244 },
4245 true; "different_device_v4_should_match")]
4246 #[test_case(
4247 Route::<Ipv6>{
4248 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4249 },
4250 Route::<Ipv6>{
4251 subnet: V6_SUB1, device: DEV2, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4252 },
4253 true; "different_device_v6_should_match")]
4254 #[test_case(
4255 Route::<Ipv4>{
4256 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4257 },
4258 Route::<Ipv4>{
4259 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP2), metric: Some(ROUTE_METRIC1),
4260 },
4261 true; "different_nexthop_v4_should_match")]
4262 #[test_case(
4263 Route::<Ipv6>{
4264 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4265 },
4266 Route::<Ipv6>{
4267 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP2), metric: Some(ROUTE_METRIC1),
4268 },
4269 true; "different_nexthop_v6_should_match")]
4270 #[test_case(
4271 Route::<Ipv4>{
4272 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4273 },
4274 Route::<Ipv4>{
4275 subnet: V4_SUB1, device: DEV2, nexthop: Some(V4_NEXTHOP2), metric: Some(ROUTE_METRIC1),
4276 },
4277 true; "different_device_and_nexthop_v4_should_match")]
4278 #[test_case(
4279 Route::<Ipv6>{
4280 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4281 },
4282 Route::<Ipv6>{
4283 subnet: V6_SUB1, device: DEV2, nexthop: Some(V6_NEXTHOP2), metric: Some(ROUTE_METRIC1),
4284 },
4285 true; "different_device_and_nexthop_v6_should_match")]
4286 #[test_case(
4287 Route::<Ipv4>{
4288 subnet: V4_SUB1, device: DEV1, nexthop: None, metric: Some(ROUTE_METRIC1),
4289 },
4290 Route::<Ipv4>{
4291 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4292 },
4293 true; "nexthop_newly_unset_v4_should_match")]
4294 #[test_case(
4295 Route::<Ipv6>{
4296 subnet: V6_SUB1, device: DEV1, nexthop: None, metric: Some(ROUTE_METRIC1),
4297 },
4298 Route::<Ipv6>{
4299 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4300 },
4301 true; "nexthop_newly_unset_v6_should_match")]
4302 #[test_case(
4303 Route::<Ipv4>{
4304 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4305 },
4306 Route::<Ipv4>{
4307 subnet: V4_SUB1, device: DEV1, nexthop: None, metric: Some(ROUTE_METRIC1),
4308 },
4309 true; "nexthop_previously_unset_v4_should_match")]
4310 #[test_case(
4311 Route::<Ipv6>{
4312 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4313 },
4314 Route::<Ipv6>{
4315 subnet: V6_SUB1, device: DEV1, nexthop: None, metric: Some(ROUTE_METRIC1),
4316 },
4317 true; "nexthop_previously_unset_v6_should_match")]
4318 #[test_case(
4319 Route::<Ipv4>{
4320 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4321 },
4322 Route::<Ipv4>{
4323 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC2),
4324 },
4325 false; "different_metric_v4_should_not_match")]
4326 #[test_case(
4327 Route::<Ipv4>{
4328 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: None,
4329 },
4330 Route::<Ipv4>{
4331 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC2),
4332 },
4333 false; "default_and_non_default_v4_should_not_match")]
4334 #[test_case(
4335 Route::<Ipv4>{
4336 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: None,
4337 },
4338 Route::<Ipv4>{
4339 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: None,
4340 },
4341 true; "default_and_default_v4_should_match")]
4342 #[test_case(
4343 Route::<Ipv6>{
4344 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4345 },
4346 Route::<Ipv6>{
4347 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC2),
4348 },
4349 false; "different_metric_v6_should_not_match")]
4350 #[test_case(
4351 Route::<Ipv4>{
4352 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4353 },
4354 Route::<Ipv4>{
4355 subnet: V4_SUB2, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4356 },
4357 false; "different_subnet_v4_should_not_match")]
4358 #[test_case(
4359 Route::<Ipv6>{
4360 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4361 },
4362 Route::<Ipv6>{
4363 subnet: V6_SUB2, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4364 },
4365 false; "different_subnet_v6_should_not_match")]
4366 #[test_case(
4367 Route::<Ipv4>{
4368 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4369 },
4370 Route::<Ipv4>{
4371 subnet: V4_SUB3, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4372 },
4373 false; "different_subnet_prefixlen_v4_should_not_match")]
4374 #[test_case(
4375 Route::<Ipv6>{
4376 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4377 },
4378 Route::<Ipv6>{
4379 subnet: V6_SUB3, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4380 },
4381 false; "different_subnet_prefixlen_v6_should_not_match")]
4382 #[test_case(
4383 Route::<Ipv6>{
4384 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: None,
4385 },
4386 Route::<Ipv6>{
4387 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC2),
4388 },
4389 false; "default_and_non_default_v6_should_not_match")]
4390 #[test_case(
4391 Route::<Ipv6>{
4392 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: None,
4393 },
4394 Route::<Ipv6>{
4395 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: None,
4396 },
4397 true; "default_and_default_v6_should_match")]
4398 fn test_new_route_matcher<I: Ip>(
4399 route1: Route<I>,
4400 route2: Route<I>,
4401 expected_to_conflict: bool,
4402 ) {
4403 let route1 = route1.to_installed_route(MAIN_FIDL_TABLE_ID);
4404 let route2 = route2.to_installed_route(MAIN_FIDL_TABLE_ID);
4405
4406 let got_conflict = routes_conflict::<I>(route1, route2.route, route2.table_id);
4407 assert_eq!(got_conflict, expected_to_conflict);
4408
4409 let got_conflict = routes_conflict::<I>(route2, route1.route, route1.table_id);
4410 assert_eq!(got_conflict, expected_to_conflict);
4411 }
4412
4413 fn test_select_route_for_deletion_helper<
4418 I: Ip + fnet_routes_ext::admin::FidlRouteAdminIpExt + fnet_routes_ext::FidlRouteIpExt,
4419 >(
4420 args: UnicastDelRouteArgs<I>,
4421 existing_routes: &[Route<I>],
4422 expected_index: Option<usize>,
4424 ) {
4425 let mut fidl_route_map = FidlRouteMap::<I>::default();
4426
4427 let _executor = fuchsia_async::TestExecutor::new();
4430
4431 let (main_route_table_proxy, _server_end) =
4432 fidl::endpoints::create_proxy::<I::RouteTableMarker>();
4433 let (own_route_table_proxy, _server_end) =
4434 fidl::endpoints::create_proxy::<I::RouteTableMarker>();
4435 let (route_set_proxy, _server_end) = fidl::endpoints::create_proxy::<I::RouteSetMarker>();
4436 let (unmanaged_route_set_proxy, _unmanaged_route_set_server_end) =
4437 fidl::endpoints::create_proxy::<I::RouteSetMarker>();
4438 let (route_table_provider, _server_end) =
4439 fidl::endpoints::create_proxy::<I::RouteTableProviderMarker>();
4440
4441 let mut route_table_map = RouteTableMap::<I>::new(
4442 main_route_table_proxy,
4443 MAIN_FIDL_TABLE_ID,
4444 unmanaged_route_set_proxy,
4445 route_table_provider,
4446 );
4447
4448 route_table_map.insert(
4449 MANAGED_ROUTE_TABLE_INDEX,
4450 RouteTable::Managed(ManagedRouteTable {
4451 route_table_proxy: own_route_table_proxy,
4452 route_set_proxy,
4453 fidl_table_id: OTHER_FIDL_TABLE_ID,
4454 rule_set_authenticated: false,
4455 }),
4456 );
4457
4458 for Route { subnet, device, nexthop, metric } in existing_routes {
4459 let fnet_routes_ext::InstalledRoute { route, effective_properties, table_id } =
4460 create_installed_route::<I>(
4461 *subnet,
4462 *nexthop,
4463 (*device).into(),
4464 metric.map_or(0, NonZeroU32::get),
4465 OTHER_FIDL_TABLE_ID,
4466 );
4467 assert_matches!(fidl_route_map.add(route, table_id, effective_properties), None);
4468 }
4469
4470 let existing_routes = existing_routes
4471 .iter()
4472 .map(|Route { subnet, device, nexthop, metric }| {
4473 let destination = (subnet.prefix() != 0).then_some(*subnet);
4475 create_netlink_route_message::<I>(
4476 subnet.prefix(),
4477 MANAGED_ROUTE_TABLE_INDEX,
4478 create_nlas::<I>(
4479 destination,
4480 nexthop.to_owned(),
4481 *device,
4482 metric.map_or(0, NonZeroU32::get),
4483 Some(MANAGED_ROUTE_TABLE_ID),
4484 ),
4485 )
4486 })
4487 .collect::<Vec<_>>();
4488 let expected_route = expected_index.map(|index| {
4489 existing_routes
4490 .get(index)
4491 .expect("index should be within the bounds of `existing_routes`")
4492 .clone()
4493 });
4494
4495 assert_eq!(
4496 select_route_for_deletion(
4497 &fidl_route_map,
4498 &route_table_map,
4499 DelRouteArgs::Unicast(args).try_into().unwrap(),
4500 ),
4501 expected_route
4502 )
4503 }
4504
4505 #[test_case(
4506 UnicastDelRouteArgs::<Ipv4> {
4507 subnet: V4_SUB1, outbound_interface: None, next_hop: None, priority: None,
4508 table: NonZeroNetlinkRouteTableIndex::new_non_zero(
4509 NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()
4510 ),
4511 },
4512 Route::<Ipv4>{
4513 subnet: V4_SUB2, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4514 },
4515 false; "subnet_does_not_match_v4")]
4516 #[test_case(
4517 UnicastDelRouteArgs::<Ipv4> {
4518 subnet: V4_SUB1, outbound_interface: None, next_hop: None, priority: None,
4519 table: NonZeroNetlinkRouteTableIndex::new_non_zero(
4520 NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()
4521 ),
4522 },
4523 Route::<Ipv4>{
4524 subnet: V4_SUB3, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4525 },
4526 false; "subnet_prefix_len_does_not_match_v4")]
4527 #[test_case(
4528 UnicastDelRouteArgs::<Ipv4> {
4529 subnet: V4_SUB1, outbound_interface: None, next_hop: None, priority: None,
4530 table: NonZeroNetlinkRouteTableIndex::new_non_zero(
4531 NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()
4532 ),
4533 },
4534 Route::<Ipv4>{
4535 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4536 },
4537 true; "subnet_matches_v4")]
4538 #[test_case(
4539 UnicastDelRouteArgs::<Ipv4> {
4540 subnet: V4_SUB1, outbound_interface: Some(NonZeroU64::new(DEV1.into()).unwrap()),
4541 next_hop: None, priority: None, table: NonZeroNetlinkRouteTableIndex::new_non_zero(
4542 NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()
4543 ),
4544 },
4545 Route::<Ipv4>{
4546 subnet: V4_SUB1, device: DEV2, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4547 },
4548 false; "interface_does_not_match_v4")]
4549 #[test_case(
4550 UnicastDelRouteArgs::<Ipv4> {
4551 subnet: V4_SUB1, outbound_interface: Some(NonZeroU64::new(DEV1.into()).unwrap()),
4552 next_hop: None, priority: None, table: NonZeroNetlinkRouteTableIndex::new_non_zero(
4553 NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()
4554 ),
4555 },
4556 Route::<Ipv4>{
4557 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4558 },
4559 true; "interface_matches_v4")]
4560 #[test_case(
4561 UnicastDelRouteArgs::<Ipv4> {
4562 subnet: V4_SUB1, outbound_interface: None,
4563 next_hop: Some(SpecifiedAddr::new(V4_NEXTHOP1).unwrap()), priority: None,
4564 table: NonZeroNetlinkRouteTableIndex::new_non_zero(
4565 NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()
4566 ),
4567 },
4568 Route::<Ipv4>{
4569 subnet: V4_SUB1, device: DEV1, nexthop: None, metric: Some(ROUTE_METRIC1),
4570 },
4571 false; "nexthop_absent_v4")]
4572 #[test_case(
4573 UnicastDelRouteArgs::<Ipv4> {
4574 subnet: V4_SUB1, outbound_interface: None,
4575 next_hop: Some(SpecifiedAddr::new(V4_NEXTHOP1).unwrap()), priority: None,
4576 table: NonZeroNetlinkRouteTableIndex::new_non_zero(
4577 NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()
4578 ),
4579 },
4580 Route::<Ipv4>{
4581 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP2), metric: Some(ROUTE_METRIC1),
4582 },
4583 false; "nexthop_does_not_match_v4")]
4584 #[test_case(
4585 UnicastDelRouteArgs::<Ipv4> {
4586 subnet: V4_SUB1, outbound_interface: None,
4587 next_hop: Some(SpecifiedAddr::new(V4_NEXTHOP1).unwrap()), priority: None,
4588 table: NonZeroNetlinkRouteTableIndex::new_non_zero(
4589 NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()
4590 ),
4591 },
4592 Route::<Ipv4>{
4593 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4594 },
4595 true; "nexthop_matches_v4")]
4596 #[test_case(
4597 UnicastDelRouteArgs::<Ipv4> {
4598 subnet: V4_SUB1, outbound_interface: None,
4599 next_hop: None, priority: Some(NonZeroU32::new(METRIC1).unwrap()),
4600 table: NonZeroNetlinkRouteTableIndex::new_non_zero(
4601 NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()
4602 ),
4603 },
4604 Route::<Ipv4>{
4605 subnet: V4_SUB1, device: DEV1, nexthop: None, metric: Some(ROUTE_METRIC2),
4606 },
4607 false; "metric_does_not_match_v4")]
4608 #[test_case(
4609 UnicastDelRouteArgs::<Ipv4> {
4610 subnet: V4_SUB1, outbound_interface: None,
4611 next_hop: None, priority: Some(NonZeroU32::new(METRIC1).unwrap()),
4612 table: NonZeroNetlinkRouteTableIndex::new_non_zero(
4613 NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()
4614 ),
4615 },
4616 Route::<Ipv4>{
4617 subnet: V4_SUB1, device: DEV1, nexthop: None, metric: Some(ROUTE_METRIC1),
4618 },
4619 true; "metric_matches_v4")]
4620 #[test_case(
4621 UnicastDelRouteArgs::<Ipv6> {
4622 subnet: V6_SUB1, outbound_interface: None, next_hop: None, priority: None,
4623 table: NonZeroNetlinkRouteTableIndex::new_non_zero(
4624 NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()
4625 ),
4626 },
4627 Route::<Ipv6>{
4628 subnet: V6_SUB2, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4629 },
4630 false; "subnet_does_not_match_v6")]
4631 #[test_case(
4632 UnicastDelRouteArgs::<Ipv6> {
4633 subnet: V6_SUB1, outbound_interface: None, next_hop: None, priority: None,
4634 table: NonZeroNetlinkRouteTableIndex::new_non_zero(
4635 NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()
4636 ),
4637 },
4638 Route::<Ipv6>{
4639 subnet: V6_SUB3, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4640 },
4641 false; "subnet_prefix_len_does_not_match_v6")]
4642 #[test_case(
4643 UnicastDelRouteArgs::<Ipv6> {
4644 subnet: V6_SUB1, outbound_interface: None, next_hop: None, priority: None,
4645 table: NonZeroNetlinkRouteTableIndex::new_non_zero(
4646 NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()
4647 ),
4648 },
4649 Route::<Ipv6>{
4650 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4651 },
4652 true; "subnet_matches_v6")]
4653 #[test_case(
4654 UnicastDelRouteArgs::<Ipv6> {
4655 subnet: V6_SUB1, outbound_interface: Some(NonZeroU64::new(DEV1.into()).unwrap()),
4656 next_hop: None, priority: None, table: NonZeroNetlinkRouteTableIndex::new_non_zero(
4657 NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()
4658 ),
4659 },
4660 Route::<Ipv6>{
4661 subnet: V6_SUB1, device: DEV2, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4662 },
4663 false; "interface_does_not_match_v6")]
4664 #[test_case(
4665 UnicastDelRouteArgs::<Ipv6> {
4666 subnet: V6_SUB1, outbound_interface: Some(NonZeroU64::new(DEV1.into()).unwrap()),
4667 next_hop: None, priority: None, table: NonZeroNetlinkRouteTableIndex::new_non_zero(
4668 NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()
4669 ),
4670 },
4671 Route::<Ipv6>{
4672 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4673 },
4674 true; "interface_matches_v6")]
4675 #[test_case(
4676 UnicastDelRouteArgs::<Ipv6> {
4677 subnet: V6_SUB1, outbound_interface: None,
4678 next_hop: Some(SpecifiedAddr::new(V6_NEXTHOP1).unwrap()), priority: None,
4679 table: NonZeroNetlinkRouteTableIndex::new_non_zero(
4680 NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()
4681 ),
4682 },
4683 Route::<Ipv6>{
4684 subnet: V6_SUB1, device: DEV1, nexthop: None, metric: Some(ROUTE_METRIC1),
4685 },
4686 false; "nexthop_absent_v6")]
4687 #[test_case(
4688 UnicastDelRouteArgs::<Ipv6> {
4689 subnet: V6_SUB1, outbound_interface: None,
4690 next_hop: Some(SpecifiedAddr::new(V6_NEXTHOP1).unwrap()), priority: None,
4691 table: NonZeroNetlinkRouteTableIndex::new_non_zero(
4692 NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()
4693 ),
4694 },
4695 Route::<Ipv6>{
4696 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP2), metric: Some(ROUTE_METRIC1),
4697 },
4698 false; "nexthop_does_not_match_v6")]
4699 #[test_case(
4700 UnicastDelRouteArgs::<Ipv6> {
4701 subnet: V6_SUB1, outbound_interface: None,
4702 next_hop: Some(SpecifiedAddr::new(V6_NEXTHOP1).unwrap()), priority: None,
4703 table: NonZeroNetlinkRouteTableIndex::new_non_zero(
4704 NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()
4705 ),
4706 },
4707 Route::<Ipv6>{
4708 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4709 },
4710 true; "nexthop_matches_v6")]
4711 #[test_case(
4712 UnicastDelRouteArgs::<Ipv6> {
4713 subnet: V6_SUB1, outbound_interface: None,
4714 next_hop: None, priority: Some(NonZeroU32::new(METRIC1).unwrap()),
4715 table: NonZeroNetlinkRouteTableIndex::new_non_zero(
4716 NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()
4717 ),
4718 },
4719 Route::<Ipv6>{
4720 subnet: V6_SUB1, device: DEV1, nexthop: None, metric: Some(ROUTE_METRIC2),
4721 },
4722 false; "metric_does_not_match_v6")]
4723 #[test_case(
4724 UnicastDelRouteArgs::<Ipv6> {
4725 subnet: V6_SUB1, outbound_interface: None,
4726 next_hop: None, priority: Some(NonZeroU32::new(METRIC1).unwrap()),
4727 table: NonZeroNetlinkRouteTableIndex::new_non_zero(
4728 NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()
4729 ),
4730 },
4731 Route::<Ipv6>{
4732 subnet: V6_SUB1, device: DEV1, nexthop: None, metric: Some(ROUTE_METRIC1),
4733 },
4734 true; "metric_matches_v6")]
4735 fn test_select_route_for_deletion<
4736 I: Ip + fnet_routes_ext::admin::FidlRouteAdminIpExt + fnet_routes_ext::FidlRouteIpExt,
4737 >(
4738 args: UnicastDelRouteArgs<I>,
4739 existing_route: Route<I>,
4740 expect_match: bool,
4741 ) {
4742 test_select_route_for_deletion_helper(args, &[existing_route], expect_match.then_some(0))
4743 }
4744
4745 #[test_case(
4746 UnicastDelRouteArgs::<Ipv4> {
4747 subnet: V4_SUB1, outbound_interface: None, next_hop: None, priority: None,
4748 table: NonZeroNetlinkRouteTableIndex::new_non_zero(NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()),
4749 },
4750 &[
4751 Route::<Ipv4>{
4752 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC2),
4753 },
4754 Route::<Ipv4>{
4755 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4756 },
4757 Route::<Ipv4>{
4758 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC3),
4759 },
4760 ],
4761 Some(1); "multiple_matches_prefers_lowest_metric_v4")]
4762 #[test_case(
4763 UnicastDelRouteArgs::<Ipv6> {
4764 subnet: V6_SUB1, outbound_interface: None, next_hop: None, priority: None,
4765 table: NonZeroNetlinkRouteTableIndex::new_non_zero(NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()),
4766 },
4767 &[
4768 Route::<Ipv6>{
4769 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC2),
4770 },
4771 Route::<Ipv6>{
4772 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4773 },
4774 Route::<Ipv6>{
4775 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC3),
4776 },
4777 ],
4778 Some(1); "multiple_matches_prefers_lowest_metric_v6")]
4779 fn test_select_route_for_deletion_multiple_matches<
4780 I: Ip + fnet_routes_ext::admin::FidlRouteAdminIpExt + fnet_routes_ext::FidlRouteIpExt,
4781 >(
4782 args: UnicastDelRouteArgs<I>,
4783 existing_routes: &[Route<I>],
4784 expected_index: Option<usize>,
4785 ) {
4786 test_select_route_for_deletion_helper(args, existing_routes, expected_index);
4787 }
4788
4789 #[ip_test(I, test = false)]
4790 #[fuchsia::test]
4791 async fn garbage_collects_empty_table<
4792 I: Ip + fnet_routes_ext::admin::FidlRouteAdminIpExt + fnet_routes_ext::FidlRouteIpExt,
4793 >() {
4794 let (_route_sink, route_client, async_work_drain_task) =
4795 crate::client::testutil::new_fake_client::<NetlinkRoute>(
4796 crate::client::testutil::CLIENT_ID_1,
4797 [ModernGroup(match I::VERSION {
4798 IpVersion::V4 => rtnetlink_groups_RTNLGRP_IPV4_ROUTE,
4799 IpVersion::V6 => rtnetlink_groups_RTNLGRP_IPV6_ROUTE,
4800 })],
4801 );
4802 let join_handle = fasync::Task::spawn(async_work_drain_task);
4803 {
4804 let route_client = route_client;
4806 let route_clients = ClientTable::default();
4807 route_clients.add_client(route_client.clone());
4808
4809 let Setup {
4810 event_loop_inputs,
4811 watcher_stream,
4812 route_sets: (main_route_table_server_end, route_table_provider_server_end),
4813 interfaces_request_stream: _,
4814 mut request_sink,
4815 async_work_sink: _,
4816 } = setup_with_route_clients_yielding_admin_server_ends::<I>(route_clients);
4817
4818 let mut main_route_table_fut = pin!(
4819 fnet_routes_ext::testutil::admin::serve_noop_route_sets_with_table_id::<I>(
4820 main_route_table_server_end,
4821 MAIN_FIDL_TABLE_ID
4822 )
4823 .fuse()
4824 );
4825
4826 let mut watcher_stream = pin!(watcher_stream.fuse());
4827 let mut route_table_provider_stream = route_table_provider_server_end.into_stream();
4828
4829 let mut event_loop = {
4830 let included_workers = match I::VERSION {
4831 IpVersion::V4 => crate::route_eventloop::IncludedWorkers {
4832 routes_v4: EventLoopComponent::Present(()),
4833 routes_v6: EventLoopComponent::Absent(Optional),
4834 interfaces: EventLoopComponent::Absent(Optional),
4835 rules_v4: EventLoopComponent::Absent(Optional),
4836 rules_v6: EventLoopComponent::Absent(Optional),
4837 nduseropt: EventLoopComponent::Absent(Optional),
4838 neighbors: EventLoopComponent::Absent(Optional),
4839 },
4840 IpVersion::V6 => crate::route_eventloop::IncludedWorkers {
4841 routes_v4: EventLoopComponent::Absent(Optional),
4842 routes_v6: EventLoopComponent::Present(()),
4843 interfaces: EventLoopComponent::Absent(Optional),
4844 rules_v4: EventLoopComponent::Absent(Optional),
4845 rules_v6: EventLoopComponent::Absent(Optional),
4846 nduseropt: EventLoopComponent::Absent(Optional),
4847 neighbors: EventLoopComponent::Absent(Optional),
4848 },
4849 };
4850
4851 let event_loop_fut = event_loop_inputs.initialize(included_workers).fuse();
4852 let watcher_fut = async {
4853 let watch_req =
4854 watcher_stream.by_ref().next().await.expect("should not have ended");
4855 fnet_routes_ext::testutil::handle_watch::<I>(
4857 watch_req,
4858 vec![fnet_routes_ext::Event::<I>::Idle.try_into().unwrap()],
4859 )
4860 }
4861 .fuse();
4862
4863 futures::select! {
4864 () = main_route_table_fut => unreachable!(),
4865 (event_loop, ()) = futures::future::join(
4866 event_loop_fut, watcher_fut
4867 ) => event_loop,
4868 }
4869 };
4870
4871 let (completer, mut initial_add_request_waiter) = oneshot::channel();
4872
4873 let new_route_args = NewRouteArgs::Unicast(I::map_ip_out(
4874 (),
4875 |()| {
4876 create_unicast_new_route_args(
4877 V4_SUB1,
4878 V4_NEXTHOP1,
4879 DEV1.into(),
4880 METRIC1,
4881 MANAGED_ROUTE_TABLE_INDEX,
4882 )
4883 },
4884 |()| {
4885 create_unicast_new_route_args(
4886 V6_SUB1,
4887 V6_NEXTHOP1,
4888 DEV1.into(),
4889 METRIC1,
4890 MANAGED_ROUTE_TABLE_INDEX,
4891 )
4892 },
4893 ));
4894 let expected_route = fnet_routes_ext::Route::<I>::from(new_route_args);
4895
4896 request_sink
4898 .try_send(
4899 Request {
4900 args: RequestArgs::Route(RouteRequestArgs::New(new_route_args)),
4901 sequence_number: TEST_SEQUENCE_NUMBER,
4902 client: route_client.clone(),
4903 completer,
4904 }
4905 .into(),
4906 )
4907 .expect("should succeed");
4908
4909 let (mut route_table_stream, mut route_set_stream) = {
4912 let event_loop_fut = event_loop.run_one_step_in_tests().fuse();
4913 let route_table_fut = async {
4914 let server_end = match I::into_route_table_provider_request(
4915 route_table_provider_stream
4916 .try_next()
4917 .await
4918 .expect("should not have ended")
4919 .expect("fidl error"),
4920 ) {
4921 fnet_routes_ext::admin::RouteTableProviderRequest::NewRouteTable {
4922 provider,
4923 options: _,
4924 control_handle: _,
4925 } => provider,
4926 r => panic!("unexpected request {r:?}"),
4927 };
4928 let mut route_table_stream = server_end.into_stream().boxed().fuse();
4929
4930 let request = I::into_route_table_request_result(
4931 route_table_stream.by_ref().next().await.expect("should not have ended"),
4932 )
4933 .expect("should not get error");
4934
4935 let responder = match request {
4936 RouteTableRequest::GetTableId { responder } => responder,
4937 _ => panic!("should be GetTableId"),
4938 };
4939 responder.send(OTHER_FIDL_TABLE_ID.get()).expect("should succeed");
4940
4941 let request = I::into_route_table_request_result(
4942 route_table_stream.by_ref().next().await.expect("should not have ended"),
4943 )
4944 .expect("should not get error");
4945
4946 let server_end = match request {
4947 RouteTableRequest::NewRouteSet { route_set, control_handle: _ } => {
4948 route_set
4949 }
4950 _ => panic!("should be NewRouteSet"),
4951 };
4952 let mut route_set_stream = server_end.into_stream().boxed().fuse();
4953
4954 let request = I::into_route_set_request_result(
4955 route_set_stream.by_ref().next().await.expect("should not have ended"),
4956 )
4957 .expect("should not get error");
4958
4959 let (route, responder) = match request {
4960 RouteSetRequest::AddRoute { route, responder } => (route, responder),
4961 _ => panic!("should be AddRoute"),
4962 };
4963 let route = route.expect("should successfully convert FIDl");
4964 assert_eq!(route, expected_route);
4965
4966 responder.send(Ok(true)).expect("sending response should succeed");
4967 (route_table_stream, route_set_stream)
4968 }
4969 .fuse();
4970 futures::select! {
4971 () = main_route_table_fut => unreachable!(),
4972 ((), streams) = futures::future::join(event_loop_fut, route_table_fut) => {
4973 streams
4974 }
4975 }
4976 };
4977
4978 {
4979 let (routes_worker, route_table_map) = event_loop.route_table_state::<I>();
4980 let table = match route_table_map.get(&MANAGED_ROUTE_TABLE_INDEX) {
4982 Some(RouteTable::Managed(table)) => table,
4983 _ => panic!("table should be present"),
4984 };
4985 assert_eq!(table.fidl_table_id, OTHER_FIDL_TABLE_ID);
4986
4987 assert!(routes_worker.fidl_route_map.route_is_uninstalled_in_tables(
4990 &expected_route,
4991 [&OTHER_FIDL_TABLE_ID, &MAIN_FIDL_TABLE_ID]
4992 ));
4993 }
4994
4995 assert_matches!(initial_add_request_waiter.try_recv(), Ok(None));
4997
4998 {
5000 let event_loop_fut = async {
5001 event_loop.run_one_step_in_tests().await;
5003 event_loop.run_one_step_in_tests().await;
5004 }
5005 .fuse();
5006 let watcher_fut = async {
5007 let watch_req =
5008 watcher_stream.by_ref().next().await.expect("should not have ended");
5009 fnet_routes_ext::testutil::handle_watch::<I>(
5011 watch_req,
5012 vec![
5013 fnet_routes_ext::Event::<I>::Added(fnet_routes_ext::InstalledRoute {
5014 route: expected_route,
5015 effective_properties: fnet_routes_ext::EffectiveRouteProperties {
5016 metric: METRIC1,
5017 },
5018 table_id: MAIN_FIDL_TABLE_ID,
5019 })
5020 .try_into()
5021 .unwrap(),
5022 fnet_routes_ext::Event::<I>::Added(fnet_routes_ext::InstalledRoute {
5023 route: expected_route,
5024 effective_properties: fnet_routes_ext::EffectiveRouteProperties {
5025 metric: METRIC1,
5026 },
5027 table_id: OTHER_FIDL_TABLE_ID,
5028 })
5029 .try_into()
5030 .unwrap(),
5031 ],
5032 );
5033 };
5034 let ((), ()) = futures::join!(event_loop_fut, watcher_fut);
5035 }
5036
5037 {
5038 let (routes_worker, _route_table_map) = event_loop.route_table_state::<I>();
5039
5040 assert!(routes_worker.fidl_route_map.route_is_installed_in_tables(
5042 &expected_route,
5043 [&OTHER_FIDL_TABLE_ID, &MAIN_FIDL_TABLE_ID]
5044 ));
5045 }
5046
5047 assert_matches!(initial_add_request_waiter.try_recv(), Ok(Some(Ok(()))));
5048
5049 let (completer, mut del_request_waiter) = oneshot::channel();
5050
5051 let del_route_args = I::map_ip_out(
5052 (),
5053 |()| {
5054 create_unicast_del_route_args(
5055 V4_SUB1,
5056 Some(V4_NEXTHOP1),
5057 Some(DEV1.into()),
5058 Some(METRIC1),
5059 MANAGED_ROUTE_TABLE_INDEX,
5060 )
5061 },
5062 |()| {
5063 create_unicast_del_route_args(
5064 V6_SUB1,
5065 Some(V6_NEXTHOP1),
5066 Some(DEV1.into()),
5067 Some(METRIC1),
5068 MANAGED_ROUTE_TABLE_INDEX,
5069 )
5070 },
5071 );
5072
5073 request_sink
5075 .try_send(
5076 Request {
5077 args: RequestArgs::Route(RouteRequestArgs::Del(DelRouteArgs::Unicast(
5078 del_route_args,
5079 ))),
5080 sequence_number: TEST_SEQUENCE_NUMBER,
5081 client: route_client.clone(),
5082 completer,
5083 }
5084 .into(),
5085 )
5086 .expect("should succeed");
5087
5088 {
5090 let event_loop_fut = event_loop.run_one_step_in_tests().fuse();
5091 let route_set_fut = async {
5092 let request = I::into_route_set_request_result(
5093 route_set_stream.next().await.expect("should not have ended"),
5094 )
5095 .expect("should not get error");
5096 let (route, responder) = match request {
5097 RouteSetRequest::RemoveRoute { route, responder } => (route, responder),
5098 _ => panic!("should be DelRoute"),
5099 };
5100 let route = route.expect("should successfully convert FIDl");
5101 assert_eq!(route, expected_route);
5102
5103 responder.send(Ok(true)).expect("sending response should succeed");
5104 }
5105 .fuse();
5106
5107 futures::select! {
5108 () = main_route_table_fut => unreachable!(),
5109 ((), ()) = futures::future::join(event_loop_fut, route_set_fut) => (),
5110 }
5111 }
5112
5113 {
5115 let (routes_worker, route_table_map) = event_loop.route_table_state::<I>();
5116 let table = match route_table_map.get(&MANAGED_ROUTE_TABLE_INDEX) {
5118 Some(RouteTable::Managed(table)) => table,
5119 _ => panic!("table should be present"),
5120 };
5121 assert_eq!(table.fidl_table_id, OTHER_FIDL_TABLE_ID);
5122
5123 assert!(routes_worker.fidl_route_map.route_is_installed_in_tables(
5124 &expected_route,
5125 [&OTHER_FIDL_TABLE_ID, &MAIN_FIDL_TABLE_ID]
5126 ));
5127 }
5128 assert_matches!(del_request_waiter.try_recv(), Ok(None));
5129
5130 {
5132 let event_loop_fut = async {
5133 event_loop.run_one_step_in_tests().await;
5135 event_loop.run_one_step_in_tests().await;
5136 }
5137 .fuse();
5138 let watcher_fut = async {
5139 let watch_req =
5140 watcher_stream.by_ref().next().await.expect("should not have ended");
5141 fnet_routes_ext::testutil::handle_watch::<I>(
5143 watch_req,
5144 vec![
5145 fnet_routes_ext::Event::<I>::Removed(fnet_routes_ext::InstalledRoute {
5146 route: expected_route,
5147 effective_properties: fnet_routes_ext::EffectiveRouteProperties {
5148 metric: 0,
5149 },
5150 table_id: MAIN_FIDL_TABLE_ID,
5151 })
5152 .try_into()
5153 .unwrap(),
5154 fnet_routes_ext::Event::<I>::Removed(fnet_routes_ext::InstalledRoute {
5155 route: expected_route,
5156 effective_properties: fnet_routes_ext::EffectiveRouteProperties {
5157 metric: 0,
5158 },
5159 table_id: OTHER_FIDL_TABLE_ID,
5160 })
5161 .try_into()
5162 .unwrap(),
5163 ],
5164 );
5165 };
5166 let ((), ()) = futures::join!(event_loop_fut, watcher_fut);
5167 }
5168
5169 {
5170 let (routes_worker, route_table_map) = event_loop.route_table_state::<I>();
5171
5172 assert!(routes_worker.fidl_route_map.route_is_uninstalled_in_tables(
5174 &expected_route,
5175 [&OTHER_FIDL_TABLE_ID, &MAIN_FIDL_TABLE_ID]
5176 ));
5177
5178 assert_matches!(route_table_map.get(&MANAGED_ROUTE_TABLE_INDEX), None);
5180 }
5181 assert_matches!(del_request_waiter.try_recv(), Ok(Some(Ok(()))));
5182
5183 let route_table_request = route_table_stream.next().await;
5186 assert!(route_table_request.is_none());
5187 };
5188 join_handle.await;
5189 }
5190}