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 type NeighborWorker = Optional;
2166 }
2167
2168 struct Setup<W, R> {
2169 pub event_loop_inputs: crate::route_eventloop::EventLoopInputs<
2170 FakeInterfacesHandler,
2171 FakeSender<RouteNetlinkMessage>,
2172 OnlyRoutes,
2173 >,
2174 pub watcher_stream: W,
2175 pub route_sets: R,
2176 pub interfaces_request_stream: fnet_root::InterfacesRequestStream,
2177 pub request_sink:
2178 mpsc::Sender<crate::route_eventloop::UnifiedRequest<FakeSender<RouteNetlinkMessage>>>,
2179 pub async_work_sink: mpsc::UnboundedSender<AsyncWorkItem<NetlinkRoute>>,
2180 }
2181
2182 fn setup_with_route_clients_yielding_admin_server_ends<
2183 I: Ip + fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
2184 >(
2185 route_clients: ClientTable<NetlinkRoute, FakeSender<RouteNetlinkMessage>>,
2186 ) -> Setup<
2187 impl Stream<Item = <<I::WatcherMarker as ProtocolMarker>::RequestStream as Stream>::Item>,
2188 (ServerEnd<I::RouteTableMarker>, ServerEnd<I::RouteTableProviderMarker>),
2189 > {
2190 let (interfaces_handler, _interfaces_handler_sink) = FakeInterfacesHandler::new();
2191 let (request_sink, request_stream) = mpsc::channel(1);
2192 let (interfaces_proxy, interfaces) =
2193 fidl::endpoints::create_proxy::<fnet_root::InterfacesMarker>();
2194 let (async_work_sink, async_work_receiver) = mpsc::unbounded();
2195
2196 #[derive(GenericOverIp)]
2197 #[generic_over_ip(I, Ip)]
2198 struct ServerEnds<
2199 I: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
2200 > {
2201 routes_state: ServerEnd<I::StateMarker>,
2202 routes_set_provider: ServerEnd<I::RouteTableMarker>,
2203 route_table_provider: ServerEnd<I::RouteTableProviderMarker>,
2204 }
2205
2206 let base_inputs = crate::route_eventloop::EventLoopInputs {
2207 interfaces_handler: EventLoopComponent::Present(interfaces_handler),
2208 route_clients: EventLoopComponent::Present(route_clients),
2209 interfaces_proxy: EventLoopComponent::Present(interfaces_proxy),
2210 async_work_receiver,
2211
2212 interfaces_state_proxy: EventLoopComponent::Absent(Optional),
2213 v4_routes_state: EventLoopComponent::Absent(Optional),
2214 v6_routes_state: EventLoopComponent::Absent(Optional),
2215 v4_main_route_table: EventLoopComponent::Absent(Optional),
2216 v6_main_route_table: EventLoopComponent::Absent(Optional),
2217 v4_route_table_provider: EventLoopComponent::Absent(Optional),
2218 v6_route_table_provider: EventLoopComponent::Absent(Optional),
2219 v4_rule_table: EventLoopComponent::Absent(Optional),
2220 v6_rule_table: EventLoopComponent::Absent(Optional),
2221 ndp_option_watcher_provider: EventLoopComponent::Absent(Optional),
2222 neighbors_view: EventLoopComponent::Absent(Optional),
2223
2224 unified_request_stream: request_stream,
2225 };
2226
2227 let (IpInvariant(inputs), server_ends) = I::map_ip_out(
2228 base_inputs,
2229 |base_inputs| {
2230 let (v4_routes_state, routes_state) =
2231 fidl::endpoints::create_proxy::<fnet_routes::StateV4Marker>();
2232 let (v4_main_route_table, routes_set_provider) =
2233 fidl::endpoints::create_proxy::<fnet_routes_admin::RouteTableV4Marker>();
2234 let (v4_route_table_provider, route_table_provider) = fidl::endpoints::create_proxy::<
2235 fnet_routes_admin::RouteTableProviderV4Marker,
2236 >();
2237 let inputs = crate::route_eventloop::EventLoopInputs {
2238 v4_routes_state: EventLoopComponent::Present(v4_routes_state),
2239 v4_main_route_table: EventLoopComponent::Present(v4_main_route_table),
2240 v4_route_table_provider: EventLoopComponent::Present(v4_route_table_provider),
2241 ..base_inputs
2242 };
2243 let server_ends =
2244 ServerEnds::<Ipv4> { routes_state, routes_set_provider, route_table_provider };
2245 (IpInvariant(inputs), server_ends)
2246 },
2247 |base_inputs| {
2248 let (v6_routes_state, routes_state) =
2249 fidl::endpoints::create_proxy::<fnet_routes::StateV6Marker>();
2250 let (v6_main_route_table, routes_set_provider) =
2251 fidl::endpoints::create_proxy::<fnet_routes_admin::RouteTableV6Marker>();
2252 let (v6_route_table_provider, route_table_provider) = fidl::endpoints::create_proxy::<
2253 fnet_routes_admin::RouteTableProviderV6Marker,
2254 >();
2255 let inputs = crate::route_eventloop::EventLoopInputs {
2256 v6_routes_state: EventLoopComponent::Present(v6_routes_state),
2257 v6_main_route_table: EventLoopComponent::Present(v6_main_route_table),
2258 v6_route_table_provider: EventLoopComponent::Present(v6_route_table_provider),
2259 ..base_inputs
2260 };
2261 let server_ends =
2262 ServerEnds::<Ipv6> { routes_state, routes_set_provider, route_table_provider };
2263 (IpInvariant(inputs), server_ends)
2264 },
2265 );
2266
2267 let ServerEnds { routes_state, routes_set_provider, route_table_provider } = server_ends;
2268
2269 let state_stream = routes_state.into_stream().boxed_local();
2270
2271 let interfaces_request_stream = interfaces.into_stream();
2272
2273 #[derive(GenericOverIp)]
2274 #[generic_over_ip(I, Ip)]
2275 struct StateRequestWrapper<I: fnet_routes_ext::FidlRouteIpExt> {
2276 request: <<I::StateMarker as ProtocolMarker>::RequestStream as futures::Stream>::Item,
2277 }
2278
2279 #[derive(GenericOverIp)]
2280 #[generic_over_ip(I, Ip)]
2281 struct WatcherRequestWrapper<I: fnet_routes_ext::FidlRouteIpExt> {
2282 watcher: <I::WatcherMarker as ProtocolMarker>::RequestStream,
2283 }
2284
2285 let watcher_stream = state_stream
2286 .map(|request| {
2287 let wrapper = I::map_ip(
2288 StateRequestWrapper { request },
2289 |StateRequestWrapper { request }| match request.expect("watcher stream error") {
2290 fnet_routes::StateV4Request::GetWatcherV4 {
2291 options: _,
2292 watcher,
2293 control_handle: _,
2294 } => WatcherRequestWrapper { watcher: watcher.into_stream() },
2295 fnet_routes::StateV4Request::GetRuleWatcherV4 {
2296 options: _,
2297 watcher: _,
2298 control_handle: _,
2299 } => todo!("TODO(https://fxbug.dev/336204757): Implement rules watcher"),
2300 },
2301 |StateRequestWrapper { request }| match request.expect("watcher stream error") {
2302 fnet_routes::StateV6Request::GetWatcherV6 {
2303 options: _,
2304 watcher,
2305 control_handle: _,
2306 } => WatcherRequestWrapper { watcher: watcher.into_stream() },
2307 fnet_routes::StateV6Request::GetRuleWatcherV6 {
2308 options: _,
2309 watcher: _,
2310 control_handle: _,
2311 } => todo!("TODO(https://fxbug.dev/336204757): Implement rules watcher"),
2312 },
2313 );
2314 wrapper
2315 })
2316 .map(|WatcherRequestWrapper { watcher }| watcher)
2317 .flatten()
2320 .fuse();
2321
2322 Setup {
2323 event_loop_inputs: inputs,
2324 watcher_stream,
2325 route_sets: (routes_set_provider, route_table_provider),
2326 interfaces_request_stream,
2327 request_sink,
2328 async_work_sink,
2329 }
2330 }
2331
2332 fn setup_with_route_clients<
2333 I: Ip + fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
2334 >(
2335 route_clients: ClientTable<NetlinkRoute, FakeSender<RouteNetlinkMessage>>,
2336 ) -> Setup<
2337 impl Stream<Item = <<I::WatcherMarker as ProtocolMarker>::RequestStream as Stream>::Item>,
2338 impl Stream<
2339 Item = (
2340 fnet_routes_ext::TableId,
2341 <<I::RouteSetMarker as ProtocolMarker>::RequestStream as Stream>::Item,
2342 ),
2343 >,
2344 > {
2345 let Setup {
2346 event_loop_inputs,
2347 watcher_stream,
2348 route_sets: (routes_set_provider, route_table_provider),
2349 interfaces_request_stream,
2350 request_sink,
2351 async_work_sink,
2352 } = setup_with_route_clients_yielding_admin_server_ends::<I>(route_clients);
2353 let route_set_stream =
2354 fnet_routes_ext::testutil::admin::serve_all_route_sets_with_table_id::<I>(
2355 routes_set_provider,
2356 Some(MAIN_FIDL_TABLE_ID),
2357 )
2358 .map(|item| (MAIN_FIDL_TABLE_ID, item));
2359
2360 let route_table_provider_request_stream = route_table_provider.into_stream();
2361
2362 let table_id = AtomicU32::new(OTHER_FIDL_TABLE_ID.get());
2363
2364 let route_sets_from_route_table_provider =
2365 futures::TryStreamExt::map_ok(route_table_provider_request_stream, move |request| {
2366 match I::into_route_table_provider_request(request) {
2367 fnet_routes_ext::admin::RouteTableProviderRequest::NewRouteTable {
2368 provider,
2369 options: _,
2370 control_handle: _,
2371 } => {
2372 let table_id =
2373 fnet_routes_ext::TableId::new(table_id.fetch_add(1, Ordering::SeqCst));
2374 fnet_routes_ext::testutil::admin::serve_all_route_sets_with_table_id::<I>(
2375 provider,
2376 Some(table_id),
2377 )
2378 .map(move |route_set_request| (table_id, route_set_request))
2379 }
2380 r => panic!("unexpected request {r:?}"),
2381 }
2382 })
2383 .map(|result| result.expect("should not get FIDL error"))
2384 .flatten_unordered(None)
2385 .fuse();
2386 let route_set_stream = futures::stream::select_all([
2387 route_set_stream.left_stream(),
2388 route_sets_from_route_table_provider.right_stream(),
2389 ])
2390 .fuse();
2391
2392 Setup {
2393 event_loop_inputs,
2394 watcher_stream,
2395 route_sets: route_set_stream,
2396 interfaces_request_stream,
2397 request_sink,
2398 async_work_sink,
2399 }
2400 }
2401
2402 async fn respond_to_watcher<
2403 I: fnet_routes_ext::FidlRouteIpExt,
2404 S: Stream<Item = <<I::WatcherMarker as ProtocolMarker>::RequestStream as Stream>::Item>,
2405 >(
2406 stream: S,
2407 updates: impl IntoIterator<Item = I::WatchEvent>,
2408 ) {
2409 #[derive(GenericOverIp)]
2410 #[generic_over_ip(I, Ip)]
2411 struct HandleInputs<I: fnet_routes_ext::FidlRouteIpExt> {
2412 request: <<I::WatcherMarker as ProtocolMarker>::RequestStream as Stream>::Item,
2413 update: I::WatchEvent,
2414 }
2415 stream
2416 .zip(futures::stream::iter(updates.into_iter()))
2417 .for_each(|(request, update)| async move {
2418 I::map_ip_in(
2419 HandleInputs { request, update },
2420 |HandleInputs { request, update }| match request
2421 .expect("failed to receive `Watch` request")
2422 {
2423 fnet_routes::WatcherV4Request::Watch { responder } => {
2424 responder.send(&[update]).expect("failed to respond to `Watch`")
2425 }
2426 },
2427 |HandleInputs { request, update }| match request
2428 .expect("failed to receive `Watch` request")
2429 {
2430 fnet_routes::WatcherV6Request::Watch { responder } => {
2431 responder.send(&[update]).expect("failed to respond to `Watch`")
2432 }
2433 },
2434 );
2435 })
2436 .await;
2437 }
2438
2439 async fn run_event_loop<I: Ip>(
2440 inputs: crate::route_eventloop::EventLoopInputs<
2441 FakeInterfacesHandler,
2442 FakeSender<RouteNetlinkMessage>,
2443 OnlyRoutes,
2444 >,
2445 ) -> Never {
2446 let included_workers = match I::VERSION {
2447 IpVersion::V4 => crate::route_eventloop::IncludedWorkers {
2448 routes_v4: EventLoopComponent::Present(()),
2449 routes_v6: EventLoopComponent::Absent(Optional),
2450 interfaces: EventLoopComponent::Absent(Optional),
2451 rules_v4: EventLoopComponent::Absent(Optional),
2452 rules_v6: EventLoopComponent::Absent(Optional),
2453 nduseropt: EventLoopComponent::Absent(Optional),
2454 neighbors: EventLoopComponent::Absent(Optional),
2455 },
2456 IpVersion::V6 => crate::route_eventloop::IncludedWorkers {
2457 routes_v4: EventLoopComponent::Absent(Optional),
2458 routes_v6: EventLoopComponent::Present(()),
2459 interfaces: EventLoopComponent::Absent(Optional),
2460 rules_v4: EventLoopComponent::Absent(Optional),
2461 rules_v6: EventLoopComponent::Absent(Optional),
2462 nduseropt: EventLoopComponent::Absent(Optional),
2463 neighbors: EventLoopComponent::Absent(Optional),
2464 },
2465 };
2466
2467 let event_loop = inputs.initialize(included_workers).await;
2468 event_loop.run().await
2469 }
2470
2471 fn get_test_route_events_new_route_args<A: IpAddress>(
2472 subnet: Subnet<A>,
2473 next_hop1: A,
2474 next_hop2: A,
2475 ) -> [RequestArgs<A::Version>; 2]
2476 where
2477 A::Version: fnet_routes_ext::FidlRouteIpExt,
2478 {
2479 [
2480 RequestArgs::Route(RouteRequestArgs::New(NewRouteArgs::Unicast(
2481 create_unicast_new_route_args(
2482 subnet,
2483 next_hop1,
2484 DEV1.into(),
2485 METRIC1,
2486 MANAGED_ROUTE_TABLE_INDEX,
2487 ),
2488 ))),
2489 RequestArgs::Route(RouteRequestArgs::New(NewRouteArgs::Unicast(
2490 create_unicast_new_route_args(
2491 subnet,
2492 next_hop2,
2493 DEV2.into(),
2494 METRIC2,
2495 MANAGED_ROUTE_TABLE_INDEX,
2496 ),
2497 ))),
2498 ]
2499 }
2500
2501 fn create_unicast_new_route_args<A: IpAddress>(
2502 subnet: Subnet<A>,
2503 next_hop: A,
2504 interface_id: u64,
2505 priority: u32,
2506 table: NetlinkRouteTableIndex,
2507 ) -> UnicastNewRouteArgs<A::Version> {
2508 UnicastNewRouteArgs {
2509 subnet,
2510 target: fnet_routes_ext::RouteTarget {
2511 outbound_interface: interface_id,
2512 next_hop: SpecifiedAddr::new(next_hop),
2513 },
2514 priority: NonZeroU32::new(priority),
2515 table,
2516 }
2517 }
2518
2519 fn create_unicast_del_route_args<A: IpAddress>(
2520 subnet: Subnet<A>,
2521 next_hop: Option<A>,
2522 interface_id: Option<u64>,
2523 priority: Option<u32>,
2524 table: NetlinkRouteTableIndex,
2525 ) -> UnicastDelRouteArgs<A::Version> {
2526 UnicastDelRouteArgs {
2527 subnet,
2528 outbound_interface: interface_id.map(NonZeroU64::new).flatten(),
2529 next_hop: next_hop.map(SpecifiedAddr::new).flatten(),
2530 priority: priority.map(NonZeroU32::new).flatten(),
2531 table: NonZeroNetlinkRouteTableIndex::new(table).unwrap(),
2532 }
2533 }
2534
2535 #[derive(Debug, PartialEq)]
2536 struct TestRequestResult {
2537 messages: Vec<SentMessage<RouteNetlinkMessage>>,
2538 waiter_results: Vec<Result<(), RequestError>>,
2539 }
2540
2541 async fn test_requests<
2547 A: IpAddress,
2548 Fut: Future<Output = ()>,
2549 F: FnOnce(fnet_root::InterfacesRequestStream) -> Fut,
2550 >(
2551 args: impl IntoIterator<Item = RequestArgs<A::Version>>,
2552 root_handler: F,
2553 route_set_results: HashMap<fnet_routes_ext::TableId, VecDeque<RouteSetResult>>,
2554 subnet: Subnet<A>,
2555 next_hop1: A,
2556 next_hop2: A,
2557 num_sink_messages: usize,
2558 ) -> TestRequestResult
2559 where
2560 A::Version: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
2561 {
2562 let scope = fasync::Scope::new();
2563 let result = {
2564 let (mut route_sink, route_client, async_work_drain_task) =
2565 crate::client::testutil::new_fake_client::<NetlinkRoute>(
2566 crate::client::testutil::CLIENT_ID_1,
2567 [ModernGroup(match A::Version::VERSION {
2568 IpVersion::V4 => rtnetlink_groups_RTNLGRP_IPV4_ROUTE,
2569 IpVersion::V6 => rtnetlink_groups_RTNLGRP_IPV6_ROUTE,
2570 })],
2571 );
2572 let _join_handle = scope.spawn(async_work_drain_task);
2573 let (mut other_sink, other_client, async_work_drain_task) =
2574 crate::client::testutil::new_fake_client::<NetlinkRoute>(
2575 crate::client::testutil::CLIENT_ID_2,
2576 [ModernGroup(rtnetlink_groups_RTNLGRP_LINK)],
2577 );
2578 let _join_handle = scope.spawn(async_work_drain_task);
2579 let Setup {
2580 event_loop_inputs,
2581 mut watcher_stream,
2582 route_sets: mut route_set_stream,
2583 interfaces_request_stream,
2584 request_sink,
2585 async_work_sink: _,
2586 } = setup_with_route_clients::<A::Version>({
2587 let route_clients = ClientTable::default();
2588 route_clients.add_client(route_client.clone());
2589 route_clients.add_client(other_client);
2590 route_clients
2591 });
2592
2593 let mut event_loop_fut = pin!(run_event_loop::<A::Version>(event_loop_inputs).fuse());
2594
2595 let watcher_stream_fut = respond_to_watcher::<A::Version, _>(
2596 watcher_stream.by_ref(),
2597 std::iter::once(fnet_routes_ext::Event::<A::Version>::Idle.try_into().unwrap()),
2598 );
2599 futures::select! {
2600 () = watcher_stream_fut.fuse() => {},
2601 err = event_loop_fut => unreachable!("eventloop should not return: {err:?}"),
2602 }
2603 assert_eq!(&route_sink.take_messages()[..], &[]);
2604 assert_eq!(&other_sink.take_messages()[..], &[]);
2605
2606 let route_client = &route_client;
2607 let fut = async {
2608 let initial_new_routes =
2610 get_test_route_events_new_route_args(subnet, next_hop1, next_hop2);
2611 let count_initial_new_routes = initial_new_routes.len();
2612
2613 let request_sink = futures::stream::iter(initial_new_routes)
2614 .fold(request_sink, |mut request_sink, args| async move {
2615 let (completer, waiter) = oneshot::channel();
2616 request_sink
2617 .send(
2618 Request {
2619 args,
2620 sequence_number: TEST_SEQUENCE_NUMBER,
2621 client: route_client.clone(),
2622 completer,
2623 }
2624 .into(),
2625 )
2626 .await
2627 .unwrap();
2628 assert_matches!(waiter.await.unwrap(), Ok(()));
2629 request_sink
2630 })
2631 .await;
2632
2633 for _ in 0..count_initial_new_routes {
2637 let _ = route_sink.next_message().await;
2638 }
2639 assert_eq!(route_sink.next_message().now_or_never(), None);
2640
2641 let (results, _request_sink) = futures::stream::iter(args)
2642 .fold(
2643 (Vec::new(), request_sink),
2644 |(mut results, mut request_sink), args| async move {
2645 let (completer, waiter) = oneshot::channel();
2646 request_sink
2647 .send(
2648 Request {
2649 args,
2650 sequence_number: TEST_SEQUENCE_NUMBER,
2651 client: route_client.clone(),
2652 completer,
2653 }
2654 .into(),
2655 )
2656 .await
2657 .unwrap();
2658 results.push(waiter.await.unwrap());
2659 (results, request_sink)
2660 },
2661 )
2662 .await;
2663
2664 let messages = {
2665 assert_eq!(&other_sink.take_messages()[..], &[]);
2666 let mut messages = Vec::new();
2667 while messages.len() < num_sink_messages {
2668 messages.push(route_sink.next_message().await);
2669 }
2670 assert_eq!(route_sink.next_message().now_or_never(), None);
2671 messages
2672 };
2673
2674 (messages, results)
2675 };
2676
2677 let route_set_fut = respond_to_route_set_modifications::<A::Version, _, _>(
2678 route_set_stream.by_ref(),
2679 watcher_stream.by_ref(),
2680 route_set_results,
2681 )
2682 .fuse();
2683
2684 let root_interfaces_fut = root_handler(interfaces_request_stream).fuse();
2685
2686 let (messages, results) = futures::select! {
2687 (messages, results) = fut.fuse() => (messages, results),
2688 res = futures::future::join3(
2689 route_set_fut,
2690 root_interfaces_fut,
2691 event_loop_fut,
2692 ) => {
2693 unreachable!("eventloop/stream handlers should not return: {res:?}")
2694 }
2695 };
2696
2697 TestRequestResult { messages, waiter_results: results }
2698 };
2699 scope.join().await;
2700 result
2701 }
2702
2703 #[test_case(V4_SUB1, V4_NEXTHOP1, V4_NEXTHOP2; "v4_route_dump")]
2704 #[test_case(V6_SUB1, V6_NEXTHOP1, V6_NEXTHOP2; "v6_route_dump")]
2705 #[fuchsia::test]
2706 async fn test_get_route<A: IpAddress>(subnet: Subnet<A>, next_hop1: A, next_hop2: A)
2707 where
2708 A::Version: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
2709 {
2710 let expected_messages = vec![
2711 SentMessage::unicast(
2712 create_netlink_route_message::<A::Version>(
2713 subnet.prefix(),
2714 MANAGED_ROUTE_TABLE_INDEX,
2715 create_nlas::<A::Version>(
2716 Some(subnet),
2717 Some(next_hop1),
2718 DEV1,
2719 METRIC1,
2720 Some(MANAGED_ROUTE_TABLE_ID),
2721 ),
2722 )
2723 .into_rtnl_new_route(TEST_SEQUENCE_NUMBER, true),
2724 ),
2725 SentMessage::unicast(
2726 create_netlink_route_message::<A::Version>(
2727 subnet.prefix(),
2728 MANAGED_ROUTE_TABLE_INDEX,
2729 create_nlas::<A::Version>(
2730 Some(subnet),
2731 Some(next_hop2),
2732 DEV2,
2733 METRIC2,
2734 Some(MANAGED_ROUTE_TABLE_ID),
2735 ),
2736 )
2737 .into_rtnl_new_route(TEST_SEQUENCE_NUMBER, true),
2738 ),
2739 ];
2740
2741 pretty_assertions::assert_eq!(
2742 {
2743 let mut test_request_result = test_requests(
2744 [RequestArgs::Route(RouteRequestArgs::Get(GetRouteArgs::Dump))],
2745 |interfaces_request_stream| async {
2746 interfaces_request_stream
2747 .for_each(|req| async move {
2748 panic!("unexpected InterfacesRequest: {req:?}")
2749 })
2750 .await;
2751 },
2752 HashMap::new(),
2753 subnet,
2754 next_hop1,
2755 next_hop2,
2756 expected_messages.len(),
2757 )
2758 .await;
2759 test_request_result.messages.sort_by_key(|message| {
2760 assert_matches!(
2761 &message.message.payload,
2762 NetlinkPayload::InnerMessage(RouteNetlinkMessage::NewRoute(m)) => {
2763 m.attributes.clone().into_iter().filter_map(|nla|
2766 match nla {
2767 RouteAttribute::Oif(interface_id) =>
2768 Some((m.header.address_family, interface_id)),
2769 RouteAttribute::Destination(_)
2770 | RouteAttribute::Gateway(_)
2771 | RouteAttribute::Priority(_)
2772 | RouteAttribute::Table(_) => None,
2773 _ => panic!("unexpected NLA {nla:?} present in payload"),
2774 }
2775 ).next()
2776 }
2777 )
2778 });
2779 test_request_result
2780 },
2781 TestRequestResult { messages: expected_messages, waiter_results: vec![Ok(())] },
2782 )
2783 }
2784
2785 #[derive(Debug, Clone, Copy)]
2786 enum RouteSetResult {
2787 AddResult(Result<bool, fnet_routes_admin::RouteSetError>),
2788 DelResult(Result<bool, fnet_routes_admin::RouteSetError>),
2789 AuthenticationResult(Result<(), fnet_routes_admin::AuthenticateForInterfaceError>),
2790 }
2791
2792 fn route_event_from_route<
2793 I: Ip + fnet_routes_ext::FidlRouteIpExt,
2794 F: FnOnce(fnet_routes_ext::InstalledRoute<I>) -> fnet_routes_ext::Event<I>,
2795 >(
2796 route: I::Route,
2797 table_id: fnet_routes_ext::TableId,
2798 event_fn: F,
2799 ) -> I::WatchEvent {
2800 let route: fnet_routes_ext::Route<I> = route.try_into().unwrap();
2801
2802 let metric = match route.properties.specified_properties.metric {
2803 fnet_routes::SpecifiedMetric::ExplicitMetric(metric) => metric,
2804 fnet_routes::SpecifiedMetric::InheritedFromInterface(fnet_routes::Empty) => {
2805 panic!("metric should be explicit")
2806 }
2807 };
2808
2809 event_fn(fnet_routes_ext::InstalledRoute {
2810 route,
2811 effective_properties: fnet_routes_ext::EffectiveRouteProperties { metric },
2812 table_id,
2814 })
2815 .try_into()
2816 .unwrap()
2817 }
2818
2819 async fn respond_to_route_set_modifications<
2822 I: Ip + fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
2823 RS: Stream<
2824 Item = (
2825 fnet_routes_ext::TableId,
2826 <<I::RouteSetMarker as ProtocolMarker>::RequestStream as Stream>::Item,
2827 ),
2828 >,
2829 WS: Stream<Item = <<I::WatcherMarker as ProtocolMarker>::RequestStream as Stream>::Item>
2830 + std::marker::Unpin,
2831 >(
2832 route_stream: RS,
2833 watcher_stream: WS,
2834 mut route_set_results: HashMap<fnet_routes_ext::TableId, VecDeque<RouteSetResult>>,
2835 ) {
2836 #[derive(GenericOverIp)]
2837 #[generic_over_ip(I, Ip)]
2838 struct RouteSetInputs<I: fnet_routes_ext::admin::FidlRouteAdminIpExt> {
2839 request: <<I::RouteSetMarker as ProtocolMarker>::RequestStream as Stream>::Item,
2840 route_set_result: RouteSetResult,
2841 }
2842 #[derive(GenericOverIp)]
2843 #[generic_over_ip(I, Ip)]
2844 struct RouteSetOutputs<I: fnet_routes_ext::FidlRouteIpExt> {
2845 event: Option<I::WatchEvent>,
2846 }
2847
2848 let mut route_stream = std::pin::pin!(route_stream);
2849 let mut watcher_stream = std::pin::pin!(watcher_stream);
2850
2851 {
2852 let queue = route_set_results.entry(OTHER_FIDL_TABLE_ID).or_default();
2853 queue.push_front(RouteSetResult::AddResult(Ok(true)));
2854 queue.push_front(RouteSetResult::AddResult(Ok(true)));
2855 }
2856
2857 while let Some((table_id, request)) = route_stream.next().await {
2858 let route_set_result = route_set_results
2859 .get_mut(&table_id)
2860 .unwrap_or_else(|| panic!("missing result for {table_id:?}"))
2861 .pop_front()
2862 .unwrap_or_else(|| panic!("missing result for {table_id:?}"));
2863 let RouteSetOutputs { event } = I::map_ip(
2864 RouteSetInputs { request, route_set_result },
2865 |RouteSetInputs { request, route_set_result }| {
2866 let request = request.expect("failed to receive request");
2867 crate::logging::log_debug!(
2868 "responding on {table_id:?} to route set request {request:?} \
2869 with result {route_set_result:?}"
2870 );
2871 match request {
2872 fnet_routes_admin::RouteSetV4Request::AddRoute { route, responder } => {
2873 let route_set_result = assert_matches!(
2874 route_set_result,
2875 RouteSetResult::AddResult(res) => res
2876 );
2877
2878 responder
2879 .send(route_set_result)
2880 .expect("failed to respond to `AddRoute`");
2881
2882 RouteSetOutputs {
2883 event: match route_set_result {
2884 Ok(true) => Some(route_event_from_route::<Ipv4, _>(
2885 route,
2886 table_id,
2887 fnet_routes_ext::Event::<Ipv4>::Added,
2888 )),
2889 _ => None,
2890 },
2891 }
2892 }
2893 fnet_routes_admin::RouteSetV4Request::RemoveRoute { route, responder } => {
2894 let route_set_result = assert_matches!(
2895 route_set_result,
2896 RouteSetResult::DelResult(res) => res
2897 );
2898
2899 responder
2900 .send(route_set_result)
2901 .expect("failed to respond to `RemoveRoute`");
2902
2903 RouteSetOutputs {
2904 event: match route_set_result {
2905 Ok(true) => Some(route_event_from_route::<Ipv4, _>(
2906 route,
2907 table_id,
2908 fnet_routes_ext::Event::<Ipv4>::Removed,
2909 )),
2910 _ => None,
2911 },
2912 }
2913 }
2914 fnet_routes_admin::RouteSetV4Request::AuthenticateForInterface {
2915 credential: _,
2916 responder,
2917 } => {
2918 let route_set_result = assert_matches!(
2919 route_set_result,
2920 RouteSetResult::AuthenticationResult(res) => res
2921 );
2922
2923 responder
2924 .send(route_set_result)
2925 .expect("failed to respond to `AuthenticateForInterface`");
2926 RouteSetOutputs { event: None }
2927 }
2928 }
2929 },
2930 |RouteSetInputs { request, route_set_result }| {
2931 let request = request.expect("failed to receive request");
2932 crate::logging::log_debug!(
2933 "responding on {table_id:?} to route set request {request:?} \
2934 with result {route_set_result:?}"
2935 );
2936 match request {
2937 fnet_routes_admin::RouteSetV6Request::AddRoute { route, responder } => {
2938 let route_set_result = assert_matches!(
2939 route_set_result,
2940 RouteSetResult::AddResult(res) => res
2941 );
2942
2943 responder
2944 .send(route_set_result)
2945 .expect("failed to respond to `AddRoute`");
2946
2947 RouteSetOutputs {
2948 event: match route_set_result {
2949 Ok(true) => Some(route_event_from_route::<Ipv6, _>(
2950 route,
2951 table_id,
2952 fnet_routes_ext::Event::<Ipv6>::Added,
2953 )),
2954 _ => None,
2955 },
2956 }
2957 }
2958 fnet_routes_admin::RouteSetV6Request::RemoveRoute { route, responder } => {
2959 let route_set_result = assert_matches!(
2960 route_set_result,
2961 RouteSetResult::DelResult(res) => res
2962 );
2963
2964 responder
2965 .send(route_set_result)
2966 .expect("failed to respond to `RemoveRoute`");
2967
2968 RouteSetOutputs {
2969 event: match route_set_result {
2970 Ok(true) => Some(route_event_from_route::<Ipv6, _>(
2971 route,
2972 table_id,
2973 fnet_routes_ext::Event::<Ipv6>::Removed,
2974 )),
2975 _ => None,
2976 },
2977 }
2978 }
2979 fnet_routes_admin::RouteSetV6Request::AuthenticateForInterface {
2980 credential: _,
2981 responder,
2982 } => {
2983 let route_set_result = assert_matches!(
2984 route_set_result,
2985 RouteSetResult::AuthenticationResult(res) => res
2986 );
2987
2988 responder
2989 .send(route_set_result)
2990 .expect("failed to respond to `AuthenticateForInterface`");
2991 RouteSetOutputs { event: None }
2992 }
2993 }
2994 },
2995 );
2996
2997 if let Some(update) = event {
2998 let request = watcher_stream.next().await.expect("watcher stream should not end");
2999
3000 #[derive(GenericOverIp)]
3001 #[generic_over_ip(I, Ip)]
3002 struct HandleInputs<I: fnet_routes_ext::FidlRouteIpExt> {
3003 request: <<I::WatcherMarker as ProtocolMarker>::RequestStream as Stream>::Item,
3004 update: I::WatchEvent,
3005 }
3006
3007 I::map_ip_in(
3008 HandleInputs { request, update },
3009 |HandleInputs { request, update }| match request
3010 .expect("failed to receive `Watch` request")
3011 {
3012 fnet_routes::WatcherV4Request::Watch { responder } => {
3013 responder.send(&[update]).expect("failed to respond to `Watch`")
3014 }
3015 },
3016 |HandleInputs { request, update }| match request
3017 .expect("failed to receive `Watch` request")
3018 {
3019 fnet_routes::WatcherV6Request::Watch { responder } => {
3020 responder.send(&[update]).expect("failed to respond to `Watch`")
3021 }
3022 },
3023 );
3024 }
3025 }
3026
3027 if route_set_results.values().any(|value| !value.is_empty()) {
3028 panic!("unused route_set_results entries: {route_set_results:?}");
3029 }
3030 }
3031
3032 async fn test_route_requests<
3037 A: IpAddress,
3038 Fut: Future<Output = ()>,
3039 F: FnMut(fnet_interfaces_admin::ControlRequest) -> Fut,
3040 >(
3041 args: impl IntoIterator<Item = RequestArgs<A::Version>>,
3042 mut control_request_handler: F,
3043 route_set_results: HashMap<fnet_routes_ext::TableId, VecDeque<RouteSetResult>>,
3044 subnet: Subnet<A>,
3045 next_hop1: A,
3046 next_hop2: A,
3047 num_sink_messages: usize,
3048 ) -> TestRequestResult
3049 where
3050 A::Version: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
3051 {
3052 test_requests(
3053 args,
3054 |interfaces_request_stream| async move {
3055 interfaces_request_stream
3056 .filter_map(|req| {
3057 futures::future::ready(match req.unwrap() {
3058 fnet_root::InterfacesRequest::GetAdmin {
3059 id,
3060 control,
3061 control_handle: _,
3062 } => {
3063 pretty_assertions::assert_eq!(id, DEV1 as u64);
3064 Some(control.into_stream())
3065 }
3066 req => unreachable!("unexpected interfaces request: {req:?}"),
3067 })
3068 })
3069 .flatten()
3070 .next()
3071 .then(|req| control_request_handler(req.unwrap().unwrap()))
3072 .await
3073 },
3074 route_set_results,
3075 subnet,
3076 next_hop1,
3077 next_hop2,
3078 num_sink_messages,
3079 )
3080 .await
3081 }
3082
3083 async fn test_route_requests_helper<A: IpAddress>(
3086 args: impl IntoIterator<Item = RequestArgs<A::Version>>,
3087 expected_messages: Vec<SentMessage<RouteNetlinkMessage>>,
3088 route_set_results: HashMap<fnet_routes_ext::TableId, VecDeque<RouteSetResult>>,
3089 waiter_results: Vec<Result<(), RequestError>>,
3090 subnet: Subnet<A>,
3091 ) where
3092 A::Version: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
3093 {
3094 let (next_hop1, next_hop2): (A, A) = A::Version::map_ip(
3095 (),
3096 |()| (V4_NEXTHOP1, V4_NEXTHOP2),
3097 |()| (V6_NEXTHOP1, V6_NEXTHOP2),
3098 );
3099
3100 pretty_assertions::assert_eq!(
3101 {
3102 let mut test_request_result = test_route_requests(
3103 args,
3104 |req| async {
3105 match req {
3106 fnet_interfaces_admin::ControlRequest::GetAuthorizationForInterface {
3107 responder,
3108 } => {
3109 let token = fidl::Event::create();
3110 let grant = fnet_resources::GrantForInterfaceAuthorization {
3111 interface_id: DEV1 as u64,
3112 token,
3113 };
3114 responder.send(grant).unwrap();
3115 }
3116 req => panic!("unexpected request {req:?}"),
3117 }
3118 },
3119 route_set_results,
3120 subnet,
3121 next_hop1,
3122 next_hop2,
3123 expected_messages.len(),
3124 )
3125 .await;
3126 test_request_result.messages.sort_by_key(|message| {
3127 let sequence_number = message.message.header.sequence_number;
3130 assert_matches!(
3131 &message.message.payload,
3132 NetlinkPayload::InnerMessage(RouteNetlinkMessage::NewRoute(m))
3133 | NetlinkPayload::InnerMessage(RouteNetlinkMessage::DelRoute(m)) => {
3134 m.attributes.clone().into_iter().filter_map(|nla|
3137 match nla {
3138 RouteAttribute::Priority(priority) =>
3139 Some((sequence_number, priority)),
3140 RouteAttribute::Destination(_)
3141 | RouteAttribute::Gateway(_)
3142 | RouteAttribute::Oif(_)
3143 | RouteAttribute::Table(_) => None,
3144 _ => panic!("unexpected NLA {nla:?} present in payload"),
3145 }
3146 ).next()
3147 }
3148 )
3149 });
3150 test_request_result
3151 },
3152 TestRequestResult { messages: expected_messages, waiter_results },
3153 )
3154 }
3155
3156 enum RouteRequestKind {
3157 New,
3158 Del,
3159 }
3160
3161 fn route_set_for_table_id(
3162 results: Vec<RouteSetResult>,
3163 table_id: fnet_routes_ext::TableId,
3164 ) -> HashMap<fnet_routes_ext::TableId, VecDeque<RouteSetResult>> {
3165 HashMap::from_iter([(table_id, results.into())])
3166 }
3167
3168 fn route_set_for_first_new_table(
3169 results: Vec<RouteSetResult>,
3170 ) -> HashMap<fnet_routes_ext::TableId, VecDeque<RouteSetResult>> {
3171 route_set_for_table_id(results, OTHER_FIDL_TABLE_ID)
3172 }
3173
3174 #[test_case(
3176 RouteRequestKind::New,
3177 vec![
3178 RouteSetResult::AddResult(Ok(true))
3179 ],
3180 Ok(()),
3181 V4_SUB1,
3182 Some(METRIC3),
3183 DEV1;
3184 "v4_new_success")]
3185 #[test_case(
3186 RouteRequestKind::New,
3187 vec![
3188 RouteSetResult::AddResult(Ok(true))
3189 ],
3190 Ok(()),
3191 V6_SUB1,
3192 Some(METRIC3),
3193 DEV1;
3194 "v6_new_success")]
3195 #[test_case(
3196 RouteRequestKind::New,
3197 vec![
3198 RouteSetResult::AddResult(Err(RouteSetError::Unauthenticated)),
3199 RouteSetResult::AuthenticationResult(Err(
3200 fnet_routes_admin::AuthenticateForInterfaceError::InvalidAuthentication
3201 )),
3202 ],
3203 Err(RequestError::UnrecognizedInterface),
3204 V4_SUB1,
3205 Some(METRIC3),
3206 DEV1;
3207 "v4_new_failed_auth")]
3208 #[test_case(
3209 RouteRequestKind::New,
3210 vec![
3211 RouteSetResult::AddResult(Err(RouteSetError::Unauthenticated)),
3212 RouteSetResult::AuthenticationResult(Err(
3213 fnet_routes_admin::AuthenticateForInterfaceError::InvalidAuthentication
3214 )),
3215 ],
3216 Err(RequestError::UnrecognizedInterface),
3217 V6_SUB1,
3218 Some(METRIC3),
3219 DEV1;
3220 "v6_new_failed_auth")]
3221 #[test_case(
3222 RouteRequestKind::New,
3223 vec![
3224 RouteSetResult::AddResult(Ok(false))
3225 ],
3226 Err(RequestError::AlreadyExists),
3227 V4_SUB1,
3228 Some(METRIC3),
3229 DEV1;
3230 "v4_new_failed_netstack_reports_exists")]
3231 #[test_case(
3232 RouteRequestKind::New,
3233 vec![
3234 RouteSetResult::AddResult(Ok(false))
3235 ],
3236 Err(RequestError::AlreadyExists),
3237 V6_SUB1,
3238 Some(METRIC3),
3239 DEV1;
3240 "v6_new_failed_netstack_reports_exists")]
3241 #[test_case(
3242 RouteRequestKind::New,
3243 vec![],
3244 Err(RequestError::AlreadyExists),
3245 V4_SUB1,
3246 Some(METRIC1),
3247 DEV1;
3248 "v4_new_failed_netlink_reports_exists")]
3249 #[test_case(
3250 RouteRequestKind::New,
3251 vec![],
3252 Err(RequestError::AlreadyExists),
3253 V4_SUB1,
3254 Some(METRIC1),
3255 DEV2;
3256 "v4_new_failed_netlink_reports_exists_different_interface")]
3257 #[test_case(
3258 RouteRequestKind::New,
3259 vec![],
3260 Err(RequestError::AlreadyExists),
3261 V6_SUB1,
3262 Some(METRIC1),
3263 DEV1;
3264 "v6_new_failed_netlink_reports_exists")]
3265 #[test_case(
3266 RouteRequestKind::New,
3267 vec![],
3268 Err(RequestError::AlreadyExists),
3269 V6_SUB1,
3270 Some(METRIC1),
3271 DEV2;
3272 "v6_new_failed_netlink_reports_exists_different_interface")]
3273 #[test_case(
3274 RouteRequestKind::New,
3275 vec![
3276 RouteSetResult::AddResult(Err(RouteSetError::InvalidDestinationSubnet))
3277 ],
3278 Err(RequestError::InvalidRequest),
3279 V4_SUB1,
3280 Some(METRIC3),
3281 DEV1;
3282 "v4_new_invalid_dest")]
3283 #[test_case(
3284 RouteRequestKind::New,
3285 vec![
3286 RouteSetResult::AddResult(Err(RouteSetError::InvalidDestinationSubnet))
3287 ],
3288 Err(RequestError::InvalidRequest),
3289 V6_SUB1,
3290 Some(METRIC3),
3291 DEV1;
3292 "v6_new_invalid_dest")]
3293 #[test_case(
3294 RouteRequestKind::New,
3295 vec![
3296 RouteSetResult::AddResult(Err(RouteSetError::InvalidNextHop))
3297 ],
3298 Err(RequestError::InvalidRequest),
3299 V4_SUB1,
3300 Some(METRIC3),
3301 DEV1;
3302 "v4_new_invalid_hop")]
3303 #[test_case(
3304 RouteRequestKind::New,
3305 vec![
3306 RouteSetResult::AddResult(Err(RouteSetError::InvalidNextHop))
3307 ],
3308 Err(RequestError::InvalidRequest),
3309 V6_SUB1,
3310 Some(METRIC3),
3311 DEV1;
3312 "v6_new_invalid_hop")]
3313 #[test_case(
3315 RouteRequestKind::Del,
3316 vec![
3317 RouteSetResult::DelResult(Ok(true))
3318 ],
3319 Ok(()),
3320 V4_SUB1,
3321 None,
3322 DEV1;
3323 "v4_del_success_only_subnet")]
3324 #[test_case(
3325 RouteRequestKind::Del,
3326 vec![
3327 RouteSetResult::DelResult(Ok(true))
3328 ],
3329 Ok(()),
3330 V4_SUB1,
3331 Some(METRIC1),
3332 DEV1;
3333 "v4_del_success_only_subnet_metric")]
3334 #[test_case(
3335 RouteRequestKind::Del,
3336 vec![
3337 RouteSetResult::DelResult(Ok(true))
3338 ],
3339 Ok(()),
3340 V6_SUB1,
3341 None,
3342 DEV1;
3343 "v6_del_success_only_subnet")]
3344 #[test_case(
3345 RouteRequestKind::Del,
3346 vec![
3347 RouteSetResult::DelResult(Ok(true))
3348 ],
3349 Ok(()),
3350 V6_SUB1,
3351 Some(METRIC1),
3352 DEV1;
3353 "v6_del_success_only_subnet_metric")]
3354 #[test_case(
3355 RouteRequestKind::Del,
3356 vec![
3357 RouteSetResult::DelResult(Err(RouteSetError::Unauthenticated)),
3358 RouteSetResult::AuthenticationResult(Err(
3359 fnet_routes_admin::AuthenticateForInterfaceError::InvalidAuthentication
3360 )),
3361 ],
3362 Err(RequestError::UnrecognizedInterface),
3363 V4_SUB1,
3364 None,
3365 DEV1;
3366 "v4_del_failed_auth")]
3367 #[test_case(
3368 RouteRequestKind::Del,
3369 vec![
3370 RouteSetResult::DelResult(Err(RouteSetError::Unauthenticated)),
3371 RouteSetResult::AuthenticationResult(Err(
3372 fnet_routes_admin::AuthenticateForInterfaceError::InvalidAuthentication
3373 )),
3374 ],
3375 Err(RequestError::UnrecognizedInterface),
3376 V6_SUB1,
3377 None,
3378 DEV1;
3379 "v6_del_failed_auth")]
3380 #[test_case(
3381 RouteRequestKind::Del,
3382 vec![
3383 RouteSetResult::DelResult(Ok(false))
3384 ],
3385 Err(RequestError::DeletionNotAllowed),
3386 V4_SUB1,
3387 None,
3388 DEV1;
3389 "v4_del_failed_attempt_to_delete_route_from_global_set")]
3390 #[test_case(
3391 RouteRequestKind::Del,
3392 vec![
3393 RouteSetResult::DelResult(Ok(false))
3394 ],
3395 Err(RequestError::DeletionNotAllowed),
3396 V6_SUB1,
3397 None,
3398 DEV1;
3399 "v6_del_failed_attempt_to_delete_route_from_global_set")]
3400 #[test_case(
3406 RouteRequestKind::Del,
3407 vec![],
3408 Err(RequestError::NotFound),
3409 V4_SUB1,
3410 Some(METRIC3),
3411 DEV1;
3412 "v4_del_no_matching_route")]
3413 #[test_case(
3414 RouteRequestKind::Del,
3415 vec![],
3416 Err(RequestError::NotFound),
3417 V6_SUB1,
3418 Some(METRIC3),
3419 DEV1;
3420 "v6_del_no_matching_route")]
3421 #[test_case(
3422 RouteRequestKind::Del,
3423 vec![
3424 RouteSetResult::DelResult(Err(RouteSetError::InvalidDestinationSubnet))
3425 ],
3426 Err(RequestError::InvalidRequest),
3427 V4_SUB1,
3428 None,
3429 DEV1;
3430 "v4_del_invalid_dest")]
3431 #[test_case(
3432 RouteRequestKind::Del,
3433 vec![
3434 RouteSetResult::DelResult(Err(RouteSetError::InvalidDestinationSubnet))
3435 ],
3436 Err(RequestError::InvalidRequest),
3437 V6_SUB1,
3438 None,
3439 DEV1;
3440 "v6_del_invalid_dest")]
3441 #[test_case(
3442 RouteRequestKind::Del,
3443 vec![
3444 RouteSetResult::DelResult(Err(RouteSetError::InvalidNextHop))
3445 ],
3446 Err(RequestError::InvalidRequest),
3447 V4_SUB1,
3448 None,
3449 DEV1;
3450 "v4_del_invalid_hop")]
3451 #[test_case(
3452 RouteRequestKind::Del,
3453 vec![
3454 RouteSetResult::DelResult(Err(RouteSetError::InvalidNextHop))
3455 ],
3456 Err(RequestError::InvalidRequest),
3457 V6_SUB1,
3458 None,
3459 DEV1;
3460 "v6_del_invalid_hop")]
3461 #[fuchsia::test]
3462 async fn test_new_del_route<A: IpAddress>(
3463 kind: RouteRequestKind,
3464 route_set_results: Vec<RouteSetResult>,
3465 waiter_result: Result<(), RequestError>,
3466 subnet: Subnet<A>,
3467 metric: Option<u32>,
3468 interface_id: u32,
3469 ) where
3470 A::Version: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
3471 {
3472 let route_group = match A::Version::VERSION {
3473 IpVersion::V4 => ModernGroup(rtnetlink_groups_RTNLGRP_IPV4_ROUTE),
3474 IpVersion::V6 => ModernGroup(rtnetlink_groups_RTNLGRP_IPV6_ROUTE),
3475 };
3476
3477 let next_hop: A = A::Version::map_ip((), |()| V4_NEXTHOP1, |()| V6_NEXTHOP1);
3478
3479 let route_req_args = match kind {
3483 RouteRequestKind::New => {
3484 RouteRequestArgs::New(NewRouteArgs::Unicast(create_unicast_new_route_args(
3486 subnet,
3487 next_hop,
3488 interface_id.into(),
3489 metric.expect("add cases should be Some"),
3490 MANAGED_ROUTE_TABLE_INDEX,
3491 )))
3492 }
3493 RouteRequestKind::Del => {
3494 RouteRequestArgs::Del(DelRouteArgs::Unicast(create_unicast_del_route_args(
3496 subnet,
3497 None,
3498 None,
3499 metric,
3500 MANAGED_ROUTE_TABLE_INDEX,
3501 )))
3502 }
3503 };
3504
3505 let messages = match waiter_result {
3508 Ok(()) => {
3509 let build_message = |table| {
3510 let route_message = create_netlink_route_message::<A::Version>(
3511 subnet.prefix(),
3512 table,
3513 create_nlas::<A::Version>(
3514 Some(subnet),
3515 Some(next_hop),
3516 DEV1,
3517 match kind {
3518 RouteRequestKind::New => metric.expect("add cases should be some"),
3519 RouteRequestKind::Del => METRIC1,
3524 },
3525 (table != MAIN_ROUTE_TABLE_INDEX).then_some(table.get()),
3526 ),
3527 );
3528 let netlink_message = match kind {
3529 RouteRequestKind::New => {
3530 route_message.into_rtnl_new_route(UNSPECIFIED_SEQUENCE_NUMBER, false)
3531 }
3532 RouteRequestKind::Del => route_message.into_rtnl_del_route(),
3533 };
3534 SentMessage::multicast(netlink_message, route_group)
3535 };
3536
3537 let route_message_in_managed_table = build_message(MANAGED_ROUTE_TABLE_INDEX);
3538
3539 vec![route_message_in_managed_table]
3540 }
3541 Err(_) => Vec::new(),
3542 };
3543
3544 test_route_requests_helper(
3545 [RequestArgs::Route(route_req_args)],
3546 messages,
3547 route_set_for_first_new_table(route_set_results),
3548 vec![waiter_result],
3549 subnet,
3550 )
3551 .await;
3552 }
3553
3554 #[test_case(
3557 RouteRequestKind::New,
3558 vec![
3559 RouteSetResult::AddResult(Err(RouteSetError::Unauthenticated)),
3560 RouteSetResult::AuthenticationResult(Ok(())),
3561 RouteSetResult::AddResult(Err(RouteSetError::Unauthenticated)),
3562 ],
3563 Err(RequestError::InvalidRequest),
3564 V4_SUB1;
3565 "v4_new_unauthenticated")]
3566 #[test_case(
3567 RouteRequestKind::New,
3568 vec![
3569 RouteSetResult::AddResult(Err(RouteSetError::Unauthenticated)),
3570 RouteSetResult::AuthenticationResult(Ok(())),
3571 RouteSetResult::AddResult(Err(RouteSetError::Unauthenticated)),
3572 ],
3573 Err(RequestError::InvalidRequest),
3574 V6_SUB1;
3575 "v6_new_unauthenticated")]
3576 #[test_case(
3577 RouteRequestKind::Del,
3578 vec![
3579 RouteSetResult::DelResult(Err(RouteSetError::Unauthenticated)),
3580 RouteSetResult::AuthenticationResult(Ok(())),
3581 RouteSetResult::DelResult(Err(RouteSetError::Unauthenticated)),
3582 ],
3583 Err(RequestError::InvalidRequest),
3584 V4_SUB1;
3585 "v4_del_unauthenticated")]
3586 #[test_case(
3587 RouteRequestKind::Del,
3588 vec![
3589 RouteSetResult::DelResult(Err(RouteSetError::Unauthenticated)),
3590 RouteSetResult::AuthenticationResult(Ok(())),
3591 RouteSetResult::DelResult(Err(RouteSetError::Unauthenticated)),
3592 ],
3593 Err(RequestError::InvalidRequest),
3594 V6_SUB1;
3595 "v6_del_unauthenticated")]
3596 #[should_panic(expected = "received unauthentication error from route set for route")]
3597 #[fuchsia::test]
3598 async fn test_new_del_route_failed<A: IpAddress>(
3599 kind: RouteRequestKind,
3600 route_set_results: Vec<RouteSetResult>,
3601 waiter_result: Result<(), RequestError>,
3602 subnet: Subnet<A>,
3603 ) where
3604 A::Version: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
3605 {
3606 let route_req_args = match kind {
3607 RouteRequestKind::New => {
3608 let next_hop: A = A::Version::map_ip((), |()| V4_NEXTHOP1, |()| V6_NEXTHOP1);
3609 RouteRequestArgs::New(NewRouteArgs::Unicast(create_unicast_new_route_args(
3611 subnet,
3612 next_hop,
3613 DEV1.into(),
3614 METRIC3,
3615 MANAGED_ROUTE_TABLE_INDEX,
3616 )))
3617 }
3618 RouteRequestKind::Del => {
3619 RouteRequestArgs::Del(DelRouteArgs::Unicast(create_unicast_del_route_args(
3621 subnet,
3622 None,
3623 None,
3624 None,
3625 MANAGED_ROUTE_TABLE_INDEX,
3626 )))
3627 }
3628 };
3629 test_route_requests_helper(
3630 [RequestArgs::Route(route_req_args)],
3631 Vec::new(),
3632 route_set_for_first_new_table(route_set_results),
3633 vec![waiter_result],
3634 subnet,
3635 )
3636 .await;
3637 }
3638
3639 #[test_case(
3640 Err(RequestError::NotFound),
3641 V4_SUB1; "v4_del")]
3642 #[test_case(
3643 Err(RequestError::NotFound),
3644 V6_SUB1; "v6_del")]
3645 #[fuchsia::test]
3646 async fn test_del_route_nonexistent_table<A: IpAddress>(
3647 waiter_result: Result<(), RequestError>,
3648 subnet: Subnet<A>,
3649 ) where
3650 A::Version: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
3651 {
3652 let route_req_args =
3654 RouteRequestArgs::Del(DelRouteArgs::Unicast(create_unicast_del_route_args(
3655 subnet,
3656 None,
3657 None,
3658 None,
3659 NetlinkRouteTableIndex::new(1234),
3660 )));
3661 test_route_requests_helper(
3662 [RequestArgs::Route(route_req_args)],
3663 Vec::new(),
3664 HashMap::new(),
3665 vec![waiter_result],
3666 subnet,
3667 )
3668 .await;
3669 }
3670
3671 #[test_case(
3675 V4_SUB1,
3676 ModernGroup(rtnetlink_groups_RTNLGRP_IPV4_ROUTE),
3677 MANAGED_ROUTE_TABLE_INDEX;
3678 "v4_new_same_table_dump")]
3679 #[test_case(
3680 V6_SUB1,
3681 ModernGroup(rtnetlink_groups_RTNLGRP_IPV6_ROUTE),
3682 MANAGED_ROUTE_TABLE_INDEX;
3683 "v6_new_same_table_dump")]
3684 #[test_case(
3685 V4_SUB1,
3686 ModernGroup(rtnetlink_groups_RTNLGRP_IPV4_ROUTE),
3687 NetlinkRouteTableIndex::new(1234);
3688 "v4_new_different_table_dump")]
3689 #[test_case(
3690 V6_SUB1,
3691 ModernGroup(rtnetlink_groups_RTNLGRP_IPV6_ROUTE),
3692 NetlinkRouteTableIndex::new(1234);
3693 "v6_new_different_table_dump")]
3694 #[fuchsia::test]
3695 async fn test_new_then_get_dump_request<A: IpAddress>(
3696 subnet: Subnet<A>,
3697 group: ModernGroup,
3698 table: NetlinkRouteTableIndex,
3699 ) where
3700 A::Version: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
3701 {
3702 let (next_hop1, next_hop2): (A, A) = A::Version::map_ip(
3703 (),
3704 |()| (V4_NEXTHOP1, V4_NEXTHOP2),
3705 |()| (V6_NEXTHOP1, V6_NEXTHOP2),
3706 );
3707
3708 let unicast_route_args =
3714 create_unicast_new_route_args(subnet, next_hop1, DEV1.into(), METRIC3, table);
3715
3716 let messages = vec![
3721 SentMessage::multicast(
3722 create_netlink_route_message::<A::Version>(
3723 subnet.prefix(),
3724 table,
3725 create_nlas::<A::Version>(
3726 Some(subnet),
3727 Some(next_hop1),
3728 DEV1,
3729 METRIC3,
3730 Some(table.get()),
3731 ),
3732 )
3733 .into_rtnl_new_route(UNSPECIFIED_SEQUENCE_NUMBER, false),
3734 group,
3735 ),
3736 SentMessage::unicast(
3737 create_netlink_route_message::<A::Version>(
3738 subnet.prefix(),
3739 MANAGED_ROUTE_TABLE_INDEX,
3740 create_nlas::<A::Version>(
3741 Some(subnet),
3742 Some(next_hop1),
3743 DEV1,
3744 METRIC1,
3745 Some(MANAGED_ROUTE_TABLE_ID),
3746 ),
3747 )
3748 .into_rtnl_new_route(TEST_SEQUENCE_NUMBER, true),
3749 ),
3750 SentMessage::unicast(
3751 create_netlink_route_message::<A::Version>(
3752 subnet.prefix(),
3753 MANAGED_ROUTE_TABLE_INDEX,
3754 create_nlas::<A::Version>(
3755 Some(subnet),
3756 Some(next_hop2),
3757 DEV2,
3758 METRIC2,
3759 Some(MANAGED_ROUTE_TABLE_ID),
3760 ),
3761 )
3762 .into_rtnl_new_route(TEST_SEQUENCE_NUMBER, true),
3763 ),
3764 SentMessage::unicast(
3765 create_netlink_route_message::<A::Version>(
3766 subnet.prefix(),
3767 table,
3768 create_nlas::<A::Version>(
3769 Some(subnet),
3770 Some(next_hop1),
3771 DEV1,
3772 METRIC3,
3773 Some(table.get()),
3774 ),
3775 )
3776 .into_rtnl_new_route(TEST_SEQUENCE_NUMBER, true),
3777 ),
3778 ];
3779
3780 test_route_requests_helper(
3781 [
3782 RequestArgs::Route(RouteRequestArgs::New(NewRouteArgs::Unicast(
3783 unicast_route_args,
3784 ))),
3785 RequestArgs::Route(RouteRequestArgs::Get(GetRouteArgs::Dump)),
3786 ],
3787 messages,
3788 route_set_for_table_id(
3789 vec![RouteSetResult::AddResult(Ok(true))],
3790 if table == MANAGED_ROUTE_TABLE_INDEX {
3791 OTHER_FIDL_TABLE_ID
3792 } else {
3793 fnet_routes_ext::TableId::new(OTHER_FIDL_TABLE_ID.get() + 1)
3794 },
3795 ),
3796 vec![Ok(()), Ok(())],
3797 subnet,
3798 )
3799 .await;
3800 }
3801
3802 #[test_case(V4_SUB1; "v4_new_dump")]
3811 #[test_case(V6_SUB1; "v6_new_dump")]
3812 #[fuchsia::test]
3813 async fn test_new_route_different_table_then_get_dump_request<A: IpAddress>(subnet: Subnet<A>)
3814 where
3815 A::Version: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
3816 {
3817 let (next_hop1, next_hop2, IpInvariant(group)): (A, A, IpInvariant<ModernGroup>) =
3818 A::Version::map_ip(
3819 (),
3820 |()| {
3821 (
3822 V4_NEXTHOP1,
3823 V4_NEXTHOP2,
3824 IpInvariant(ModernGroup(rtnetlink_groups_RTNLGRP_IPV4_ROUTE)),
3825 )
3826 },
3827 |()| {
3828 (
3829 V6_NEXTHOP1,
3830 V6_NEXTHOP2,
3831 IpInvariant(ModernGroup(rtnetlink_groups_RTNLGRP_IPV6_ROUTE)),
3832 )
3833 },
3834 );
3835
3836 const ALTERNATIVE_ROUTE_TABLE: NetlinkRouteTableIndex = NetlinkRouteTableIndex::new(1337);
3837
3838 let unicast_route_args = create_unicast_new_route_args(
3845 subnet,
3846 next_hop1,
3847 DEV1.into(),
3848 METRIC1,
3849 ALTERNATIVE_ROUTE_TABLE,
3850 );
3851
3852 let messages = vec![
3856 SentMessage::multicast(
3857 create_netlink_route_message::<A::Version>(
3858 subnet.prefix(),
3859 ALTERNATIVE_ROUTE_TABLE,
3860 create_nlas::<A::Version>(
3861 Some(subnet),
3862 Some(next_hop1),
3863 DEV1,
3864 METRIC1,
3865 Some(ALTERNATIVE_ROUTE_TABLE.get()),
3866 ),
3867 )
3868 .into_rtnl_new_route(UNSPECIFIED_SEQUENCE_NUMBER, false),
3869 group,
3870 ),
3871 SentMessage::unicast(
3872 create_netlink_route_message::<A::Version>(
3873 subnet.prefix(),
3874 MANAGED_ROUTE_TABLE_INDEX,
3875 create_nlas::<A::Version>(
3876 Some(subnet),
3877 Some(next_hop1),
3878 DEV1,
3879 METRIC1,
3880 Some(MANAGED_ROUTE_TABLE_ID),
3881 ),
3882 )
3883 .into_rtnl_new_route(TEST_SEQUENCE_NUMBER, true),
3884 ),
3885 SentMessage::unicast(
3886 create_netlink_route_message::<A::Version>(
3887 subnet.prefix(),
3888 ALTERNATIVE_ROUTE_TABLE,
3889 create_nlas::<A::Version>(
3890 Some(subnet),
3891 Some(next_hop1),
3892 DEV1,
3893 METRIC1,
3894 Some(ALTERNATIVE_ROUTE_TABLE.get()),
3895 ),
3896 )
3897 .into_rtnl_new_route(TEST_SEQUENCE_NUMBER, true),
3898 ),
3899 SentMessage::unicast(
3900 create_netlink_route_message::<A::Version>(
3901 subnet.prefix(),
3902 MANAGED_ROUTE_TABLE_INDEX,
3903 create_nlas::<A::Version>(
3904 Some(subnet),
3905 Some(next_hop2),
3906 DEV2,
3907 METRIC2,
3908 Some(MANAGED_ROUTE_TABLE_ID),
3909 ),
3910 )
3911 .into_rtnl_new_route(TEST_SEQUENCE_NUMBER, true),
3912 ),
3913 ];
3914
3915 test_route_requests_helper(
3916 [
3917 RequestArgs::Route(RouteRequestArgs::New(NewRouteArgs::Unicast(
3918 unicast_route_args,
3919 ))),
3920 RequestArgs::Route(RouteRequestArgs::Get(GetRouteArgs::Dump)),
3921 ],
3922 messages,
3923 HashMap::from_iter([
3924 (MAIN_FIDL_TABLE_ID, vec![RouteSetResult::AddResult(Ok(false))].into()),
3926 (
3928 fnet_routes_ext::TableId::new(OTHER_FIDL_TABLE_ID.get() + 1),
3929 vec![RouteSetResult::AddResult(Ok(true))].into(),
3930 ),
3931 ]),
3932 vec![Ok(()), Ok(())],
3933 subnet,
3934 )
3935 .await;
3936 }
3937
3938 #[test_case(V4_SUB1, ModernGroup(rtnetlink_groups_RTNLGRP_IPV4_ROUTE); "v4_new_del_dump")]
3942 #[test_case(V6_SUB1, ModernGroup(rtnetlink_groups_RTNLGRP_IPV6_ROUTE); "v6_new_del_dump")]
3943 #[fuchsia::test]
3944 async fn test_new_then_del_then_get_dump_request<A: IpAddress>(
3945 subnet: Subnet<A>,
3946 group: ModernGroup,
3947 ) where
3948 A::Version: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
3949 {
3950 let (next_hop1, next_hop2): (A, A) = A::Version::map_ip(
3951 (),
3952 |()| (V4_NEXTHOP1, V4_NEXTHOP2),
3953 |()| (V6_NEXTHOP1, V6_NEXTHOP2),
3954 );
3955
3956 let new_route_args = create_unicast_new_route_args(
3962 subnet,
3963 next_hop1,
3964 DEV1.into(),
3965 METRIC3,
3966 MANAGED_ROUTE_TABLE_INDEX,
3967 );
3968
3969 let del_route_args = create_unicast_del_route_args(
3971 subnet,
3972 None,
3973 None,
3974 Some(METRIC3),
3975 MANAGED_ROUTE_TABLE_INDEX,
3976 );
3977
3978 let messages = vec![
3982 SentMessage::multicast(
3983 create_netlink_route_message::<A::Version>(
3984 subnet.prefix(),
3985 MANAGED_ROUTE_TABLE_INDEX,
3986 create_nlas::<A::Version>(
3987 Some(subnet),
3988 Some(next_hop1),
3989 DEV1,
3990 METRIC3,
3991 Some(MANAGED_ROUTE_TABLE_ID),
3992 ),
3993 )
3994 .into_rtnl_new_route(UNSPECIFIED_SEQUENCE_NUMBER, false),
3995 group,
3996 ),
3997 SentMessage::multicast(
3998 create_netlink_route_message::<A::Version>(
3999 subnet.prefix(),
4000 MANAGED_ROUTE_TABLE_INDEX,
4001 create_nlas::<A::Version>(
4002 Some(subnet),
4003 Some(next_hop1),
4004 DEV1,
4005 METRIC3,
4006 Some(MANAGED_ROUTE_TABLE_ID),
4007 ),
4008 )
4009 .into_rtnl_del_route(),
4010 group,
4011 ),
4012 SentMessage::unicast(
4013 create_netlink_route_message::<A::Version>(
4014 subnet.prefix(),
4015 MANAGED_ROUTE_TABLE_INDEX,
4016 create_nlas::<A::Version>(
4017 Some(subnet),
4018 Some(next_hop1),
4019 DEV1,
4020 METRIC1,
4021 Some(MANAGED_ROUTE_TABLE_ID),
4022 ),
4023 )
4024 .into_rtnl_new_route(TEST_SEQUENCE_NUMBER, true),
4025 ),
4026 SentMessage::unicast(
4027 create_netlink_route_message::<A::Version>(
4028 subnet.prefix(),
4029 MANAGED_ROUTE_TABLE_INDEX,
4030 create_nlas::<A::Version>(
4031 Some(subnet),
4032 Some(next_hop2),
4033 DEV2,
4034 METRIC2,
4035 Some(MANAGED_ROUTE_TABLE_ID),
4036 ),
4037 )
4038 .into_rtnl_new_route(TEST_SEQUENCE_NUMBER, true),
4039 ),
4040 ];
4041
4042 test_route_requests_helper(
4043 [
4044 RequestArgs::Route(RouteRequestArgs::New(NewRouteArgs::Unicast(new_route_args))),
4045 RequestArgs::Route(RouteRequestArgs::Del(DelRouteArgs::Unicast(del_route_args))),
4046 RequestArgs::Route(RouteRequestArgs::Get(GetRouteArgs::Dump)),
4047 ],
4048 messages,
4049 route_set_for_first_new_table(vec![
4050 RouteSetResult::AddResult(Ok(true)),
4051 RouteSetResult::DelResult(Ok(true)),
4052 ]),
4053 vec![Ok(()), Ok(()), Ok(())],
4054 subnet,
4055 )
4056 .await;
4057 }
4058
4059 #[test_case(RouteRequestKind::New, V4_SUB1; "v4_new_if_removed")]
4064 #[test_case(RouteRequestKind::New, V6_SUB1; "v6_new_if_removed")]
4065 #[test_case(RouteRequestKind::Del, V4_SUB1; "v4_del_if_removed")]
4066 #[test_case(RouteRequestKind::Del, V6_SUB1; "v6_del_if_removed")]
4067 #[fuchsia::test]
4068 async fn test_new_del_route_interface_removed<A: IpAddress>(
4069 kind: RouteRequestKind,
4070 subnet: Subnet<A>,
4071 ) where
4072 A::Version: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
4073 {
4074 let (next_hop1, next_hop2): (A, A) = A::Version::map_ip(
4075 (),
4076 |()| (V4_NEXTHOP1, V4_NEXTHOP2),
4077 |()| (V6_NEXTHOP1, V6_NEXTHOP2),
4078 );
4079
4080 let (route_req_args, route_set_result) = match kind {
4084 RouteRequestKind::New => {
4085 let args =
4087 RouteRequestArgs::New(NewRouteArgs::Unicast(create_unicast_new_route_args(
4088 subnet,
4089 next_hop1,
4090 DEV1.into(),
4091 METRIC3,
4092 MANAGED_ROUTE_TABLE_INDEX,
4093 )));
4094 let res = RouteSetResult::AddResult(Err(RouteSetError::Unauthenticated));
4095 (args, res)
4096 }
4097 RouteRequestKind::Del => {
4098 let args =
4100 RouteRequestArgs::Del(DelRouteArgs::Unicast(create_unicast_del_route_args(
4101 subnet,
4102 None,
4103 None,
4104 None,
4105 MANAGED_ROUTE_TABLE_INDEX,
4106 )));
4107 let res = RouteSetResult::DelResult(Err(RouteSetError::Unauthenticated));
4108 (args, res)
4109 }
4110 };
4111
4112 let expected_messages = Vec::new();
4114
4115 pretty_assertions::assert_eq!(
4116 test_requests(
4117 [RequestArgs::Route(route_req_args)],
4118 |interfaces_request_stream| async move {
4119 interfaces_request_stream
4120 .for_each(|req| {
4121 futures::future::ready(match req.unwrap() {
4122 fnet_root::InterfacesRequest::GetAdmin {
4123 id,
4124 control,
4125 control_handle: _,
4126 } => {
4127 pretty_assertions::assert_eq!(id, DEV1 as u64);
4128 let control = control.into_stream();
4129 let control = control.control_handle();
4130 control.shutdown();
4131 }
4132 req => unreachable!("unexpected interfaces request: {req:?}"),
4133 })
4134 })
4135 .await
4136 },
4137 route_set_for_first_new_table(vec![route_set_result]),
4138 subnet,
4139 next_hop1,
4140 next_hop2,
4141 expected_messages.len(),
4142 )
4143 .await,
4144 TestRequestResult {
4145 messages: expected_messages,
4146 waiter_results: vec![Err(RequestError::UnrecognizedInterface)],
4147 },
4148 )
4149 }
4150
4151 #[derive(Clone)]
4153 struct Route<I: Ip> {
4154 subnet: Subnet<I::Addr>,
4155 device: u32,
4156 nexthop: Option<I::Addr>,
4157 metric: Option<NonZeroU32>,
4158 }
4159
4160 impl<I: Ip> Route<I> {
4161 fn to_route(self) -> fnet_routes_ext::Route<I> {
4162 let Self { subnet, device, nexthop, metric } = self;
4163 fnet_routes_ext::Route {
4164 destination: subnet,
4165 action: fnet_routes_ext::RouteAction::Forward(fnet_routes_ext::RouteTarget {
4166 outbound_interface: device.into(),
4167 next_hop: nexthop
4168 .map(|a| SpecifiedAddr::new(a).expect("nexthop should be specified")),
4169 }),
4170 properties: fnet_routes_ext::RouteProperties {
4171 specified_properties: fnet_routes_ext::SpecifiedRouteProperties {
4172 metric: netlink_priority_to_specified_metric(metric, I::VERSION),
4173 },
4174 },
4175 }
4176 }
4177
4178 fn to_installed_route(
4179 self,
4180 table_id: fnet_routes_ext::TableId,
4181 ) -> fnet_routes_ext::InstalledRoute<I> {
4182 const DEFAULT_INTERFACE_METRIC: u32 = 100000;
4183 let effective_metric = match self.metric {
4184 None => DEFAULT_INTERFACE_METRIC,
4185 Some(metric) => metric.into(),
4186 };
4187 let route = self.to_route();
4188 fnet_routes_ext::InstalledRoute {
4189 route,
4190 effective_properties: fnet_routes_ext::EffectiveRouteProperties {
4191 metric: effective_metric,
4192 },
4193 table_id,
4194 }
4195 }
4196 }
4197
4198 const ROUTE_METRIC1: NonZeroU32 = NonZeroU32::new(METRIC1).unwrap();
4199 const ROUTE_METRIC2: NonZeroU32 = NonZeroU32::new(METRIC2).unwrap();
4200 const ROUTE_METRIC3: NonZeroU32 = NonZeroU32::new(METRIC3).unwrap();
4201
4202 #[test_case(
4203 Route::<Ipv4>{
4204 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4205 },
4206 Route::<Ipv4>{
4207 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4208 },
4209 true; "all_fields_the_same_v4_should_match")]
4210 #[test_case(
4211 Route::<Ipv6>{
4212 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4213 },
4214 Route::<Ipv6>{
4215 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4216 },
4217 true; "all_fields_the_same_v6_should_match")]
4218 #[test_case(
4219 Route::<Ipv4>{
4220 subnet: V4_DFLT, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4221 },
4222 Route::<Ipv4>{
4223 subnet: V4_DFLT, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4224 },
4225 true; "default_route_v4_should_match")]
4226 #[test_case(
4227 Route::<Ipv6>{
4228 subnet: V6_DFLT, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4229 },
4230 Route::<Ipv6>{
4231 subnet: V6_DFLT, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4232 },
4233 true; "default_route_v6_should_match")]
4234 #[test_case(
4235 Route::<Ipv4>{
4236 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4237 },
4238 Route::<Ipv4>{
4239 subnet: V4_SUB1, device: DEV2, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4240 },
4241 true; "different_device_v4_should_match")]
4242 #[test_case(
4243 Route::<Ipv6>{
4244 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4245 },
4246 Route::<Ipv6>{
4247 subnet: V6_SUB1, device: DEV2, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4248 },
4249 true; "different_device_v6_should_match")]
4250 #[test_case(
4251 Route::<Ipv4>{
4252 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4253 },
4254 Route::<Ipv4>{
4255 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP2), metric: Some(ROUTE_METRIC1),
4256 },
4257 true; "different_nexthop_v4_should_match")]
4258 #[test_case(
4259 Route::<Ipv6>{
4260 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4261 },
4262 Route::<Ipv6>{
4263 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP2), metric: Some(ROUTE_METRIC1),
4264 },
4265 true; "different_nexthop_v6_should_match")]
4266 #[test_case(
4267 Route::<Ipv4>{
4268 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4269 },
4270 Route::<Ipv4>{
4271 subnet: V4_SUB1, device: DEV2, nexthop: Some(V4_NEXTHOP2), metric: Some(ROUTE_METRIC1),
4272 },
4273 true; "different_device_and_nexthop_v4_should_match")]
4274 #[test_case(
4275 Route::<Ipv6>{
4276 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4277 },
4278 Route::<Ipv6>{
4279 subnet: V6_SUB1, device: DEV2, nexthop: Some(V6_NEXTHOP2), metric: Some(ROUTE_METRIC1),
4280 },
4281 true; "different_device_and_nexthop_v6_should_match")]
4282 #[test_case(
4283 Route::<Ipv4>{
4284 subnet: V4_SUB1, device: DEV1, nexthop: None, metric: Some(ROUTE_METRIC1),
4285 },
4286 Route::<Ipv4>{
4287 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4288 },
4289 true; "nexthop_newly_unset_v4_should_match")]
4290 #[test_case(
4291 Route::<Ipv6>{
4292 subnet: V6_SUB1, device: DEV1, nexthop: None, metric: Some(ROUTE_METRIC1),
4293 },
4294 Route::<Ipv6>{
4295 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4296 },
4297 true; "nexthop_newly_unset_v6_should_match")]
4298 #[test_case(
4299 Route::<Ipv4>{
4300 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4301 },
4302 Route::<Ipv4>{
4303 subnet: V4_SUB1, device: DEV1, nexthop: None, metric: Some(ROUTE_METRIC1),
4304 },
4305 true; "nexthop_previously_unset_v4_should_match")]
4306 #[test_case(
4307 Route::<Ipv6>{
4308 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4309 },
4310 Route::<Ipv6>{
4311 subnet: V6_SUB1, device: DEV1, nexthop: None, metric: Some(ROUTE_METRIC1),
4312 },
4313 true; "nexthop_previously_unset_v6_should_match")]
4314 #[test_case(
4315 Route::<Ipv4>{
4316 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4317 },
4318 Route::<Ipv4>{
4319 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC2),
4320 },
4321 false; "different_metric_v4_should_not_match")]
4322 #[test_case(
4323 Route::<Ipv4>{
4324 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: None,
4325 },
4326 Route::<Ipv4>{
4327 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC2),
4328 },
4329 false; "default_and_non_default_v4_should_not_match")]
4330 #[test_case(
4331 Route::<Ipv4>{
4332 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: None,
4333 },
4334 Route::<Ipv4>{
4335 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: None,
4336 },
4337 true; "default_and_default_v4_should_match")]
4338 #[test_case(
4339 Route::<Ipv6>{
4340 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4341 },
4342 Route::<Ipv6>{
4343 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC2),
4344 },
4345 false; "different_metric_v6_should_not_match")]
4346 #[test_case(
4347 Route::<Ipv4>{
4348 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4349 },
4350 Route::<Ipv4>{
4351 subnet: V4_SUB2, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4352 },
4353 false; "different_subnet_v4_should_not_match")]
4354 #[test_case(
4355 Route::<Ipv6>{
4356 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4357 },
4358 Route::<Ipv6>{
4359 subnet: V6_SUB2, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4360 },
4361 false; "different_subnet_v6_should_not_match")]
4362 #[test_case(
4363 Route::<Ipv4>{
4364 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4365 },
4366 Route::<Ipv4>{
4367 subnet: V4_SUB3, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4368 },
4369 false; "different_subnet_prefixlen_v4_should_not_match")]
4370 #[test_case(
4371 Route::<Ipv6>{
4372 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4373 },
4374 Route::<Ipv6>{
4375 subnet: V6_SUB3, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4376 },
4377 false; "different_subnet_prefixlen_v6_should_not_match")]
4378 #[test_case(
4379 Route::<Ipv6>{
4380 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: None,
4381 },
4382 Route::<Ipv6>{
4383 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC2),
4384 },
4385 false; "default_and_non_default_v6_should_not_match")]
4386 #[test_case(
4387 Route::<Ipv6>{
4388 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: None,
4389 },
4390 Route::<Ipv6>{
4391 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: None,
4392 },
4393 true; "default_and_default_v6_should_match")]
4394 fn test_new_route_matcher<I: Ip>(
4395 route1: Route<I>,
4396 route2: Route<I>,
4397 expected_to_conflict: bool,
4398 ) {
4399 let route1 = route1.to_installed_route(MAIN_FIDL_TABLE_ID);
4400 let route2 = route2.to_installed_route(MAIN_FIDL_TABLE_ID);
4401
4402 let got_conflict = routes_conflict::<I>(route1, route2.route, route2.table_id);
4403 assert_eq!(got_conflict, expected_to_conflict);
4404
4405 let got_conflict = routes_conflict::<I>(route2, route1.route, route1.table_id);
4406 assert_eq!(got_conflict, expected_to_conflict);
4407 }
4408
4409 fn test_select_route_for_deletion_helper<
4414 I: Ip + fnet_routes_ext::admin::FidlRouteAdminIpExt + fnet_routes_ext::FidlRouteIpExt,
4415 >(
4416 args: UnicastDelRouteArgs<I>,
4417 existing_routes: &[Route<I>],
4418 expected_index: Option<usize>,
4420 ) {
4421 let mut fidl_route_map = FidlRouteMap::<I>::default();
4422
4423 let _executor = fuchsia_async::TestExecutor::new();
4426
4427 let (main_route_table_proxy, _server_end) =
4428 fidl::endpoints::create_proxy::<I::RouteTableMarker>();
4429 let (own_route_table_proxy, _server_end) =
4430 fidl::endpoints::create_proxy::<I::RouteTableMarker>();
4431 let (route_set_proxy, _server_end) = fidl::endpoints::create_proxy::<I::RouteSetMarker>();
4432 let (unmanaged_route_set_proxy, _unmanaged_route_set_server_end) =
4433 fidl::endpoints::create_proxy::<I::RouteSetMarker>();
4434 let (route_table_provider, _server_end) =
4435 fidl::endpoints::create_proxy::<I::RouteTableProviderMarker>();
4436
4437 let mut route_table_map = RouteTableMap::<I>::new(
4438 main_route_table_proxy,
4439 MAIN_FIDL_TABLE_ID,
4440 unmanaged_route_set_proxy,
4441 route_table_provider,
4442 );
4443
4444 route_table_map.insert(
4445 MANAGED_ROUTE_TABLE_INDEX,
4446 RouteTable::Managed(ManagedRouteTable {
4447 route_table_proxy: own_route_table_proxy,
4448 route_set_proxy,
4449 fidl_table_id: OTHER_FIDL_TABLE_ID,
4450 rule_set_authenticated: false,
4451 }),
4452 );
4453
4454 for Route { subnet, device, nexthop, metric } in existing_routes {
4455 let fnet_routes_ext::InstalledRoute { route, effective_properties, table_id } =
4456 create_installed_route::<I>(
4457 *subnet,
4458 *nexthop,
4459 (*device).into(),
4460 metric.map_or(0, NonZeroU32::get),
4461 OTHER_FIDL_TABLE_ID,
4462 );
4463 assert_matches!(fidl_route_map.add(route, table_id, effective_properties), None);
4464 }
4465
4466 let existing_routes = existing_routes
4467 .iter()
4468 .map(|Route { subnet, device, nexthop, metric }| {
4469 let destination = (subnet.prefix() != 0).then_some(*subnet);
4471 create_netlink_route_message::<I>(
4472 subnet.prefix(),
4473 MANAGED_ROUTE_TABLE_INDEX,
4474 create_nlas::<I>(
4475 destination,
4476 nexthop.to_owned(),
4477 *device,
4478 metric.map_or(0, NonZeroU32::get),
4479 Some(MANAGED_ROUTE_TABLE_ID),
4480 ),
4481 )
4482 })
4483 .collect::<Vec<_>>();
4484 let expected_route = expected_index.map(|index| {
4485 existing_routes
4486 .get(index)
4487 .expect("index should be within the bounds of `existing_routes`")
4488 .clone()
4489 });
4490
4491 assert_eq!(
4492 select_route_for_deletion(
4493 &fidl_route_map,
4494 &route_table_map,
4495 DelRouteArgs::Unicast(args).try_into().unwrap(),
4496 ),
4497 expected_route
4498 )
4499 }
4500
4501 #[test_case(
4502 UnicastDelRouteArgs::<Ipv4> {
4503 subnet: V4_SUB1, outbound_interface: None, next_hop: None, priority: None,
4504 table: NonZeroNetlinkRouteTableIndex::new_non_zero(
4505 NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()
4506 ),
4507 },
4508 Route::<Ipv4>{
4509 subnet: V4_SUB2, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4510 },
4511 false; "subnet_does_not_match_v4")]
4512 #[test_case(
4513 UnicastDelRouteArgs::<Ipv4> {
4514 subnet: V4_SUB1, outbound_interface: None, next_hop: None, priority: None,
4515 table: NonZeroNetlinkRouteTableIndex::new_non_zero(
4516 NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()
4517 ),
4518 },
4519 Route::<Ipv4>{
4520 subnet: V4_SUB3, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4521 },
4522 false; "subnet_prefix_len_does_not_match_v4")]
4523 #[test_case(
4524 UnicastDelRouteArgs::<Ipv4> {
4525 subnet: V4_SUB1, outbound_interface: None, next_hop: None, priority: None,
4526 table: NonZeroNetlinkRouteTableIndex::new_non_zero(
4527 NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()
4528 ),
4529 },
4530 Route::<Ipv4>{
4531 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4532 },
4533 true; "subnet_matches_v4")]
4534 #[test_case(
4535 UnicastDelRouteArgs::<Ipv4> {
4536 subnet: V4_SUB1, outbound_interface: Some(NonZeroU64::new(DEV1.into()).unwrap()),
4537 next_hop: None, priority: None, table: NonZeroNetlinkRouteTableIndex::new_non_zero(
4538 NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()
4539 ),
4540 },
4541 Route::<Ipv4>{
4542 subnet: V4_SUB1, device: DEV2, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4543 },
4544 false; "interface_does_not_match_v4")]
4545 #[test_case(
4546 UnicastDelRouteArgs::<Ipv4> {
4547 subnet: V4_SUB1, outbound_interface: Some(NonZeroU64::new(DEV1.into()).unwrap()),
4548 next_hop: None, priority: None, table: NonZeroNetlinkRouteTableIndex::new_non_zero(
4549 NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()
4550 ),
4551 },
4552 Route::<Ipv4>{
4553 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4554 },
4555 true; "interface_matches_v4")]
4556 #[test_case(
4557 UnicastDelRouteArgs::<Ipv4> {
4558 subnet: V4_SUB1, outbound_interface: None,
4559 next_hop: Some(SpecifiedAddr::new(V4_NEXTHOP1).unwrap()), priority: None,
4560 table: NonZeroNetlinkRouteTableIndex::new_non_zero(
4561 NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()
4562 ),
4563 },
4564 Route::<Ipv4>{
4565 subnet: V4_SUB1, device: DEV1, nexthop: None, metric: Some(ROUTE_METRIC1),
4566 },
4567 false; "nexthop_absent_v4")]
4568 #[test_case(
4569 UnicastDelRouteArgs::<Ipv4> {
4570 subnet: V4_SUB1, outbound_interface: None,
4571 next_hop: Some(SpecifiedAddr::new(V4_NEXTHOP1).unwrap()), priority: None,
4572 table: NonZeroNetlinkRouteTableIndex::new_non_zero(
4573 NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()
4574 ),
4575 },
4576 Route::<Ipv4>{
4577 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP2), metric: Some(ROUTE_METRIC1),
4578 },
4579 false; "nexthop_does_not_match_v4")]
4580 #[test_case(
4581 UnicastDelRouteArgs::<Ipv4> {
4582 subnet: V4_SUB1, outbound_interface: None,
4583 next_hop: Some(SpecifiedAddr::new(V4_NEXTHOP1).unwrap()), priority: None,
4584 table: NonZeroNetlinkRouteTableIndex::new_non_zero(
4585 NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()
4586 ),
4587 },
4588 Route::<Ipv4>{
4589 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4590 },
4591 true; "nexthop_matches_v4")]
4592 #[test_case(
4593 UnicastDelRouteArgs::<Ipv4> {
4594 subnet: V4_SUB1, outbound_interface: None,
4595 next_hop: None, priority: Some(NonZeroU32::new(METRIC1).unwrap()),
4596 table: NonZeroNetlinkRouteTableIndex::new_non_zero(
4597 NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()
4598 ),
4599 },
4600 Route::<Ipv4>{
4601 subnet: V4_SUB1, device: DEV1, nexthop: None, metric: Some(ROUTE_METRIC2),
4602 },
4603 false; "metric_does_not_match_v4")]
4604 #[test_case(
4605 UnicastDelRouteArgs::<Ipv4> {
4606 subnet: V4_SUB1, outbound_interface: None,
4607 next_hop: None, priority: Some(NonZeroU32::new(METRIC1).unwrap()),
4608 table: NonZeroNetlinkRouteTableIndex::new_non_zero(
4609 NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()
4610 ),
4611 },
4612 Route::<Ipv4>{
4613 subnet: V4_SUB1, device: DEV1, nexthop: None, metric: Some(ROUTE_METRIC1),
4614 },
4615 true; "metric_matches_v4")]
4616 #[test_case(
4617 UnicastDelRouteArgs::<Ipv6> {
4618 subnet: V6_SUB1, outbound_interface: None, next_hop: None, priority: None,
4619 table: NonZeroNetlinkRouteTableIndex::new_non_zero(
4620 NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()
4621 ),
4622 },
4623 Route::<Ipv6>{
4624 subnet: V6_SUB2, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4625 },
4626 false; "subnet_does_not_match_v6")]
4627 #[test_case(
4628 UnicastDelRouteArgs::<Ipv6> {
4629 subnet: V6_SUB1, outbound_interface: None, next_hop: None, priority: None,
4630 table: NonZeroNetlinkRouteTableIndex::new_non_zero(
4631 NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()
4632 ),
4633 },
4634 Route::<Ipv6>{
4635 subnet: V6_SUB3, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4636 },
4637 false; "subnet_prefix_len_does_not_match_v6")]
4638 #[test_case(
4639 UnicastDelRouteArgs::<Ipv6> {
4640 subnet: V6_SUB1, outbound_interface: None, next_hop: None, priority: None,
4641 table: NonZeroNetlinkRouteTableIndex::new_non_zero(
4642 NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()
4643 ),
4644 },
4645 Route::<Ipv6>{
4646 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4647 },
4648 true; "subnet_matches_v6")]
4649 #[test_case(
4650 UnicastDelRouteArgs::<Ipv6> {
4651 subnet: V6_SUB1, outbound_interface: Some(NonZeroU64::new(DEV1.into()).unwrap()),
4652 next_hop: None, priority: None, table: NonZeroNetlinkRouteTableIndex::new_non_zero(
4653 NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()
4654 ),
4655 },
4656 Route::<Ipv6>{
4657 subnet: V6_SUB1, device: DEV2, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4658 },
4659 false; "interface_does_not_match_v6")]
4660 #[test_case(
4661 UnicastDelRouteArgs::<Ipv6> {
4662 subnet: V6_SUB1, outbound_interface: Some(NonZeroU64::new(DEV1.into()).unwrap()),
4663 next_hop: None, priority: None, table: NonZeroNetlinkRouteTableIndex::new_non_zero(
4664 NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()
4665 ),
4666 },
4667 Route::<Ipv6>{
4668 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4669 },
4670 true; "interface_matches_v6")]
4671 #[test_case(
4672 UnicastDelRouteArgs::<Ipv6> {
4673 subnet: V6_SUB1, outbound_interface: None,
4674 next_hop: Some(SpecifiedAddr::new(V6_NEXTHOP1).unwrap()), priority: None,
4675 table: NonZeroNetlinkRouteTableIndex::new_non_zero(
4676 NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()
4677 ),
4678 },
4679 Route::<Ipv6>{
4680 subnet: V6_SUB1, device: DEV1, nexthop: None, metric: Some(ROUTE_METRIC1),
4681 },
4682 false; "nexthop_absent_v6")]
4683 #[test_case(
4684 UnicastDelRouteArgs::<Ipv6> {
4685 subnet: V6_SUB1, outbound_interface: None,
4686 next_hop: Some(SpecifiedAddr::new(V6_NEXTHOP1).unwrap()), priority: None,
4687 table: NonZeroNetlinkRouteTableIndex::new_non_zero(
4688 NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()
4689 ),
4690 },
4691 Route::<Ipv6>{
4692 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP2), metric: Some(ROUTE_METRIC1),
4693 },
4694 false; "nexthop_does_not_match_v6")]
4695 #[test_case(
4696 UnicastDelRouteArgs::<Ipv6> {
4697 subnet: V6_SUB1, outbound_interface: None,
4698 next_hop: Some(SpecifiedAddr::new(V6_NEXTHOP1).unwrap()), priority: None,
4699 table: NonZeroNetlinkRouteTableIndex::new_non_zero(
4700 NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()
4701 ),
4702 },
4703 Route::<Ipv6>{
4704 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4705 },
4706 true; "nexthop_matches_v6")]
4707 #[test_case(
4708 UnicastDelRouteArgs::<Ipv6> {
4709 subnet: V6_SUB1, outbound_interface: None,
4710 next_hop: None, priority: Some(NonZeroU32::new(METRIC1).unwrap()),
4711 table: NonZeroNetlinkRouteTableIndex::new_non_zero(
4712 NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()
4713 ),
4714 },
4715 Route::<Ipv6>{
4716 subnet: V6_SUB1, device: DEV1, nexthop: None, metric: Some(ROUTE_METRIC2),
4717 },
4718 false; "metric_does_not_match_v6")]
4719 #[test_case(
4720 UnicastDelRouteArgs::<Ipv6> {
4721 subnet: V6_SUB1, outbound_interface: None,
4722 next_hop: None, priority: Some(NonZeroU32::new(METRIC1).unwrap()),
4723 table: NonZeroNetlinkRouteTableIndex::new_non_zero(
4724 NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()
4725 ),
4726 },
4727 Route::<Ipv6>{
4728 subnet: V6_SUB1, device: DEV1, nexthop: None, metric: Some(ROUTE_METRIC1),
4729 },
4730 true; "metric_matches_v6")]
4731 fn test_select_route_for_deletion<
4732 I: Ip + fnet_routes_ext::admin::FidlRouteAdminIpExt + fnet_routes_ext::FidlRouteIpExt,
4733 >(
4734 args: UnicastDelRouteArgs<I>,
4735 existing_route: Route<I>,
4736 expect_match: bool,
4737 ) {
4738 test_select_route_for_deletion_helper(args, &[existing_route], expect_match.then_some(0))
4739 }
4740
4741 #[test_case(
4742 UnicastDelRouteArgs::<Ipv4> {
4743 subnet: V4_SUB1, outbound_interface: None, next_hop: None, priority: None,
4744 table: NonZeroNetlinkRouteTableIndex::new_non_zero(NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()),
4745 },
4746 &[
4747 Route::<Ipv4>{
4748 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC2),
4749 },
4750 Route::<Ipv4>{
4751 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4752 },
4753 Route::<Ipv4>{
4754 subnet: V4_SUB1, device: DEV1, nexthop: Some(V4_NEXTHOP1), metric: Some(ROUTE_METRIC3),
4755 },
4756 ],
4757 Some(1); "multiple_matches_prefers_lowest_metric_v4")]
4758 #[test_case(
4759 UnicastDelRouteArgs::<Ipv6> {
4760 subnet: V6_SUB1, outbound_interface: None, next_hop: None, priority: None,
4761 table: NonZeroNetlinkRouteTableIndex::new_non_zero(NonZeroU32::new(MANAGED_ROUTE_TABLE_INDEX.get()).unwrap()),
4762 },
4763 &[
4764 Route::<Ipv6>{
4765 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC2),
4766 },
4767 Route::<Ipv6>{
4768 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC1),
4769 },
4770 Route::<Ipv6>{
4771 subnet: V6_SUB1, device: DEV1, nexthop: Some(V6_NEXTHOP1), metric: Some(ROUTE_METRIC3),
4772 },
4773 ],
4774 Some(1); "multiple_matches_prefers_lowest_metric_v6")]
4775 fn test_select_route_for_deletion_multiple_matches<
4776 I: Ip + fnet_routes_ext::admin::FidlRouteAdminIpExt + fnet_routes_ext::FidlRouteIpExt,
4777 >(
4778 args: UnicastDelRouteArgs<I>,
4779 existing_routes: &[Route<I>],
4780 expected_index: Option<usize>,
4781 ) {
4782 test_select_route_for_deletion_helper(args, existing_routes, expected_index);
4783 }
4784
4785 #[ip_test(I, test = false)]
4786 #[fuchsia::test]
4787 async fn garbage_collects_empty_table<
4788 I: Ip + fnet_routes_ext::admin::FidlRouteAdminIpExt + fnet_routes_ext::FidlRouteIpExt,
4789 >() {
4790 let (_route_sink, route_client, async_work_drain_task) =
4791 crate::client::testutil::new_fake_client::<NetlinkRoute>(
4792 crate::client::testutil::CLIENT_ID_1,
4793 [ModernGroup(match I::VERSION {
4794 IpVersion::V4 => rtnetlink_groups_RTNLGRP_IPV4_ROUTE,
4795 IpVersion::V6 => rtnetlink_groups_RTNLGRP_IPV6_ROUTE,
4796 })],
4797 );
4798 let join_handle = fasync::Task::spawn(async_work_drain_task);
4799 {
4800 let route_client = route_client;
4802 let route_clients = ClientTable::default();
4803 route_clients.add_client(route_client.clone());
4804
4805 let Setup {
4806 event_loop_inputs,
4807 watcher_stream,
4808 route_sets: (main_route_table_server_end, route_table_provider_server_end),
4809 interfaces_request_stream: _,
4810 mut request_sink,
4811 async_work_sink: _,
4812 } = setup_with_route_clients_yielding_admin_server_ends::<I>(route_clients);
4813
4814 let mut main_route_table_fut = pin!(
4815 fnet_routes_ext::testutil::admin::serve_noop_route_sets_with_table_id::<I>(
4816 main_route_table_server_end,
4817 MAIN_FIDL_TABLE_ID
4818 )
4819 .fuse()
4820 );
4821
4822 let mut watcher_stream = pin!(watcher_stream.fuse());
4823 let mut route_table_provider_stream = route_table_provider_server_end.into_stream();
4824
4825 let mut event_loop = {
4826 let included_workers = match I::VERSION {
4827 IpVersion::V4 => crate::route_eventloop::IncludedWorkers {
4828 routes_v4: EventLoopComponent::Present(()),
4829 routes_v6: EventLoopComponent::Absent(Optional),
4830 interfaces: EventLoopComponent::Absent(Optional),
4831 rules_v4: EventLoopComponent::Absent(Optional),
4832 rules_v6: EventLoopComponent::Absent(Optional),
4833 nduseropt: EventLoopComponent::Absent(Optional),
4834 neighbors: EventLoopComponent::Absent(Optional),
4835 },
4836 IpVersion::V6 => crate::route_eventloop::IncludedWorkers {
4837 routes_v4: EventLoopComponent::Absent(Optional),
4838 routes_v6: EventLoopComponent::Present(()),
4839 interfaces: EventLoopComponent::Absent(Optional),
4840 rules_v4: EventLoopComponent::Absent(Optional),
4841 rules_v6: EventLoopComponent::Absent(Optional),
4842 nduseropt: EventLoopComponent::Absent(Optional),
4843 neighbors: EventLoopComponent::Absent(Optional),
4844 },
4845 };
4846
4847 let event_loop_fut = event_loop_inputs.initialize(included_workers).fuse();
4848 let watcher_fut = async {
4849 let watch_req =
4850 watcher_stream.by_ref().next().await.expect("should not have ended");
4851 fnet_routes_ext::testutil::handle_watch::<I>(
4853 watch_req,
4854 vec![fnet_routes_ext::Event::<I>::Idle.try_into().unwrap()],
4855 )
4856 }
4857 .fuse();
4858
4859 futures::select! {
4860 () = main_route_table_fut => unreachable!(),
4861 (event_loop, ()) = futures::future::join(
4862 event_loop_fut, watcher_fut
4863 ) => event_loop,
4864 }
4865 };
4866
4867 let (completer, mut initial_add_request_waiter) = oneshot::channel();
4868
4869 let new_route_args = NewRouteArgs::Unicast(I::map_ip_out(
4870 (),
4871 |()| {
4872 create_unicast_new_route_args(
4873 V4_SUB1,
4874 V4_NEXTHOP1,
4875 DEV1.into(),
4876 METRIC1,
4877 MANAGED_ROUTE_TABLE_INDEX,
4878 )
4879 },
4880 |()| {
4881 create_unicast_new_route_args(
4882 V6_SUB1,
4883 V6_NEXTHOP1,
4884 DEV1.into(),
4885 METRIC1,
4886 MANAGED_ROUTE_TABLE_INDEX,
4887 )
4888 },
4889 ));
4890 let expected_route = fnet_routes_ext::Route::<I>::from(new_route_args);
4891
4892 request_sink
4894 .try_send(
4895 Request {
4896 args: RequestArgs::Route(RouteRequestArgs::New(new_route_args)),
4897 sequence_number: TEST_SEQUENCE_NUMBER,
4898 client: route_client.clone(),
4899 completer,
4900 }
4901 .into(),
4902 )
4903 .expect("should succeed");
4904
4905 let (mut route_table_stream, mut route_set_stream) = {
4908 let event_loop_fut = event_loop.run_one_step_in_tests().fuse();
4909 let route_table_fut = async {
4910 let server_end = match I::into_route_table_provider_request(
4911 route_table_provider_stream
4912 .try_next()
4913 .await
4914 .expect("should not have ended")
4915 .expect("fidl error"),
4916 ) {
4917 fnet_routes_ext::admin::RouteTableProviderRequest::NewRouteTable {
4918 provider,
4919 options: _,
4920 control_handle: _,
4921 } => provider,
4922 r => panic!("unexpected request {r:?}"),
4923 };
4924 let mut route_table_stream = server_end.into_stream().boxed().fuse();
4925
4926 let request = I::into_route_table_request_result(
4927 route_table_stream.by_ref().next().await.expect("should not have ended"),
4928 )
4929 .expect("should not get error");
4930
4931 let responder = match request {
4932 RouteTableRequest::GetTableId { responder } => responder,
4933 _ => panic!("should be GetTableId"),
4934 };
4935 responder.send(OTHER_FIDL_TABLE_ID.get()).expect("should succeed");
4936
4937 let request = I::into_route_table_request_result(
4938 route_table_stream.by_ref().next().await.expect("should not have ended"),
4939 )
4940 .expect("should not get error");
4941
4942 let server_end = match request {
4943 RouteTableRequest::NewRouteSet { route_set, control_handle: _ } => {
4944 route_set
4945 }
4946 _ => panic!("should be NewRouteSet"),
4947 };
4948 let mut route_set_stream = server_end.into_stream().boxed().fuse();
4949
4950 let request = I::into_route_set_request_result(
4951 route_set_stream.by_ref().next().await.expect("should not have ended"),
4952 )
4953 .expect("should not get error");
4954
4955 let (route, responder) = match request {
4956 RouteSetRequest::AddRoute { route, responder } => (route, responder),
4957 _ => panic!("should be AddRoute"),
4958 };
4959 let route = route.expect("should successfully convert FIDl");
4960 assert_eq!(route, expected_route);
4961
4962 responder.send(Ok(true)).expect("sending response should succeed");
4963 (route_table_stream, route_set_stream)
4964 }
4965 .fuse();
4966 futures::select! {
4967 () = main_route_table_fut => unreachable!(),
4968 ((), streams) = futures::future::join(event_loop_fut, route_table_fut) => {
4969 streams
4970 }
4971 }
4972 };
4973
4974 {
4975 let (routes_worker, route_table_map) = event_loop.route_table_state::<I>();
4976 let table = match route_table_map.get(&MANAGED_ROUTE_TABLE_INDEX) {
4978 Some(RouteTable::Managed(table)) => table,
4979 _ => panic!("table should be present"),
4980 };
4981 assert_eq!(table.fidl_table_id, OTHER_FIDL_TABLE_ID);
4982
4983 assert!(routes_worker.fidl_route_map.route_is_uninstalled_in_tables(
4986 &expected_route,
4987 [&OTHER_FIDL_TABLE_ID, &MAIN_FIDL_TABLE_ID]
4988 ));
4989 }
4990
4991 assert_matches!(initial_add_request_waiter.try_recv(), Ok(None));
4993
4994 {
4996 let event_loop_fut = async {
4997 event_loop.run_one_step_in_tests().await;
4999 event_loop.run_one_step_in_tests().await;
5000 }
5001 .fuse();
5002 let watcher_fut = async {
5003 let watch_req =
5004 watcher_stream.by_ref().next().await.expect("should not have ended");
5005 fnet_routes_ext::testutil::handle_watch::<I>(
5007 watch_req,
5008 vec![
5009 fnet_routes_ext::Event::<I>::Added(fnet_routes_ext::InstalledRoute {
5010 route: expected_route,
5011 effective_properties: fnet_routes_ext::EffectiveRouteProperties {
5012 metric: METRIC1,
5013 },
5014 table_id: MAIN_FIDL_TABLE_ID,
5015 })
5016 .try_into()
5017 .unwrap(),
5018 fnet_routes_ext::Event::<I>::Added(fnet_routes_ext::InstalledRoute {
5019 route: expected_route,
5020 effective_properties: fnet_routes_ext::EffectiveRouteProperties {
5021 metric: METRIC1,
5022 },
5023 table_id: OTHER_FIDL_TABLE_ID,
5024 })
5025 .try_into()
5026 .unwrap(),
5027 ],
5028 );
5029 };
5030 let ((), ()) = futures::join!(event_loop_fut, watcher_fut);
5031 }
5032
5033 {
5034 let (routes_worker, _route_table_map) = event_loop.route_table_state::<I>();
5035
5036 assert!(routes_worker.fidl_route_map.route_is_installed_in_tables(
5038 &expected_route,
5039 [&OTHER_FIDL_TABLE_ID, &MAIN_FIDL_TABLE_ID]
5040 ));
5041 }
5042
5043 assert_matches!(initial_add_request_waiter.try_recv(), Ok(Some(Ok(()))));
5044
5045 let (completer, mut del_request_waiter) = oneshot::channel();
5046
5047 let del_route_args = I::map_ip_out(
5048 (),
5049 |()| {
5050 create_unicast_del_route_args(
5051 V4_SUB1,
5052 Some(V4_NEXTHOP1),
5053 Some(DEV1.into()),
5054 Some(METRIC1),
5055 MANAGED_ROUTE_TABLE_INDEX,
5056 )
5057 },
5058 |()| {
5059 create_unicast_del_route_args(
5060 V6_SUB1,
5061 Some(V6_NEXTHOP1),
5062 Some(DEV1.into()),
5063 Some(METRIC1),
5064 MANAGED_ROUTE_TABLE_INDEX,
5065 )
5066 },
5067 );
5068
5069 request_sink
5071 .try_send(
5072 Request {
5073 args: RequestArgs::Route(RouteRequestArgs::Del(DelRouteArgs::Unicast(
5074 del_route_args,
5075 ))),
5076 sequence_number: TEST_SEQUENCE_NUMBER,
5077 client: route_client.clone(),
5078 completer,
5079 }
5080 .into(),
5081 )
5082 .expect("should succeed");
5083
5084 {
5086 let event_loop_fut = event_loop.run_one_step_in_tests().fuse();
5087 let route_set_fut = async {
5088 let request = I::into_route_set_request_result(
5089 route_set_stream.next().await.expect("should not have ended"),
5090 )
5091 .expect("should not get error");
5092 let (route, responder) = match request {
5093 RouteSetRequest::RemoveRoute { route, responder } => (route, responder),
5094 _ => panic!("should be DelRoute"),
5095 };
5096 let route = route.expect("should successfully convert FIDl");
5097 assert_eq!(route, expected_route);
5098
5099 responder.send(Ok(true)).expect("sending response should succeed");
5100 }
5101 .fuse();
5102
5103 futures::select! {
5104 () = main_route_table_fut => unreachable!(),
5105 ((), ()) = futures::future::join(event_loop_fut, route_set_fut) => (),
5106 }
5107 }
5108
5109 {
5111 let (routes_worker, route_table_map) = event_loop.route_table_state::<I>();
5112 let table = match route_table_map.get(&MANAGED_ROUTE_TABLE_INDEX) {
5114 Some(RouteTable::Managed(table)) => table,
5115 _ => panic!("table should be present"),
5116 };
5117 assert_eq!(table.fidl_table_id, OTHER_FIDL_TABLE_ID);
5118
5119 assert!(routes_worker.fidl_route_map.route_is_installed_in_tables(
5120 &expected_route,
5121 [&OTHER_FIDL_TABLE_ID, &MAIN_FIDL_TABLE_ID]
5122 ));
5123 }
5124 assert_matches!(del_request_waiter.try_recv(), Ok(None));
5125
5126 {
5128 let event_loop_fut = async {
5129 event_loop.run_one_step_in_tests().await;
5131 event_loop.run_one_step_in_tests().await;
5132 }
5133 .fuse();
5134 let watcher_fut = async {
5135 let watch_req =
5136 watcher_stream.by_ref().next().await.expect("should not have ended");
5137 fnet_routes_ext::testutil::handle_watch::<I>(
5139 watch_req,
5140 vec![
5141 fnet_routes_ext::Event::<I>::Removed(fnet_routes_ext::InstalledRoute {
5142 route: expected_route,
5143 effective_properties: fnet_routes_ext::EffectiveRouteProperties {
5144 metric: 0,
5145 },
5146 table_id: MAIN_FIDL_TABLE_ID,
5147 })
5148 .try_into()
5149 .unwrap(),
5150 fnet_routes_ext::Event::<I>::Removed(fnet_routes_ext::InstalledRoute {
5151 route: expected_route,
5152 effective_properties: fnet_routes_ext::EffectiveRouteProperties {
5153 metric: 0,
5154 },
5155 table_id: OTHER_FIDL_TABLE_ID,
5156 })
5157 .try_into()
5158 .unwrap(),
5159 ],
5160 );
5161 };
5162 let ((), ()) = futures::join!(event_loop_fut, watcher_fut);
5163 }
5164
5165 {
5166 let (routes_worker, route_table_map) = event_loop.route_table_state::<I>();
5167
5168 assert!(routes_worker.fidl_route_map.route_is_uninstalled_in_tables(
5170 &expected_route,
5171 [&OTHER_FIDL_TABLE_ID, &MAIN_FIDL_TABLE_ID]
5172 ));
5173
5174 assert_matches!(route_table_map.get(&MANAGED_ROUTE_TABLE_INDEX), None);
5176 }
5177 assert_matches!(del_request_waiter.try_recv(), Ok(Some(Ok(()))));
5178
5179 let route_table_request = route_table_stream.next().await;
5182 assert!(route_table_request.is_none());
5183 };
5184 join_handle.await;
5185 }
5186}