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