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