1use std::collections::BTreeMap;
10use std::fmt::Debug;
11use std::net::IpAddr;
12use std::num::{NonZeroU32, NonZeroU64};
13
14use fidl_fuchsia_net_ext::IntoExt as _;
15use fidl_fuchsia_net_interfaces_admin::{
16 self as fnet_interfaces_admin, AddressRemovalReason, InterfaceRemovedReason,
17};
18use fidl_fuchsia_net_interfaces_ext::admin::{
19 AddressStateProviderError, TerminalError, wait_for_address_added_event,
20};
21use fidl_fuchsia_net_interfaces_ext::{self as fnet_interfaces_ext, Update as _};
22use {
23 fidl_fuchsia_net as fnet, fidl_fuchsia_net_interfaces as fnet_interfaces,
24 fidl_fuchsia_net_root as fnet_root,
25};
26
27use derivative::Derivative;
28use either::Either;
29use futures::StreamExt as _;
30use futures::channel::oneshot;
31use linux_uapi::{
32 ARPHRD_6LOWPAN, ARPHRD_ETHER, ARPHRD_LOOPBACK, ARPHRD_PPP, ARPHRD_VOID,
33 net_device_flags_IFF_LOOPBACK, net_device_flags_IFF_LOWER_UP, net_device_flags_IFF_RUNNING,
34 net_device_flags_IFF_UP, rtnetlink_groups_RTNLGRP_IPV4_IFADDR,
35 rtnetlink_groups_RTNLGRP_IPV6_IFADDR, rtnetlink_groups_RTNLGRP_LINK,
36};
37use net_types::ip::{AddrSubnetEither, IpVersion, Ipv4, Ipv6};
38use netlink_packet_core::{NLM_F_MULTIPART, NetlinkMessage};
39use netlink_packet_route::address::{
40 AddressAttribute, AddressFlags, AddressHeader, AddressHeaderFlags, AddressMessage,
41};
42use netlink_packet_route::link::{
43 LinkAttribute, LinkFlags, LinkHeader, LinkLayerType, LinkMessage, State,
44};
45use netlink_packet_route::{AddressFamily, RouteNetlinkMessage};
46
47use crate::SysctlError;
48use crate::client::{ClientTable, InternalClient};
49use crate::logging::{log_debug, log_error, log_warn};
50use crate::messaging::Sender;
51use crate::multicast_groups::ModernGroup;
52use crate::netlink_packet::UNSPECIFIED_SEQUENCE_NUMBER;
53use crate::netlink_packet::errno::Errno;
54use crate::protocol_family::ProtocolFamily;
55use crate::protocol_family::route::NetlinkRoute;
56use crate::route_tables::{NetlinkRouteTableIndex, RouteTable, RouteTableMap, UnmanagedTable};
57use crate::util::respond_to_completer;
58
59pub trait InterfacesHandler: Send + Sync + 'static {
61 fn handle_new_link(&mut self, name: &str, interface_id: NonZeroU64);
63
64 fn handle_deleted_link(&mut self, name: &str);
66
67 fn handle_idle_event(&mut self) {}
69}
70
71#[derive(Clone, Debug, PartialEq, Eq)]
73pub(crate) enum LinkSpecifier {
74 Index(NonZeroU32),
75 Name(String),
76}
77
78#[derive(Clone, Debug, PartialEq, Eq)]
80pub(crate) enum GetLinkArgs {
81 Dump,
83 Get(LinkSpecifier),
85}
86
87#[derive(Clone, Debug, PartialEq, Eq)]
89pub(crate) struct SetLinkArgs {
90 pub(crate) link: LinkSpecifier,
92 pub(crate) enable: Option<bool>,
95}
96
97#[derive(Clone, Debug, PartialEq, Eq)]
99pub(crate) enum LinkRequestArgs {
100 Get(GetLinkArgs),
102 Set(SetLinkArgs),
104}
105
106#[derive(Copy, Clone, Debug, PartialEq, Eq)]
108pub(crate) enum GetAddressArgs {
109 Dump { ip_version_filter: Option<IpVersion> },
111 }
114
115#[derive(Copy, Clone, Debug, PartialEq, Eq)]
117pub(crate) struct AddressAndInterfaceArgs {
118 pub address: AddrSubnetEither,
119 pub interface_id: NonZeroU32,
120}
121
122#[derive(Copy, Clone, Debug, PartialEq, Eq)]
124pub(crate) struct NewAddressArgs {
125 pub address_and_interface_id: AddressAndInterfaceArgs,
127 pub add_subnet_route: bool,
130}
131
132#[derive(Copy, Clone, Debug, PartialEq, Eq)]
134pub(crate) struct DelAddressArgs {
135 pub address_and_interface_id: AddressAndInterfaceArgs,
137}
138
139#[derive(Copy, Clone, Debug, PartialEq, Eq)]
141pub(crate) enum AddressRequestArgs {
142 Get(GetAddressArgs),
144 New(NewAddressArgs),
146 #[allow(unused)]
148 Del(DelAddressArgs),
149}
150
151#[derive(Clone, Debug, PartialEq, Eq)]
153pub(crate) enum RequestArgs {
154 Link(LinkRequestArgs),
155 Address(AddressRequestArgs),
156}
157
158#[derive(Copy, Clone, Debug, PartialEq, Eq)]
160pub(crate) enum RequestError {
161 Unknown,
162 InvalidRequest,
163 UnrecognizedInterface,
164 AlreadyExists,
165 AddressNotFound,
166}
167
168impl RequestError {
169 pub(crate) fn into_errno(self) -> Errno {
170 match self {
171 RequestError::Unknown => {
172 log_error!("observed an unknown error, reporting `EINVAL` as the best guess");
173 Errno::EINVAL
174 }
175 RequestError::InvalidRequest => Errno::EINVAL,
176 RequestError::UnrecognizedInterface => Errno::ENODEV,
177 RequestError::AlreadyExists => Errno::EEXIST,
178 RequestError::AddressNotFound => Errno::EADDRNOTAVAIL,
179 }
180 }
181}
182
183fn map_existing_interface_terminal_error(
184 e: TerminalError<InterfaceRemovedReason>,
185 interface_id: NonZeroU64,
186) -> RequestError {
187 match e {
188 TerminalError::Fidl(e) => {
189 if !e.is_closed() {
192 log_error!(
193 "unexpected interface terminal error for interface ({:?}): {:?}",
194 interface_id,
195 e,
196 )
197 }
198 }
199 TerminalError::Terminal(reason) => match reason {
200 reason @ (InterfaceRemovedReason::DuplicateName
201 | InterfaceRemovedReason::PortAlreadyBound
202 | InterfaceRemovedReason::BadPort) => {
203 unreachable!(
206 "unexpected interface removed reason {:?} for interface ({:?})",
207 reason, interface_id,
208 )
209 }
210 InterfaceRemovedReason::PortClosed | InterfaceRemovedReason::User => {
211 }
214 reason => {
215 log_error!(
223 "unrecognized removal reason {:?} from interface {:?}",
224 reason,
225 interface_id
226 )
227 }
228 },
229 }
230
231 RequestError::UnrecognizedInterface
232}
233
234#[derive(Derivative)]
236#[derivative(Debug(bound = ""))]
237pub(crate) struct Request<S: Sender<<NetlinkRoute as ProtocolFamily>::Response>> {
238 pub args: RequestArgs,
240 pub sequence_number: u32,
245 pub client: InternalClient<NetlinkRoute, S>,
247 pub completer: oneshot::Sender<Result<(), RequestError>>,
249}
250
251pub(crate) struct InterfacesWorkerState<H, S: Sender<<NetlinkRoute as ProtocolFamily>::Response>> {
256 interfaces_handler: H,
258 interfaces_proxy: fnet_root::InterfacesProxy,
260 route_clients: ClientTable<NetlinkRoute, S>,
262 pub(crate) interface_properties: BTreeMap<
265 u64,
266 fnet_interfaces_ext::PropertiesAndState<InterfaceState, fnet_interfaces_ext::AllInterest>,
267 >,
268 pub(crate) default_accept_ra_rt_table: AcceptRaRtTable,
271 pub(crate) all_accept_ra_rt_table: AcceptRaRtTable,
274}
275
276#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
285pub(crate) enum AcceptRaRtTable {
286 #[default]
288 Main,
289 Manual(u32),
291 Auto(u32),
293}
294
295impl From<i32> for AcceptRaRtTable {
296 fn from(val: i32) -> Self {
297 if val == 0 {
298 Self::Main
299 } else if val > 0 {
300 Self::Manual(val.unsigned_abs())
301 } else {
302 Self::Auto(val.unsigned_abs())
303 }
304 }
305}
306
307impl From<AcceptRaRtTable> for i32 {
308 fn from(val: AcceptRaRtTable) -> Self {
309 match val {
310 AcceptRaRtTable::Main => 0,
311 AcceptRaRtTable::Manual(val) => i32::try_from(val).expect("larger than i32::MAX"),
312 AcceptRaRtTable::Auto(val) => {
313 0i32.checked_sub_unsigned(val).expect("less than i32::MIN")
314 }
315 }
316 }
317}
318
319#[derive(Debug, Default, Clone)]
320pub(crate) struct InterfaceState {
321 addresses: BTreeMap<fnet::IpAddress, NetlinkAddressMessage>,
324 link_address: Option<Vec<u8>>,
325 control: Option<fnet_interfaces_ext::admin::Control>,
326 accept_ra_rt_table: AcceptRaRtTable,
327}
328
329impl InterfaceState {
330 pub(crate) fn accept_ra_rt_table(&self) -> AcceptRaRtTable {
331 self.accept_ra_rt_table
332 }
333
334 pub(crate) async fn set_accept_ra_rt_table(
335 &mut self,
336 interface_id: NonZeroU64,
337 new_accept_ra_rt_table: AcceptRaRtTable,
338 interfaces_proxy: &fnet_root::InterfacesProxy,
339 route_table_maps: Option<(&mut RouteTableMap<Ipv4>, &mut RouteTableMap<Ipv6>)>,
340 ) -> Result<(), SysctlError> {
341 let old_accept_ra_rt_table = self.accept_ra_rt_table;
342 if old_accept_ra_rt_table == new_accept_ra_rt_table {
343 return Ok(());
344 }
345
346 enum InsertOrRemove {
347 Insert,
348 Remove,
349 }
350
351 let (delta, insert_or_remove) = match (old_accept_ra_rt_table, new_accept_ra_rt_table) {
352 (AcceptRaRtTable::Main, AcceptRaRtTable::Auto(delta)) => {
353 (delta, InsertOrRemove::Insert)
354 }
355 (AcceptRaRtTable::Auto(delta), AcceptRaRtTable::Main) => {
356 (delta, InsertOrRemove::Remove)
357 }
358 (from, to) => {
359 log::error!("unsupported transition from {from:?} to {to:?}");
360 return Err(SysctlError::Unsupported);
361 }
362 };
363
364 let netlink_id = match u32::try_from(interface_id.get()) {
365 Ok(i) => {
366 NetlinkRouteTableIndex::new(i.checked_add(delta).ok_or(SysctlError::Unsupported)?)
367 }
368 Err(std::num::TryFromIntError { .. }) => {
369 log::error!(
370 "not using local route table for interface \
371 {interface_id:?} because it is not representable in u32"
372 );
373 return Err(SysctlError::Unsupported);
374 }
375 };
376
377 self.accept_ra_rt_table = new_accept_ra_rt_table;
378
379 let Some((v4_route_table_map, v6_route_table_map)) = route_table_maps else {
380 return Ok(());
381 };
382 let control = self.control(interfaces_proxy, interface_id);
383 match insert_or_remove {
384 InsertOrRemove::Insert => {
385 let result = futures::future::try_join(
386 UnmanagedTable::<Ipv4>::interface_local(
387 control,
388 &v4_route_table_map.route_table_provider(),
389 ),
390 UnmanagedTable::<Ipv6>::interface_local(
391 control,
392 &v6_route_table_map.route_table_provider(),
393 ),
394 )
395 .await;
396 match result {
397 Ok((local_table_v4, local_table_v6)) => {
398 log::info!(
399 "local table mapping for {interface_id}: \
400 {netlink_id:?} -> ({:?}, {:?})",
401 local_table_v4.fidl_table_id,
402 local_table_v6.fidl_table_id,
403 );
404 v4_route_table_map
405 .insert(netlink_id, RouteTable::Unmanaged(local_table_v4));
406 v6_route_table_map
407 .insert(netlink_id, RouteTable::Unmanaged(local_table_v6));
408 }
409 Err(err) => {
410 log::info!("failed to get a local table for {interface_id}: {err:?}");
411 }
412 }
413 }
414 InsertOrRemove::Remove => {
415 let _: Option<_> = v4_route_table_map.remove(netlink_id);
416 let _: Option<_> = v6_route_table_map.remove(netlink_id);
417 }
418 }
419 Ok(())
420 }
421
422 pub(crate) fn control(
423 &mut self,
424 interfaces_proxy: &fnet_root::InterfacesProxy,
425 interface_id: NonZeroU64,
426 ) -> &fnet_interfaces_ext::admin::Control {
427 self.control.get_or_insert_with(|| {
428 let (control, server_end) = fnet_interfaces_ext::admin::Control::create_endpoints()
429 .expect("create Control endpoints");
430 interfaces_proxy
431 .get_admin(interface_id.get(), server_end)
432 .expect("send get admin request");
433 control
434 })
435 }
436}
437
438async fn set_link_address(
439 interfaces_proxy: &fnet_root::InterfacesProxy,
440 id: NonZeroU64,
441 link_address: &mut Option<Vec<u8>>,
442) {
443 match interfaces_proxy
444 .get_mac(id.get())
445 .await
446 .expect("netstack should never close its end of `fuchsia.net.root/Interfaces`")
447 {
448 Ok(None) => {
449 log_debug!("no MAC address for interface ({id:?})")
451 }
452 Ok(Some(mac)) => {
453 let fnet::MacAddress { octets } = *mac;
454 assert_eq!(link_address.replace(octets.to_vec()), None)
455 }
456 Err(fnet_root::InterfacesGetMacError::NotFound) => {
457 log_warn!("failed to get MAC address for interface ({id:?}) with not found error")
461 }
462 }
463}
464
465#[derive(Clone, Copy, Debug)]
466enum PendingRequestKind {
467 AddAddress(AddressAndInterfaceArgs),
468 DelAddress(AddressAndInterfaceArgs),
469 DisableInterface(NonZeroU64),
470 }
473
474#[derive(Derivative)]
475#[derivative(Debug(bound = ""))]
476pub(crate) struct PendingRequest<S: Sender<<NetlinkRoute as ProtocolFamily>::Response>> {
477 kind: PendingRequestKind,
478 client: InternalClient<NetlinkRoute, S>,
479 completer: oneshot::Sender<Result<(), RequestError>>,
480}
481
482impl<H: InterfacesHandler, S: Sender<<NetlinkRoute as ProtocolFamily>::Response>>
483 InterfacesWorkerState<H, S>
484{
485 pub(crate) async fn create(
492 mut interfaces_handler: H,
493 route_clients: ClientTable<NetlinkRoute, S>,
494 interfaces_proxy: fnet_root::InterfacesProxy,
495 interfaces_state_proxy: fnet_interfaces::StateProxy,
496 ) -> (
497 Self,
498 impl futures::Stream<
499 Item = Result<
500 fnet_interfaces_ext::EventWithInterest<fnet_interfaces_ext::AllInterest>,
501 fidl::Error,
502 >,
503 > + 'static,
504 ) {
505 let mut if_event_stream = Box::pin(
506 fnet_interfaces_ext::event_stream_from_state(
507 &interfaces_state_proxy,
508 fnet_interfaces_ext::WatchOptions {
509 included_addresses: fnet_interfaces_ext::IncludedAddresses::All,
510 ..Default::default()
511 },
512 )
513 .expect("connecting to fuchsia.net.interfaces.State FIDL should succeed"),
514 );
515
516 let mut interface_properties = fnet_interfaces_ext::existing(
517 if_event_stream.by_ref(),
518 BTreeMap::<u64, fnet_interfaces_ext::PropertiesAndState<InterfaceState, _>>::new(),
519 )
520 .await
521 .expect("determining already installed interfaces should succeed");
522
523 for fnet_interfaces_ext::PropertiesAndState { properties, state } in
524 interface_properties.values_mut()
525 {
526 set_link_address(&interfaces_proxy, properties.id, &mut state.link_address).await;
527
528 if let Some(interface_addresses) =
529 addresses_optionally_from_interface_properties(properties)
530 {
531 state.addresses = interface_addresses;
532 }
533
534 interfaces_handler.handle_new_link(&properties.name, properties.id);
535 }
536
537 interfaces_handler.handle_idle_event();
538
539 (
540 InterfacesWorkerState {
541 interfaces_handler,
542 interfaces_proxy,
543 route_clients,
544 interface_properties,
545 default_accept_ra_rt_table: Default::default(),
546 all_accept_ra_rt_table: Default::default(),
547 },
548 if_event_stream,
549 )
550 }
551
552 pub(crate) async fn handle_interface_watcher_event(
560 &mut self,
561 event: fnet_interfaces_ext::EventWithInterest<fnet_interfaces_ext::AllInterest>,
562 route_table_maps: Option<(&mut RouteTableMap<Ipv4>, &mut RouteTableMap<Ipv6>)>,
563 ) {
564 let update = self
565 .interface_properties
566 .update(event)
567 .expect("Netstack interface event resulted in an invalid update");
568
569 match update {
570 fnet_interfaces_ext::UpdateResult::Added { properties, state } => {
571 set_link_address(&self.interfaces_proxy, properties.id, &mut state.link_address)
572 .await;
573
574 let interface_id = properties.id;
575
576 let initial_value = self.default_accept_ra_rt_table;
578 state
579 .set_accept_ra_rt_table(
580 interface_id,
581 initial_value,
582 &self.interfaces_proxy,
583 route_table_maps,
584 )
585 .await
586 .unwrap_or_else(|_err| {
587 log::error!("failed to update the accept_ra_rt_table for {interface_id:?}")
588 });
589
590 if let Some(message) =
591 NetlinkLinkMessage::optionally_from(properties, &state.link_address)
592 {
593 self.route_clients.send_message_to_group(
594 message.into_rtnl_new_link(UNSPECIFIED_SEQUENCE_NUMBER, false),
595 ModernGroup(rtnetlink_groups_RTNLGRP_LINK),
596 )
597 }
598
599 if let Some(updated_addresses) =
603 addresses_optionally_from_interface_properties(properties)
604 {
605 update_addresses(&mut state.addresses, updated_addresses, &self.route_clients);
606 }
607
608 self.interfaces_handler.handle_new_link(&properties.name, properties.id);
609
610 log_debug!("processed add/existing event for id {}", properties.id);
611 }
612 fnet_interfaces_ext::UpdateResult::Changed {
613 previous:
614 fnet_interfaces::Properties {
615 online,
616 addresses,
617 id: _,
618 name: _,
619 has_default_ipv4_route: _,
620 has_default_ipv6_route: _,
621 port_class: _,
622 ..
623 },
624 current:
625 current @ fnet_interfaces_ext::Properties {
626 id,
627 addresses: _,
628 name: _,
629 port_class: _,
630 online: _,
631 has_default_ipv4_route: _,
632 has_default_ipv6_route: _,
633 port_identity_koid: _,
634 },
635 state:
636 InterfaceState {
637 addresses: interface_addresses,
638 link_address,
639 control: _,
640 accept_ra_rt_table: _,
641 },
642 } => {
643 if online.is_some() {
644 if let Some(message) =
645 NetlinkLinkMessage::optionally_from(current, link_address)
646 {
647 self.route_clients.send_message_to_group(
648 message.into_rtnl_new_link(UNSPECIFIED_SEQUENCE_NUMBER, false),
649 ModernGroup(rtnetlink_groups_RTNLGRP_LINK),
650 )
651 }
652
653 log_debug!("processed interface link change event for id {}", id);
654 };
655
656 if addresses.is_some() {
662 if let Some(updated_addresses) =
663 addresses_optionally_from_interface_properties(current)
664 {
665 update_addresses(
666 interface_addresses,
667 updated_addresses,
668 &self.route_clients,
669 );
670 }
671
672 log_debug!("processed interface address change event for id {}", id);
673 }
674 }
675 fnet_interfaces_ext::UpdateResult::Removed(
676 fnet_interfaces_ext::PropertiesAndState {
677 properties,
678 state:
679 InterfaceState {
680 mut addresses,
681 link_address,
682 control: _,
683 accept_ra_rt_table: _,
684 },
685 },
686 ) => {
687 update_addresses(&mut addresses, BTreeMap::new(), &self.route_clients);
688
689 if let Some(message) =
693 NetlinkLinkMessage::optionally_from(&properties, &link_address)
694 {
695 self.route_clients.send_message_to_group(
696 message.into_rtnl_del_link(UNSPECIFIED_SEQUENCE_NUMBER),
697 ModernGroup(rtnetlink_groups_RTNLGRP_LINK),
698 )
699 }
700
701 self.interfaces_handler.handle_deleted_link(&properties.name);
702
703 log_debug!("processed interface remove event for id {}", properties.id);
704 }
705 fnet_interfaces_ext::UpdateResult::Existing { properties, state: _ } => {
706 panic!("Netstack reported the addition of an existing interface: {properties:?}");
707 }
708 fnet_interfaces_ext::UpdateResult::NoChange => {}
709 }
710 }
711
712 pub(crate) fn handle_pending_request(
716 &self,
717 pending_request: PendingRequest<S>,
718 ) -> Option<PendingRequest<S>> {
719 let PendingRequest { kind, client: _, completer: _ } = &pending_request;
720 let contains_addr = |&AddressAndInterfaceArgs { address, interface_id }| {
721 let fnet_interfaces_ext::PropertiesAndState {
727 properties: _,
728 state:
729 InterfaceState { addresses, link_address: _, control: _, accept_ra_rt_table: _ },
730 } = self
731 .interface_properties
732 .get(&interface_id.get().into())
733 .expect("interfaces with pending address change should exist");
734 let fnet::Subnet { addr, prefix_len: _ } = address.clone().into_ext();
735 addresses.contains_key(&addr)
736 };
737
738 let done = match kind {
739 PendingRequestKind::AddAddress(address_and_interface_args) => {
740 contains_addr(address_and_interface_args)
741 }
742 PendingRequestKind::DelAddress(address_and_interface_args) => {
743 !contains_addr(address_and_interface_args)
744 }
745 PendingRequestKind::DisableInterface(interface_id) => {
746 let fnet_interfaces_ext::PropertiesAndState { properties, state: _ } =
751 self.interface_properties.get(&interface_id.get()).unwrap_or_else(|| {
752 panic!("interface {interface_id} with pending disable should exist")
753 });
754 !properties.online
763 }
764 };
765
766 if done {
767 log_debug!("completed pending request; req = {pending_request:?}");
768
769 let PendingRequest { kind, client, completer } = pending_request;
770
771 respond_to_completer(client, completer, Ok(()), kind);
772 None
773 } else {
774 log_debug!("pending request not done yet; req = {pending_request:?}");
776 Some(pending_request)
777 }
778 }
779
780 fn get_interface_control(
784 &mut self,
785 interface_id: NonZeroU64,
786 ) -> Option<&fnet_interfaces_ext::admin::Control> {
787 let interface = self.interface_properties.get_mut(&interface_id.get())?;
788
789 Some(interface.state.control(&self.interfaces_proxy, interface_id))
790 }
791
792 fn get_link(
794 &self,
795 specifier: LinkSpecifier,
796 ) -> Option<
797 &fnet_interfaces_ext::PropertiesAndState<InterfaceState, fnet_interfaces_ext::AllInterest>,
798 > {
799 match specifier {
800 LinkSpecifier::Index(id) => self.interface_properties.get(&id.get().into()),
801 LinkSpecifier::Name(name) => self.interface_properties.values().find(
802 |fnet_interfaces_ext::PropertiesAndState { properties, state: _ }| {
803 properties.name == name
804 },
805 ),
806 }
807 }
808
809 fn handle_get_link_request(
814 &self,
815 args: GetLinkArgs,
816 sequence_number: u32,
817 client: &mut InternalClient<NetlinkRoute, S>,
818 ) -> Result<(), RequestError> {
819 let (is_dump, interfaces_iter) = match args {
820 GetLinkArgs::Dump => {
821 let ifaces = self.interface_properties.values();
822 (true, Either::Left(ifaces))
823 }
824 GetLinkArgs::Get(specifier) => {
825 let iface = self.get_link(specifier).ok_or(RequestError::UnrecognizedInterface)?;
826 (false, Either::Right(std::iter::once(iface)))
827 }
828 };
829
830 interfaces_iter
831 .filter_map(
832 |fnet_interfaces_ext::PropertiesAndState {
833 properties,
834 state:
835 InterfaceState {
836 addresses: _,
837 link_address,
838 control: _,
839 accept_ra_rt_table: _,
840 },
841 }| {
842 NetlinkLinkMessage::optionally_from(&properties, &link_address)
843 },
844 )
845 .for_each(|message| {
846 client.send_unicast(message.into_rtnl_new_link(sequence_number, is_dump))
847 });
848 Ok(())
849 }
850
851 async fn handle_set_link_request(
853 &mut self,
854 args: SetLinkArgs,
855 ) -> Result<Option<PendingRequestKind>, RequestError> {
856 let SetLinkArgs { link, enable } = args;
857 let id = self.get_link(link).ok_or(RequestError::UnrecognizedInterface)?.properties.id;
858
859 let Some(enable) = enable else { return Ok(None) };
863
864 let control = self.get_interface_control(id).ok_or(RequestError::UnrecognizedInterface)?;
865
866 if enable {
867 let _did_enable = control
868 .enable()
869 .await
870 .map_err(|e| {
871 log_warn!("error enabling interface {id}: {e:?}");
872 map_existing_interface_terminal_error(e, id)
873 })?
874 .map_err(|e: fnet_interfaces_admin::ControlEnableError| {
875 log_error!("failed to enable interface {id} for unknown reason: {e:?}");
878 RequestError::Unknown
879 })?;
880 Ok(None)
884 } else {
885 let did_disable = control
886 .disable()
887 .await
888 .map_err(|e| {
889 log_warn!("error disabling interface {id}: {e:?}");
890 map_existing_interface_terminal_error(e, id)
891 })?
892 .map_err(|e: fnet_interfaces_admin::ControlDisableError| {
893 log_error!("failed to disable interface {id} for unknown reason: {e:?}");
896 RequestError::Unknown
897 })?;
898 Ok(did_disable.then_some(PendingRequestKind::DisableInterface(id)))
899 }
900 }
901
902 async fn handle_new_address_request(
908 &mut self,
909 NewAddressArgs {
910 address_and_interface_id:
911 address_and_interface_id @ AddressAndInterfaceArgs { address, interface_id },
912 add_subnet_route,
913 }: NewAddressArgs,
914 ) -> Result<Option<AddressAndInterfaceArgs>, RequestError> {
915 let control = self
916 .get_interface_control(interface_id.into())
917 .ok_or(RequestError::UnrecognizedInterface)?;
918
919 let (asp, asp_server_end) =
920 fidl::endpoints::create_proxy::<fnet_interfaces_admin::AddressStateProviderMarker>();
921 control
922 .add_address(
923 &address.into_ext(),
924 &fnet_interfaces_admin::AddressParameters {
925 add_subnet_route: Some(add_subnet_route),
928 ..fnet_interfaces_admin::AddressParameters::default()
929 },
930 asp_server_end,
931 )
932 .map_err(|e| {
933 log_warn!("error adding {address} to interface ({interface_id}): {e:?}");
934 map_existing_interface_terminal_error(e, interface_id.into())
935 })?;
936
937 asp.detach().unwrap_or_else(|e| {
943 log_warn!(
947 "error detaching ASP for {} on interface ({}): {:?}",
948 address,
949 interface_id,
950 e
951 )
952 });
953
954 match wait_for_address_added_event(&mut asp.take_event_stream()).await {
955 Ok(()) => {
956 log_debug!("{} added on interface ({})", address, interface_id);
957 Ok(Some(address_and_interface_id))
958 }
959 Err(e) => {
960 log_warn!(
961 "error waiting for state update for {} on interface ({}): {:?}",
962 address,
963 interface_id,
964 e,
965 );
966
967 Err(match e {
968 AddressStateProviderError::AddressRemoved(reason) => match reason {
969 AddressRemovalReason::Invalid | AddressRemovalReason::InvalidProperties => {
970 RequestError::InvalidRequest
971 }
972 AddressRemovalReason::AlreadyAssigned => RequestError::AlreadyExists,
973 reason @ (AddressRemovalReason::DadFailed
974 | AddressRemovalReason::Forfeited
975 | AddressRemovalReason::InterfaceRemoved
976 | AddressRemovalReason::UserRemoved) => {
977 unreachable!(
982 "expected netstack to send initial state before removing {} on interface ({}) with reason {:?}",
983 address, interface_id, reason,
984 )
985 }
986 },
987 AddressStateProviderError::Fidl(e) => {
988 if !e.is_closed() {
989 log_error!(
990 "unexpected ASP error when adding {} on interface ({}): {:?}",
991 address,
992 interface_id,
993 e,
994 )
995 }
996
997 RequestError::UnrecognizedInterface
998 }
999 AddressStateProviderError::ChannelClosed => {
1000 RequestError::UnrecognizedInterface
1003 }
1004 })
1005 }
1006 }
1007 }
1008
1009 async fn handle_del_address_request(
1015 &mut self,
1016 DelAddressArgs {
1017 address_and_interface_id:
1018 address_and_interface_id @ AddressAndInterfaceArgs { address, interface_id },
1019 }: DelAddressArgs,
1020 ) -> Result<AddressAndInterfaceArgs, RequestError> {
1021 let control = self
1022 .get_interface_control(interface_id.into())
1023 .ok_or(RequestError::UnrecognizedInterface)?;
1024
1025 match control.remove_address(&address.into_ext()).await.map_err(|e| {
1026 log_warn!("error removing {address} from interface ({interface_id}): {e:?}");
1027 map_existing_interface_terminal_error(e, interface_id.into())
1028 })? {
1029 Ok(did_remove) => {
1030 if did_remove {
1031 Ok(address_and_interface_id)
1032 } else {
1033 Err(RequestError::AddressNotFound)
1034 }
1035 }
1036 Err(e) => {
1037 let e: fnet_interfaces_admin::ControlRemoveAddressError = e;
1039 match e {
1040 fnet_interfaces_admin::ControlRemoveAddressErrorUnknown!() => {
1041 log_error!(
1042 "unrecognized address removal error {:?} for address {} on interface ({})",
1043 e,
1044 address,
1045 interface_id,
1046 );
1047
1048 Err(RequestError::InvalidRequest)
1050 }
1051 }
1052 }
1053 }
1054 }
1055
1056 pub(crate) async fn handle_request(
1062 &mut self,
1063 Request { args, sequence_number, mut client, completer }: Request<S>,
1064 ) -> Option<PendingRequest<S>> {
1065 log_debug!("handling request {args:?} from {client}");
1066
1067 let result = match args.clone() {
1068 RequestArgs::Link(LinkRequestArgs::Get(args)) => {
1069 self.handle_get_link_request(args, sequence_number, &mut client)
1070 }
1071 RequestArgs::Link(LinkRequestArgs::Set(args)) => {
1072 match self.handle_set_link_request(args).await {
1073 Ok(Some(kind)) => return Some(PendingRequest { kind, client, completer }),
1074 Ok(None) => Ok(()),
1075 Err(e) => Err(e),
1076 }
1077 }
1078 RequestArgs::Address(args) => match args {
1079 AddressRequestArgs::Get(args) => match args {
1080 GetAddressArgs::Dump { ip_version_filter } => {
1081 self.interface_properties
1082 .values()
1083 .map(|iface| iface.state.addresses.values())
1084 .flatten()
1085 .filter(|NetlinkAddressMessage(message)| {
1086 ip_version_filter.map_or(true, |ip_version| {
1087 ip_version.eq(&match message.header.family() {
1088 AddressFamily::Inet => IpVersion::V4,
1089 AddressFamily::Inet6 => IpVersion::V6,
1090 family => unreachable!(
1091 "unexpected address family ({:?}); addr = {:?}",
1092 family, message,
1093 ),
1094 })
1095 })
1096 })
1097 .for_each(|message| {
1098 client.send_unicast(message.to_rtnl_new_addr(sequence_number, true))
1099 });
1100 Ok(())
1101 }
1102 },
1103 AddressRequestArgs::New(args) => {
1104 match self.handle_new_address_request(args).await {
1105 Ok(None) => Ok(()),
1106 Ok(Some(address_and_interface_id)) => {
1107 return Some(PendingRequest {
1108 kind: PendingRequestKind::AddAddress(address_and_interface_id),
1109 client,
1110 completer,
1111 });
1112 }
1113 Err(e) => Err(e),
1114 }
1115 }
1116 AddressRequestArgs::Del(args) => {
1117 match self.handle_del_address_request(args).await {
1118 Ok(address_and_interface_id) => {
1119 return Some(PendingRequest {
1120 kind: PendingRequestKind::DelAddress(address_and_interface_id),
1121 client,
1122 completer,
1123 });
1124 }
1125 Err(e) => Err(e),
1126 }
1127 }
1128 },
1129 };
1130
1131 log_debug!("handled request {args:?} from {client} with result = {result:?}");
1132 respond_to_completer(client, completer, result, args);
1133 None
1134 }
1135}
1136
1137fn update_addresses<S: Sender<<NetlinkRoute as ProtocolFamily>::Response>>(
1138 existing_addresses: &mut BTreeMap<fnet::IpAddress, NetlinkAddressMessage>,
1139 updated_addresses: BTreeMap<fnet::IpAddress, NetlinkAddressMessage>,
1140 route_clients: &ClientTable<NetlinkRoute, S>,
1141) {
1142 enum UpdateKind {
1143 New,
1144 Del,
1145 }
1146
1147 let send_update = |addr: &NetlinkAddressMessage, kind| {
1148 let NetlinkAddressMessage(inner) = addr;
1149 let group = match inner.header.family() {
1150 AddressFamily::Inet => rtnetlink_groups_RTNLGRP_IPV4_IFADDR,
1151 AddressFamily::Inet6 => rtnetlink_groups_RTNLGRP_IPV6_IFADDR,
1152 family => {
1153 unreachable!("unrecognized interface address family ({family:?}); addr = {addr:?}")
1154 }
1155 };
1156
1157 let message = match kind {
1158 UpdateKind::New => addr.to_rtnl_new_addr(UNSPECIFIED_SEQUENCE_NUMBER, false),
1159 UpdateKind::Del => addr.to_rtnl_del_addr(UNSPECIFIED_SEQUENCE_NUMBER),
1160 };
1161
1162 route_clients.send_message_to_group(message, ModernGroup(group));
1163 };
1164
1165 for (key, message) in updated_addresses.iter() {
1168 if existing_addresses.get(key) != Some(message) {
1169 send_update(message, UpdateKind::New)
1170 }
1171 }
1172
1173 existing_addresses.retain(|addr, message| {
1174 if updated_addresses.contains_key(addr) {
1177 return true;
1178 }
1179
1180 send_update(message, UpdateKind::Del);
1183
1184 false
1185 });
1186
1187 existing_addresses.extend(updated_addresses);
1190}
1191
1192#[derive(Clone, Debug, Eq, PartialEq)]
1196pub(crate) struct NetlinkLinkMessage(LinkMessage);
1197
1198impl NetlinkLinkMessage {
1199 fn optionally_from(
1200 properties: &fnet_interfaces_ext::Properties<fnet_interfaces_ext::AllInterest>,
1201 link_address: &Option<Vec<u8>>,
1202 ) -> Option<Self> {
1203 match interface_properties_to_link_message(properties, link_address) {
1204 Ok(o) => Some(o),
1205 Err(NetlinkLinkMessageConversionError::InvalidInterfaceId(id)) => {
1206 log_warn!("Invalid interface id: {:?}", id);
1207 None
1208 }
1209 }
1210 }
1211
1212 pub(crate) fn into_rtnl_new_link(
1213 self,
1214 sequence_number: u32,
1215 is_dump: bool,
1216 ) -> NetlinkMessage<RouteNetlinkMessage> {
1217 let Self(message) = self;
1218 let mut msg: NetlinkMessage<RouteNetlinkMessage> =
1219 RouteNetlinkMessage::NewLink(message).into();
1220 msg.header.sequence_number = sequence_number;
1221 if is_dump {
1222 msg.header.flags |= NLM_F_MULTIPART;
1223 }
1224 msg.finalize();
1225 msg
1226 }
1227
1228 fn into_rtnl_del_link(self, sequence_number: u32) -> NetlinkMessage<RouteNetlinkMessage> {
1229 let Self(message) = self;
1230 let mut msg: NetlinkMessage<RouteNetlinkMessage> =
1231 RouteNetlinkMessage::DelLink(message).into();
1232 msg.header.sequence_number = sequence_number;
1233 msg.finalize();
1234 msg
1235 }
1236}
1237
1238#[derive(Debug, PartialEq)]
1240pub(crate) enum NetlinkLinkMessageConversionError {
1241 InvalidInterfaceId(u64),
1243}
1244
1245fn port_class_to_link_type(port_class: fnet_interfaces_ext::PortClass) -> u16 {
1246 match port_class {
1247 fnet_interfaces_ext::PortClass::Loopback => ARPHRD_LOOPBACK,
1248 fnet_interfaces_ext::PortClass::Blackhole => ARPHRD_VOID,
1249 fnet_interfaces_ext::PortClass::Ethernet
1250 | fnet_interfaces_ext::PortClass::Bridge
1251 | fnet_interfaces_ext::PortClass::WlanClient
1252 | fnet_interfaces_ext::PortClass::WlanAp => ARPHRD_ETHER,
1253 fnet_interfaces_ext::PortClass::Ppp => ARPHRD_PPP,
1254 fnet_interfaces_ext::PortClass::Virtual => ARPHRD_VOID,
1258 fnet_interfaces_ext::PortClass::Lowpan => ARPHRD_6LOWPAN,
1259 }
1260 .try_into()
1261 .expect("potential values will fit into the u16 range")
1262}
1263
1264const ONLINE_IF_FLAGS: u32 =
1295 net_device_flags_IFF_UP | net_device_flags_IFF_RUNNING | net_device_flags_IFF_LOWER_UP;
1296
1297fn interface_properties_to_link_message(
1300 fnet_interfaces_ext::Properties {
1301 id,
1302 name,
1303 port_class,
1304 online,
1305 addresses: _,
1306 has_default_ipv4_route: _,
1307 has_default_ipv6_route: _,
1308 port_identity_koid: _,
1309 }: &fnet_interfaces_ext::Properties<fnet_interfaces_ext::AllInterest>,
1310 link_address: &Option<Vec<u8>>,
1311) -> Result<NetlinkLinkMessage, NetlinkLinkMessageConversionError> {
1312 let online = *online;
1313 let mut link_header = LinkHeader::default();
1314
1315 link_header.interface_family = AddressFamily::Unspec;
1316
1317 let id: u32 = match id.get().try_into() {
1319 Err(std::num::TryFromIntError { .. }) => {
1320 return Err(NetlinkLinkMessageConversionError::InvalidInterfaceId(id.clone().into()));
1321 }
1322 Ok(id) => id,
1323 };
1324 link_header.index = id;
1325
1326 let link_layer_type = port_class_to_link_type(*port_class);
1327 link_header.link_layer_type = LinkLayerType::from(link_layer_type);
1328
1329 let mut flags = 0;
1330 if online {
1331 flags |= ONLINE_IF_FLAGS;
1332 };
1333 if link_header.link_layer_type == LinkLayerType::Loopback {
1334 flags |= net_device_flags_IFF_LOOPBACK;
1335 };
1336 if *port_class == fnet_interfaces_ext::PortClass::WlanClient {
1337 flags |= net_device_flags_IFF_UP;
1343 }
1344
1345 link_header.flags = LinkFlags::from_bits(flags).unwrap();
1348
1349 link_header.change_mask = LinkFlags::from_bits(u32::MAX).unwrap();
1352
1353 let nlas = [
1368 LinkAttribute::IfName(name.clone()),
1369 LinkAttribute::Link(link_layer_type.into()),
1370 LinkAttribute::OperState(if online { State::Up } else { State::Down }),
1373 ]
1374 .into_iter()
1375 .chain(link_address.clone().map(LinkAttribute::Address))
1377 .collect();
1378
1379 let mut link_message = LinkMessage::default();
1380 link_message.header = link_header;
1381 link_message.attributes = nlas;
1382
1383 return Ok(NetlinkLinkMessage(link_message));
1384}
1385
1386#[derive(Clone, Debug, Eq, PartialEq)]
1389pub(crate) struct NetlinkAddressMessage(AddressMessage);
1390
1391impl NetlinkAddressMessage {
1392 pub(crate) fn to_rtnl_new_addr(
1393 &self,
1394 sequence_number: u32,
1395 is_dump: bool,
1396 ) -> NetlinkMessage<RouteNetlinkMessage> {
1397 let Self(message) = self;
1398 let mut message: NetlinkMessage<RouteNetlinkMessage> =
1399 RouteNetlinkMessage::NewAddress(message.clone()).into();
1400 message.header.sequence_number = sequence_number;
1401 if is_dump {
1402 message.header.flags |= NLM_F_MULTIPART;
1403 }
1404 message.finalize();
1405 message
1406 }
1407
1408 pub(crate) fn to_rtnl_del_addr(
1409 &self,
1410 sequence_number: u32,
1411 ) -> NetlinkMessage<RouteNetlinkMessage> {
1412 let Self(message) = self;
1413 let mut message: NetlinkMessage<RouteNetlinkMessage> =
1414 RouteNetlinkMessage::DelAddress(message.clone()).into();
1415 message.header.sequence_number = sequence_number;
1416 message.finalize();
1417 message
1418 }
1419}
1420
1421#[derive(Debug, PartialEq)]
1423enum NetlinkAddressMessageConversionError {
1424 InvalidInterfaceId(u64),
1426}
1427
1428fn addresses_optionally_from_interface_properties(
1429 properties: &fnet_interfaces_ext::Properties<fnet_interfaces_ext::AllInterest>,
1430) -> Option<BTreeMap<fnet::IpAddress, NetlinkAddressMessage>> {
1431 match interface_properties_to_address_messages(properties) {
1432 Ok(o) => Some(o),
1433 Err(NetlinkAddressMessageConversionError::InvalidInterfaceId(id)) => {
1434 log_warn!("Invalid interface id: {:?}", id);
1435 None
1436 }
1437 }
1438}
1439
1440fn interface_properties_to_address_messages(
1443 fnet_interfaces_ext::Properties {
1444 id,
1445 name,
1446 addresses,
1447 port_class: _,
1448 online: _,
1449 has_default_ipv4_route: _,
1450 has_default_ipv6_route: _,
1451 port_identity_koid: _,
1452 }: &fnet_interfaces_ext::Properties<fnet_interfaces_ext::AllInterest>,
1453) -> Result<BTreeMap<fnet::IpAddress, NetlinkAddressMessage>, NetlinkAddressMessageConversionError>
1454{
1455 let id: u32 = match id.get().try_into() {
1457 Err(std::num::TryFromIntError { .. }) => {
1458 return Err(NetlinkAddressMessageConversionError::InvalidInterfaceId(
1459 id.clone().into(),
1460 ));
1461 }
1462 Ok(id) => id,
1463 };
1464
1465 let address_messages = addresses
1466 .into_iter()
1467 .map(
1468 |fnet_interfaces_ext::Address {
1469 addr: fnet::Subnet { addr, prefix_len },
1470 valid_until: _,
1471 preferred_lifetime_info: _,
1472 assignment_state,
1473 }| {
1474 let mut addr_header = AddressHeader::default();
1475
1476 let (family, addr_bytes) = match addr {
1477 fnet::IpAddress::Ipv4(ip_addr) => {
1478 (AddressFamily::Inet, IpAddr::V4(ip_addr.addr.into()))
1479 }
1480 fnet::IpAddress::Ipv6(ip_addr) => {
1481 (AddressFamily::Inet6, IpAddr::V6(ip_addr.addr.into()))
1482 }
1483 };
1484
1485 addr_header.family = family.into();
1488 addr_header.prefix_len = *prefix_len;
1489
1490 let flags = AddressHeaderFlags::Permanent
1493 | match assignment_state {
1494 fnet_interfaces::AddressAssignmentState::Assigned => {
1495 AddressHeaderFlags::empty()
1496 }
1497 fnet_interfaces::AddressAssignmentState::Tentative
1498 | fnet_interfaces::AddressAssignmentState::Unavailable => {
1499 AddressHeaderFlags::Tentative
1504 }
1505 };
1506 addr_header.flags = flags.bits();
1507 addr_header.index = id.into();
1508
1509 let nlas = vec![
1523 AddressAttribute::Address(addr_bytes),
1524 AddressAttribute::Label(name.clone()),
1525 AddressAttribute::Flags(AddressFlags::from_bits(flags.bits().into()).unwrap()),
1528 ];
1529
1530 let mut addr_message = AddressMessage::default();
1531 addr_message.header = addr_header;
1532 addr_message.attributes = nlas;
1533 (addr.clone(), NetlinkAddressMessage(addr_message))
1534 },
1535 )
1536 .collect();
1537
1538 Ok(address_messages)
1539}
1540
1541#[cfg(test)]
1542pub(crate) mod testutil {
1543 use super::*;
1544
1545 use fuchsia_sync::Mutex;
1546 use std::convert::Infallible as Never;
1547 use std::sync::Arc;
1548
1549 use futures::TryStreamExt as _;
1550 use futures::channel::mpsc;
1551 use futures::future::Future;
1552 use futures::stream::Stream;
1553 use net_declare::{fidl_subnet, net_addr_subnet};
1554
1555 use crate::client::AsyncWorkItem;
1556 use crate::messaging::testutil::FakeSender;
1557 use crate::route_eventloop::{EventLoopComponent, IncludedWorkers, Optional, Required};
1558
1559 pub(crate) const LO_INTERFACE_ID: u64 = 1;
1560 pub(crate) const LO_NAME: &str = "lo";
1561 pub(crate) const ETH_INTERFACE_ID: u64 = 2;
1562 pub(crate) const ETH_NAME: &str = "eth";
1563 pub(crate) const WLAN_INTERFACE_ID: u64 = 3;
1564 pub(crate) const WLAN_NAME: &str = "wlan";
1565 pub(crate) const PPP_INTERFACE_ID: u64 = 4;
1566 pub(crate) const PPP_NAME: &str = "ppp";
1567
1568 pub(crate) const BRIDGE: fnet_interfaces_ext::PortClass =
1569 fnet_interfaces_ext::PortClass::Bridge;
1570 pub(crate) const ETHERNET: fnet_interfaces_ext::PortClass =
1571 fnet_interfaces_ext::PortClass::Ethernet;
1572 pub(crate) const WLAN_CLIENT: fnet_interfaces_ext::PortClass =
1573 fnet_interfaces_ext::PortClass::WlanClient;
1574 pub(crate) const WLAN_AP: fnet_interfaces_ext::PortClass =
1575 fnet_interfaces_ext::PortClass::WlanAp;
1576 pub(crate) const PPP: fnet_interfaces_ext::PortClass = fnet_interfaces_ext::PortClass::Ppp;
1577 pub(crate) const LOOPBACK: fnet_interfaces_ext::PortClass =
1578 fnet_interfaces_ext::PortClass::Loopback;
1579 pub(crate) const TEST_V4_ADDR: fnet::Subnet = fidl_subnet!("192.0.2.1/24");
1580 pub(crate) const TEST_V6_ADDR: fnet::Subnet = fidl_subnet!("2001:db8::1/32");
1581
1582 pub(crate) fn test_addr_subnet_v4() -> AddrSubnetEither {
1584 net_addr_subnet!("192.0.2.1/24")
1585 }
1586
1587 pub(crate) fn test_addr_subnet_v6() -> AddrSubnetEither {
1589 net_addr_subnet!("2001:db8::1/32")
1590 }
1591
1592 pub(crate) fn add_test_addr_subnet_v4() -> AddrSubnetEither {
1594 net_addr_subnet!("192.0.2.2/24")
1595 }
1596
1597 pub(crate) fn add_test_addr_subnet_v6() -> AddrSubnetEither {
1599 net_addr_subnet!("2001:db8::2/32")
1600 }
1601
1602 #[derive(Debug, PartialEq, Eq)]
1603 pub(crate) enum HandledLinkKind {
1604 New,
1605 Del,
1606 }
1607
1608 #[derive(Debug, PartialEq, Eq)]
1609 pub(crate) struct HandledLink {
1610 pub name: String,
1611 pub kind: HandledLinkKind,
1612 }
1613
1614 pub(crate) struct FakeInterfacesHandlerSink(Arc<Mutex<Vec<HandledLink>>>);
1615
1616 impl FakeInterfacesHandlerSink {
1617 pub(crate) fn take_handled(&mut self) -> Vec<HandledLink> {
1618 let Self(rc) = self;
1619 core::mem::take(&mut *rc.lock())
1620 }
1621 }
1622
1623 pub(crate) struct FakeInterfacesHandler(Arc<Mutex<Vec<HandledLink>>>);
1624
1625 impl FakeInterfacesHandler {
1626 pub(crate) fn new() -> (FakeInterfacesHandler, FakeInterfacesHandlerSink) {
1627 let inner = Arc::default();
1628 (FakeInterfacesHandler(Arc::clone(&inner)), FakeInterfacesHandlerSink(inner))
1629 }
1630 }
1631
1632 impl InterfacesHandler for FakeInterfacesHandler {
1633 fn handle_new_link(&mut self, name: &str, _interface_id: NonZeroU64) {
1634 let Self(rc) = self;
1635 rc.lock().push(HandledLink { name: name.to_string(), kind: HandledLinkKind::New })
1636 }
1637
1638 fn handle_deleted_link(&mut self, name: &str) {
1639 let Self(rc) = self;
1640 rc.lock().push(HandledLink { name: name.to_string(), kind: HandledLinkKind::Del })
1641 }
1642 }
1643
1644 enum OnlyInterfaces {}
1645 impl crate::route_eventloop::EventLoopSpec for OnlyInterfaces {
1646 type InterfacesProxy = Required;
1647 type InterfacesStateProxy = Required;
1648 type InterfacesHandler = Required;
1649 type RouteClients = Required;
1650
1651 type V4RoutesState = Optional;
1652 type V6RoutesState = Optional;
1653 type V4RoutesSetProvider = Optional;
1654 type V6RoutesSetProvider = Optional;
1655 type V4RouteTableProvider = Optional;
1656 type V6RouteTableProvider = Optional;
1657
1658 type InterfacesWorker = Required;
1659 type RoutesV4Worker = Optional;
1660 type RoutesV6Worker = Optional;
1661 type RuleV4Worker = Optional;
1662 type RuleV6Worker = Optional;
1663 type NduseroptWorker = Optional;
1664 }
1665
1666 pub(crate) struct Setup<E, W> {
1667 pub event_loop_fut: E,
1668 pub watcher_stream: W,
1669 pub request_sink:
1670 mpsc::Sender<crate::route_eventloop::UnifiedRequest<FakeSender<RouteNetlinkMessage>>>,
1671 pub interfaces_request_stream: fnet_root::InterfacesRequestStream,
1672 pub interfaces_handler_sink: FakeInterfacesHandlerSink,
1673 pub _async_work_sink: mpsc::UnboundedSender<AsyncWorkItem<NetlinkRoute>>,
1674 }
1675
1676 pub(crate) fn setup_with_route_clients(
1677 route_clients: ClientTable<NetlinkRoute, FakeSender<RouteNetlinkMessage>>,
1678 ) -> Setup<impl Future<Output = Never>, impl Stream<Item = fnet_interfaces::WatcherRequest>>
1679 {
1680 let (request_sink, request_stream) = mpsc::channel(1);
1681 let (interfaces_handler, interfaces_handler_sink) = FakeInterfacesHandler::new();
1682 let (interfaces_proxy, interfaces) =
1683 fidl::endpoints::create_proxy::<fnet_root::InterfacesMarker>();
1684 let (interfaces_state_proxy, interfaces_state) =
1685 fidl::endpoints::create_proxy::<fnet_interfaces::StateMarker>();
1686 let (async_work_sink, async_work_receiver) = mpsc::unbounded();
1687 let event_loop_inputs = crate::route_eventloop::EventLoopInputs::<_, _, OnlyInterfaces> {
1688 route_clients: EventLoopComponent::Present(route_clients),
1689 interfaces_handler: EventLoopComponent::Present(interfaces_handler),
1690 interfaces_proxy: EventLoopComponent::Present(interfaces_proxy),
1691 interfaces_state_proxy: EventLoopComponent::Present(interfaces_state_proxy),
1692 async_work_receiver,
1693
1694 v4_routes_state: EventLoopComponent::Absent(Optional),
1695 v6_routes_state: EventLoopComponent::Absent(Optional),
1696 v4_main_route_table: EventLoopComponent::Absent(Optional),
1697 v6_main_route_table: EventLoopComponent::Absent(Optional),
1698 v4_route_table_provider: EventLoopComponent::Absent(Optional),
1699 v6_route_table_provider: EventLoopComponent::Absent(Optional),
1700 v4_rule_table: EventLoopComponent::Absent(Optional),
1701 v6_rule_table: EventLoopComponent::Absent(Optional),
1702 ndp_option_watcher_provider: EventLoopComponent::Absent(Optional),
1703
1704 unified_request_stream: request_stream,
1705 };
1706
1707 let interfaces_request_stream = interfaces.into_stream();
1708 let if_stream = interfaces_state.into_stream();
1709 let watcher_stream = if_stream
1710 .and_then(|req| match req {
1711 fnet_interfaces::StateRequest::GetWatcher {
1712 options: _,
1713 watcher,
1714 control_handle: _,
1715 } => futures::future::ready(Ok(watcher.into_stream())),
1716 })
1717 .try_flatten()
1718 .map(|res| res.expect("watcher stream error"));
1719
1720 Setup {
1721 event_loop_fut: async move {
1722 let event_loop = event_loop_inputs
1723 .initialize(IncludedWorkers {
1724 interfaces: EventLoopComponent::Present(()),
1725 routes_v4: EventLoopComponent::Absent(Optional),
1726 routes_v6: EventLoopComponent::Absent(Optional),
1727 rules_v4: EventLoopComponent::Absent(Optional),
1728 rules_v6: EventLoopComponent::Absent(Optional),
1729 nduseropt: EventLoopComponent::Absent(Optional),
1730 })
1731 .await;
1732 event_loop.run().await
1733 },
1734 watcher_stream,
1735 request_sink,
1736 interfaces_request_stream,
1737 interfaces_handler_sink,
1738 _async_work_sink: async_work_sink,
1739 }
1740 }
1741
1742 pub(crate) async fn respond_to_watcher<S: Stream<Item = fnet_interfaces::WatcherRequest>>(
1743 stream: S,
1744 updates: impl IntoIterator<Item = fnet_interfaces::Event>,
1745 ) {
1746 stream
1747 .zip(futures::stream::iter(updates.into_iter()))
1748 .for_each(|(req, update)| async move {
1749 match req {
1750 fnet_interfaces::WatcherRequest::Watch { responder } => {
1751 responder.send(&update).expect("send watch response")
1752 }
1753 }
1754 })
1755 .await
1756 }
1757
1758 pub(crate) fn create_netlink_link_message(
1759 id: u64,
1760 link_type: u16,
1761 flags: u32,
1762 nlas: Vec<LinkAttribute>,
1763 ) -> NetlinkLinkMessage {
1764 let mut link_header = LinkHeader::default();
1765 link_header.index = id.try_into().expect("should fit into u32");
1766 link_header.link_layer_type = LinkLayerType::from(link_type);
1767 link_header.flags = LinkFlags::from_bits(flags).unwrap();
1768 link_header.change_mask = LinkFlags::from_bits(u32::MAX).unwrap();
1769
1770 let mut link_message = LinkMessage::default();
1771 link_message.header = link_header;
1772 link_message.attributes = nlas;
1773
1774 NetlinkLinkMessage(link_message)
1775 }
1776
1777 pub(crate) fn create_nlas(
1778 name: String,
1779 link_type: u16,
1780 online: bool,
1781 mac: &Option<fnet::MacAddress>,
1782 ) -> Vec<LinkAttribute> {
1783 [
1784 LinkAttribute::IfName(name),
1785 LinkAttribute::Link(link_type.into()),
1786 LinkAttribute::OperState(if online { State::Up } else { State::Down }),
1787 ]
1788 .into_iter()
1789 .chain(mac.map(|fnet::MacAddress { octets }| LinkAttribute::Address(octets.to_vec())))
1790 .collect()
1791 }
1792
1793 pub(crate) fn create_address_message(
1794 interface_id: u32,
1795 subnet: fnet::Subnet,
1796 interface_name: String,
1797 flags: u32,
1798 ) -> NetlinkAddressMessage {
1799 let mut addr_header = AddressHeader::default();
1800 let (family, addr) = match subnet.addr {
1801 fnet::IpAddress::Ipv4(ip_addr) => {
1802 (AddressFamily::Inet, IpAddr::V4(ip_addr.addr.into()))
1803 }
1804 fnet::IpAddress::Ipv6(ip_addr) => {
1805 (AddressFamily::Inet6, IpAddr::V6(ip_addr.addr.into()))
1806 }
1807 };
1808 addr_header.family = family.into();
1809 addr_header.prefix_len = subnet.prefix_len;
1810 addr_header.flags = AddressHeaderFlags::from_bits(flags as u8).unwrap().bits();
1811 addr_header.index = interface_id.into();
1812
1813 let nlas = vec![
1814 AddressAttribute::Address(addr),
1815 AddressAttribute::Label(interface_name),
1816 AddressAttribute::Flags(AddressFlags::from_bits(flags).unwrap()),
1817 ];
1818
1819 let mut addr_message = AddressMessage::default();
1820 addr_message.header = addr_header;
1821 addr_message.attributes = nlas;
1822 NetlinkAddressMessage(addr_message)
1823 }
1824
1825 pub(crate) fn test_addr_with_assignment_state(
1826 addr: fnet::Subnet,
1827 assignment_state: fnet_interfaces::AddressAssignmentState,
1828 ) -> fnet_interfaces::Address {
1829 fnet_interfaces_ext::Address::<fnet_interfaces_ext::AllInterest> {
1830 addr,
1831 valid_until: fnet_interfaces_ext::PositiveMonotonicInstant::INFINITE_FUTURE,
1832 preferred_lifetime_info: fnet_interfaces_ext::PreferredLifetimeInfo::preferred_forever(
1833 ),
1834 assignment_state,
1835 }
1836 .into()
1837 }
1838
1839 pub(crate) fn test_addr(addr: fnet::Subnet) -> fnet_interfaces::Address {
1840 test_addr_with_assignment_state(addr, fnet_interfaces::AddressAssignmentState::Assigned)
1841 }
1842}
1843
1844#[cfg(test)]
1845mod tests {
1846 use super::testutil::*;
1847 use super::*;
1848
1849 use std::pin::{Pin, pin};
1850
1851 use fidl::endpoints::{ControlHandle as _, RequestStream as _, Responder as _};
1852 use fidl_fuchsia_net as fnet;
1853 use fnet_interfaces::AddressAssignmentState;
1854 use fuchsia_async::{self as fasync};
1855
1856 use assert_matches::assert_matches;
1857 use futures::FutureExt as _;
1858 use futures::sink::SinkExt as _;
1859 use futures::stream::Stream;
1860 use linux_uapi::{IFA_F_PERMANENT, IFA_F_TENTATIVE, rtnetlink_groups_RTNLGRP_IPV4_ROUTE};
1861 use pretty_assertions::assert_eq;
1862 use test_case::test_case;
1863
1864 use crate::messaging::testutil::SentMessage;
1865
1866 const TEST_SEQUENCE_NUMBER: u32 = 1234;
1867
1868 fn create_interface(
1869 id: u64,
1870 name: String,
1871 port_class: fnet_interfaces_ext::PortClass,
1872 online: bool,
1873 addresses: Vec<fnet_interfaces_ext::Address<fnet_interfaces_ext::AllInterest>>,
1874 ) -> fnet_interfaces_ext::Properties<fnet_interfaces_ext::AllInterest> {
1875 fnet_interfaces_ext::Properties {
1876 id: NonZeroU64::new(id).unwrap(),
1877 name,
1878 port_class,
1879 online,
1880 addresses,
1881 has_default_ipv4_route: false,
1882 has_default_ipv6_route: false,
1883 port_identity_koid: None,
1884 }
1885 }
1886
1887 fn create_interface_with_addresses(
1888 id: u64,
1889 name: String,
1890 port_class: fnet_interfaces_ext::PortClass,
1891 online: bool,
1892 ) -> fnet_interfaces_ext::Properties<fnet_interfaces_ext::AllInterest> {
1893 let addresses = vec![
1894 fnet_interfaces_ext::Address {
1895 addr: TEST_V4_ADDR,
1896 valid_until: fnet_interfaces_ext::PositiveMonotonicInstant::INFINITE_FUTURE,
1897 assignment_state: AddressAssignmentState::Assigned,
1898 preferred_lifetime_info:
1899 fnet_interfaces_ext::PreferredLifetimeInfo::preferred_forever(),
1900 },
1901 fnet_interfaces_ext::Address {
1902 addr: TEST_V6_ADDR,
1903 valid_until: fnet_interfaces_ext::PositiveMonotonicInstant::INFINITE_FUTURE,
1904 assignment_state: AddressAssignmentState::Assigned,
1905 preferred_lifetime_info:
1906 fnet_interfaces_ext::PreferredLifetimeInfo::preferred_forever(),
1907 },
1908 ];
1909 create_interface(id, name, port_class, online, addresses)
1910 }
1911
1912 fn create_default_address_messages(
1913 interface_id: u64,
1914 interface_name: String,
1915 flags: u32,
1916 ) -> BTreeMap<fnet::IpAddress, NetlinkAddressMessage> {
1917 let interface_id = interface_id.try_into().expect("should fit into u32");
1918 BTreeMap::from_iter([
1919 (
1920 TEST_V4_ADDR.addr,
1921 create_address_message(interface_id, TEST_V4_ADDR, interface_name.clone(), flags),
1922 ),
1923 (
1924 TEST_V6_ADDR.addr,
1925 create_address_message(interface_id, TEST_V6_ADDR, interface_name, flags),
1926 ),
1927 ])
1928 }
1929
1930 #[test_case(ETHERNET, false, 0, ARPHRD_ETHER)]
1931 #[test_case(ETHERNET, true, ONLINE_IF_FLAGS, ARPHRD_ETHER)]
1932 #[test_case(WLAN_CLIENT, false, net_device_flags_IFF_UP, ARPHRD_ETHER)]
1933 #[test_case(WLAN_CLIENT, true, ONLINE_IF_FLAGS, ARPHRD_ETHER)]
1934 #[test_case(WLAN_AP, false, 0, ARPHRD_ETHER)]
1935 #[test_case(WLAN_AP, true, ONLINE_IF_FLAGS, ARPHRD_ETHER)]
1936 #[test_case(PPP, false, 0, ARPHRD_PPP)]
1937 #[test_case(PPP, true, ONLINE_IF_FLAGS, ARPHRD_PPP)]
1938 #[test_case(LOOPBACK, false, net_device_flags_IFF_LOOPBACK, ARPHRD_LOOPBACK)]
1939 #[test_case(LOOPBACK, true, ONLINE_IF_FLAGS | net_device_flags_IFF_LOOPBACK, ARPHRD_LOOPBACK)]
1940 #[test_case(BRIDGE, false, 0, ARPHRD_ETHER)]
1941 #[test_case(BRIDGE, true, ONLINE_IF_FLAGS, ARPHRD_ETHER)]
1942 fn test_interface_conversion(
1943 port_class: fnet_interfaces_ext::PortClass,
1944 online: bool,
1945 flags: u32,
1946 expected_link_type: u32,
1947 ) {
1948 let expected_link_type = expected_link_type as u16;
1951 let interface_name = LO_NAME.to_string();
1952 let interface =
1953 create_interface(LO_INTERFACE_ID, interface_name.clone(), port_class, online, vec![]);
1954 let actual: NetlinkLinkMessage =
1955 interface_properties_to_link_message(&interface, &LO_MAC.map(|a| a.octets.to_vec()))
1956 .unwrap();
1957
1958 let nlas = create_nlas(interface_name, expected_link_type, online, &LO_MAC);
1959 let expected =
1960 create_netlink_link_message(LO_INTERFACE_ID, expected_link_type, flags, nlas);
1961 pretty_assertions::assert_eq!(actual, expected);
1962 }
1963
1964 #[fuchsia::test]
1965 fn test_oversized_interface_id_link_address_conversion() {
1966 let invalid_interface_id = (u32::MAX as u64) + 1;
1967 let interface =
1968 create_interface(invalid_interface_id, "test".into(), ETHERNET, true, vec![]);
1969
1970 let actual_link_message = interface_properties_to_link_message(&interface, &None);
1971 assert_eq!(
1972 actual_link_message,
1973 Err(NetlinkLinkMessageConversionError::InvalidInterfaceId(invalid_interface_id))
1974 );
1975
1976 assert_eq!(
1977 interface_properties_to_address_messages(&interface),
1978 Err(NetlinkAddressMessageConversionError::InvalidInterfaceId(invalid_interface_id))
1979 );
1980 }
1981
1982 #[fuchsia::test]
1983 fn test_interface_to_address_conversion() {
1984 let interface_name: String = "test".into();
1985 let interface_id = 1;
1986
1987 let interface =
1988 create_interface_with_addresses(interface_id, interface_name.clone(), ETHERNET, true);
1989 let actual = interface_properties_to_address_messages(&interface).unwrap();
1990
1991 let expected =
1992 create_default_address_messages(interface_id, interface_name, IFA_F_PERMANENT);
1993 assert_eq!(actual, expected);
1994 }
1995
1996 #[test]
1997 fn test_into_rtnl_new_link_is_serializable() {
1998 let link = create_netlink_link_message(0, 0, 0, vec![]);
1999 let new_link_message = link.into_rtnl_new_link(UNSPECIFIED_SEQUENCE_NUMBER, false);
2000 let mut buf = vec![0; new_link_message.buffer_len()];
2001 new_link_message.serialize(&mut buf);
2003 }
2004
2005 #[test]
2006 fn test_into_rtnl_del_link_is_serializable() {
2007 let link = create_netlink_link_message(0, 0, 0, vec![]);
2008 let del_link_message = link.into_rtnl_del_link(UNSPECIFIED_SEQUENCE_NUMBER);
2009 let mut buf = vec![0; del_link_message.buffer_len()];
2010 del_link_message.serialize(&mut buf);
2012 }
2013
2014 #[fuchsia::test]
2015 async fn test_deliver_updates() {
2016 let (mut link_sink, link_client, _async_work_drain_task) =
2017 crate::client::testutil::new_fake_client::<NetlinkRoute>(
2018 crate::client::testutil::CLIENT_ID_1,
2019 [ModernGroup(rtnetlink_groups_RTNLGRP_LINK)],
2020 );
2021 let (mut addr4_sink, addr4_client, _async_work_drain_task) =
2022 crate::client::testutil::new_fake_client::<NetlinkRoute>(
2023 crate::client::testutil::CLIENT_ID_2,
2024 [ModernGroup(rtnetlink_groups_RTNLGRP_IPV4_IFADDR)],
2025 );
2026 let (mut addr6_sink, addr6_client, _async_work_drain_task) =
2027 crate::client::testutil::new_fake_client::<NetlinkRoute>(
2028 crate::client::testutil::CLIENT_ID_3,
2029 [ModernGroup(rtnetlink_groups_RTNLGRP_IPV6_IFADDR)],
2030 );
2031 let (mut other_sink, other_client, _async_work_drain_task) =
2032 crate::client::testutil::new_fake_client::<NetlinkRoute>(
2033 crate::client::testutil::CLIENT_ID_4,
2034 [ModernGroup(rtnetlink_groups_RTNLGRP_IPV4_ROUTE)],
2035 );
2036 let (mut all_sink, all_client, _async_work_drain_task) =
2037 crate::client::testutil::new_fake_client::<NetlinkRoute>(
2038 crate::client::testutil::CLIENT_ID_5,
2039 [
2040 ModernGroup(rtnetlink_groups_RTNLGRP_LINK),
2041 ModernGroup(rtnetlink_groups_RTNLGRP_IPV6_IFADDR),
2042 ModernGroup(rtnetlink_groups_RTNLGRP_IPV4_IFADDR),
2043 ],
2044 );
2045 let Setup {
2046 event_loop_fut,
2047 mut watcher_stream,
2048 request_sink: _,
2049 interfaces_request_stream,
2050 mut interfaces_handler_sink,
2051 _async_work_sink: _,
2052 } = setup_with_route_clients({
2053 let route_clients = ClientTable::default();
2054 route_clients.add_client(link_client);
2055 route_clients.add_client(addr4_client);
2056 route_clients.add_client(addr6_client);
2057 route_clients.add_client(other_client);
2058 route_clients.add_client(all_client);
2059 route_clients
2060 });
2061 let event_loop_fut = event_loop_fut.fuse();
2062 let mut event_loop_fut = pin!(event_loop_fut);
2063 let root_interfaces_fut =
2064 handle_only_get_mac_root_requests_fut(interfaces_request_stream).fuse();
2065 let mut root_interfaces_fut = pin!(root_interfaces_fut);
2066
2067 let watcher_stream_fut = respond_to_watcher(
2069 watcher_stream.by_ref(),
2070 [
2071 fnet_interfaces::Event::Existing(fnet_interfaces::Properties {
2072 id: Some(LO_INTERFACE_ID),
2073 name: Some(LO_NAME.to_string()),
2074 port_class: Some(LOOPBACK.into()),
2075 online: Some(false),
2076 addresses: Some(vec![test_addr_with_assignment_state(
2077 TEST_V4_ADDR,
2078 fnet_interfaces::AddressAssignmentState::Assigned,
2079 )]),
2080 has_default_ipv4_route: Some(false),
2081 has_default_ipv6_route: Some(false),
2082 ..Default::default()
2083 }),
2084 fnet_interfaces::Event::Existing(fnet_interfaces::Properties {
2085 id: Some(ETH_INTERFACE_ID),
2086 name: Some(ETH_NAME.to_string()),
2087 port_class: Some(ETHERNET.into()),
2088 online: Some(false),
2089 addresses: Some(vec![
2090 test_addr_with_assignment_state(
2091 TEST_V6_ADDR,
2092 fnet_interfaces::AddressAssignmentState::Unavailable,
2093 ),
2094 test_addr_with_assignment_state(
2095 TEST_V4_ADDR,
2096 fnet_interfaces::AddressAssignmentState::Unavailable,
2097 ),
2098 ]),
2099 has_default_ipv4_route: Some(false),
2100 has_default_ipv6_route: Some(false),
2101 ..Default::default()
2102 }),
2103 fnet_interfaces::Event::Existing(fnet_interfaces::Properties {
2104 id: Some(PPP_INTERFACE_ID),
2105 name: Some(PPP_NAME.to_string()),
2106 port_class: Some(PPP.into()),
2107 online: Some(false),
2108 addresses: Some(vec![
2109 test_addr_with_assignment_state(
2110 TEST_V4_ADDR,
2111 fnet_interfaces::AddressAssignmentState::Assigned,
2112 ),
2113 test_addr_with_assignment_state(
2114 TEST_V6_ADDR,
2115 fnet_interfaces::AddressAssignmentState::Assigned,
2116 ),
2117 ]),
2118 has_default_ipv4_route: Some(false),
2119 has_default_ipv6_route: Some(false),
2120 ..Default::default()
2121 }),
2122 fnet_interfaces::Event::Idle(fnet_interfaces::Empty),
2123 ],
2124 );
2125 futures::select! {
2126 () = watcher_stream_fut.fuse() => {},
2127 () = root_interfaces_fut => {
2128 unreachable!("root interfaces request stream should never end")
2129 }
2130 err = event_loop_fut => unreachable!("eventloop should not return: {err:?}"),
2131 }
2132 assert_eq!(&link_sink.take_messages()[..], &[]);
2133 assert_eq!(&addr4_sink.take_messages()[..], &[]);
2134 assert_eq!(&addr6_sink.take_messages()[..], &[]);
2135 assert_eq!(&other_sink.take_messages()[..], &[]);
2136 assert_eq!(&all_sink.take_messages()[..], &[]);
2137
2138 let watcher_stream_fut = respond_to_watcher(
2139 watcher_stream.by_ref(),
2140 [
2141 fnet_interfaces::Event::Added(fnet_interfaces::Properties {
2142 id: Some(WLAN_INTERFACE_ID),
2143 name: Some(WLAN_NAME.to_string()),
2144 port_class: Some(WLAN_CLIENT.into()),
2145 online: Some(false),
2146 addresses: Some(vec![
2147 test_addr_with_assignment_state(
2148 TEST_V4_ADDR,
2149 fnet_interfaces::AddressAssignmentState::Tentative,
2150 ),
2151 test_addr_with_assignment_state(
2152 TEST_V6_ADDR,
2153 fnet_interfaces::AddressAssignmentState::Tentative,
2154 ),
2155 ]),
2156 has_default_ipv4_route: Some(false),
2157 has_default_ipv6_route: Some(false),
2158 ..Default::default()
2159 }),
2160 fnet_interfaces::Event::Changed(fnet_interfaces::Properties {
2161 id: Some(LO_INTERFACE_ID),
2162 online: Some(true),
2163 addresses: Some(vec![
2164 test_addr_with_assignment_state(
2165 TEST_V4_ADDR,
2166 fnet_interfaces::AddressAssignmentState::Assigned,
2167 ),
2168 test_addr_with_assignment_state(
2169 TEST_V6_ADDR,
2170 fnet_interfaces::AddressAssignmentState::Assigned,
2171 ),
2172 ]),
2173 ..Default::default()
2174 }),
2175 fnet_interfaces::Event::Removed(ETH_INTERFACE_ID),
2176 fnet_interfaces::Event::Changed(fnet_interfaces::Properties {
2177 id: Some(PPP_INTERFACE_ID),
2178 addresses: Some(Vec::new()),
2179 ..Default::default()
2180 }),
2181 fnet_interfaces::Event::Changed(fnet_interfaces::Properties {
2182 id: Some(WLAN_INTERFACE_ID),
2183 has_default_ipv6_route: Some(true),
2184 ..Default::default()
2185 }),
2186 ],
2187 );
2188
2189 futures::select! {
2190 () = watcher_stream_fut.fuse() => {},
2191 () = root_interfaces_fut => {
2192 unreachable!("root interfaces request stream should never end")
2193 }
2194 err = event_loop_fut => unreachable!("eventloop should not return: {err:?}"),
2195 }
2196
2197 assert_matches!(event_loop_fut.now_or_never(), None);
2201
2202 assert_eq!(
2203 interfaces_handler_sink.take_handled(),
2204 [
2205 HandledLink { name: LO_NAME.to_string(), kind: HandledLinkKind::New },
2206 HandledLink { name: ETH_NAME.to_string(), kind: HandledLinkKind::New },
2207 HandledLink { name: PPP_NAME.to_string(), kind: HandledLinkKind::New },
2208 HandledLink { name: WLAN_NAME.to_string(), kind: HandledLinkKind::New },
2209 HandledLink { name: ETH_NAME.to_string(), kind: HandledLinkKind::Del },
2210 ],
2211 );
2212 let arphrd_ether_u16: u16 = ARPHRD_ETHER as u16;
2214 let arphrd_loopback_u16: u16 = ARPHRD_LOOPBACK as u16;
2216 let wlan_link = SentMessage::multicast(
2217 create_netlink_link_message(
2218 WLAN_INTERFACE_ID,
2219 arphrd_ether_u16,
2220 net_device_flags_IFF_UP, create_nlas(WLAN_NAME.to_string(), arphrd_ether_u16, false, &WLAN_MAC),
2222 )
2223 .into_rtnl_new_link(UNSPECIFIED_SEQUENCE_NUMBER, false),
2224 ModernGroup(rtnetlink_groups_RTNLGRP_LINK),
2225 );
2226 let lo_link = SentMessage::multicast(
2227 create_netlink_link_message(
2228 LO_INTERFACE_ID,
2229 arphrd_loopback_u16,
2230 ONLINE_IF_FLAGS | net_device_flags_IFF_LOOPBACK,
2231 create_nlas(LO_NAME.to_string(), arphrd_loopback_u16, true, &LO_MAC),
2232 )
2233 .into_rtnl_new_link(UNSPECIFIED_SEQUENCE_NUMBER, false),
2234 ModernGroup(rtnetlink_groups_RTNLGRP_LINK),
2235 );
2236 let eth_link = SentMessage::multicast(
2237 create_netlink_link_message(
2238 ETH_INTERFACE_ID,
2239 arphrd_ether_u16,
2240 0,
2241 create_nlas(ETH_NAME.to_string(), arphrd_ether_u16, false, Ð_MAC),
2242 )
2243 .into_rtnl_del_link(UNSPECIFIED_SEQUENCE_NUMBER),
2244 ModernGroup(rtnetlink_groups_RTNLGRP_LINK),
2245 );
2246 assert_eq!(
2247 &link_sink.take_messages()[..],
2248 &[wlan_link.clone(), lo_link.clone(), eth_link.clone(),],
2249 );
2250
2251 let wlan_v4_addr = SentMessage::multicast(
2252 create_address_message(
2253 WLAN_INTERFACE_ID.try_into().unwrap(),
2254 TEST_V4_ADDR,
2255 WLAN_NAME.to_string(),
2256 IFA_F_PERMANENT | IFA_F_TENTATIVE,
2257 )
2258 .to_rtnl_new_addr(UNSPECIFIED_SEQUENCE_NUMBER, false),
2259 ModernGroup(rtnetlink_groups_RTNLGRP_IPV4_IFADDR),
2260 );
2261 let eth_v4_addr = SentMessage::multicast(
2262 create_address_message(
2263 ETH_INTERFACE_ID.try_into().unwrap(),
2264 TEST_V4_ADDR,
2265 ETH_NAME.to_string(),
2266 IFA_F_PERMANENT | IFA_F_TENTATIVE,
2267 )
2268 .to_rtnl_del_addr(UNSPECIFIED_SEQUENCE_NUMBER),
2269 ModernGroup(rtnetlink_groups_RTNLGRP_IPV4_IFADDR),
2270 );
2271 let ppp_v4_addr = SentMessage::multicast(
2272 create_address_message(
2273 PPP_INTERFACE_ID.try_into().unwrap(),
2274 TEST_V4_ADDR,
2275 PPP_NAME.to_string(),
2276 IFA_F_PERMANENT,
2277 )
2278 .to_rtnl_del_addr(UNSPECIFIED_SEQUENCE_NUMBER),
2279 ModernGroup(rtnetlink_groups_RTNLGRP_IPV4_IFADDR),
2280 );
2281 assert_eq!(
2282 &addr4_sink.take_messages()[..],
2283 &[wlan_v4_addr.clone(), eth_v4_addr.clone(), ppp_v4_addr.clone(),],
2284 );
2285
2286 let wlan_v6_addr = SentMessage::multicast(
2287 create_address_message(
2288 WLAN_INTERFACE_ID.try_into().unwrap(),
2289 TEST_V6_ADDR,
2290 WLAN_NAME.to_string(),
2291 IFA_F_PERMANENT | IFA_F_TENTATIVE,
2292 )
2293 .to_rtnl_new_addr(UNSPECIFIED_SEQUENCE_NUMBER, false),
2294 ModernGroup(rtnetlink_groups_RTNLGRP_IPV6_IFADDR),
2295 );
2296 let lo_v6_addr = SentMessage::multicast(
2297 create_address_message(
2298 LO_INTERFACE_ID.try_into().unwrap(),
2299 TEST_V6_ADDR,
2300 LO_NAME.to_string(),
2301 IFA_F_PERMANENT,
2302 )
2303 .to_rtnl_new_addr(UNSPECIFIED_SEQUENCE_NUMBER, false),
2304 ModernGroup(rtnetlink_groups_RTNLGRP_IPV6_IFADDR),
2305 );
2306 let eth_v6_addr = SentMessage::multicast(
2307 create_address_message(
2308 ETH_INTERFACE_ID.try_into().unwrap(),
2309 TEST_V6_ADDR,
2310 ETH_NAME.to_string(),
2311 IFA_F_PERMANENT | IFA_F_TENTATIVE,
2312 )
2313 .to_rtnl_del_addr(UNSPECIFIED_SEQUENCE_NUMBER),
2314 ModernGroup(rtnetlink_groups_RTNLGRP_IPV6_IFADDR),
2315 );
2316 let ppp_v6_addr = SentMessage::multicast(
2317 create_address_message(
2318 PPP_INTERFACE_ID.try_into().unwrap(),
2319 TEST_V6_ADDR,
2320 PPP_NAME.to_string(),
2321 IFA_F_PERMANENT,
2322 )
2323 .to_rtnl_del_addr(UNSPECIFIED_SEQUENCE_NUMBER),
2324 ModernGroup(rtnetlink_groups_RTNLGRP_IPV6_IFADDR),
2325 );
2326 assert_eq!(
2327 &addr6_sink.take_messages()[..],
2328 &[wlan_v6_addr.clone(), lo_v6_addr.clone(), eth_v6_addr.clone(), ppp_v6_addr.clone(),],
2329 );
2330
2331 assert_eq!(
2332 &all_sink.take_messages()[..],
2333 &[
2334 wlan_link,
2336 wlan_v4_addr,
2337 wlan_v6_addr,
2338 lo_link,
2339 lo_v6_addr,
2340 eth_v4_addr,
2342 eth_v6_addr,
2343 eth_link,
2344 ppp_v4_addr,
2345 ppp_v6_addr,
2346 ],
2347 );
2348 assert_eq!(&other_sink.take_messages()[..], &[]);
2349 }
2350
2351 const LO_MAC: Option<fnet::MacAddress> = None;
2352 const ETH_MAC: Option<fnet::MacAddress> = Some(fnet::MacAddress { octets: [1, 1, 1, 1, 1, 1] });
2353 const PPP_MAC: Option<fnet::MacAddress> = Some(fnet::MacAddress { octets: [2, 2, 2, 2, 2, 2] });
2354 const WLAN_MAC: Option<fnet::MacAddress> =
2355 Some(fnet::MacAddress { octets: [3, 3, 3, 3, 3, 3] });
2356
2357 fn handle_get_mac_root_request_or_panic(req: fnet_root::InterfacesRequest) {
2358 match req {
2359 fnet_root::InterfacesRequest::GetMac { id, responder } => {
2360 let link_address = match id {
2361 LO_INTERFACE_ID => LO_MAC,
2362 ETH_INTERFACE_ID => ETH_MAC,
2363 PPP_INTERFACE_ID => PPP_MAC,
2364 WLAN_INTERFACE_ID => WLAN_MAC,
2365 id => panic!("unexpected interface ID {id}"),
2366 };
2367
2368 responder.send(Ok(link_address.as_ref())).unwrap()
2369 }
2370 req => panic!("unexpected request {:?}", req),
2371 }
2372 }
2373
2374 fn expect_only_get_mac_root_requests(
2375 interfaces_request_stream: fnet_root::InterfacesRequestStream,
2376 ) -> impl Stream<Item = fnet_interfaces::Event> {
2377 futures::stream::unfold(interfaces_request_stream, |interfaces_request_stream| async move {
2378 interfaces_request_stream
2379 .for_each(|req| async move { handle_get_mac_root_request_or_panic(req.unwrap()) })
2380 .await;
2381
2382 None
2383 })
2384 }
2385
2386 async fn handle_only_get_mac_root_requests_fut(
2387 interfaces_request_stream: fnet_root::InterfacesRequestStream,
2388 ) {
2389 expect_only_get_mac_root_requests(interfaces_request_stream)
2390 .for_each(|item| async move { panic!("unexpected item = {item:?}") })
2391 .await
2392 }
2393
2394 #[derive(Debug, PartialEq)]
2395 struct TestRequestResult {
2396 messages: Vec<SentMessage<RouteNetlinkMessage>>,
2397 waiter_results: Vec<Result<(), RequestError>>,
2398 }
2399
2400 async fn test_request<
2406 St: Stream<Item = fnet_interfaces::Event>,
2407 F: FnOnce(fnet_root::InterfacesRequestStream) -> St,
2408 >(
2409 args: impl IntoIterator<Item = RequestArgs>,
2410 root_handler: F,
2411 ) -> TestRequestResult {
2412 test_request_with_initial_state(
2413 args,
2414 root_handler,
2415 InitialState { eth_interface_online: false },
2416 )
2417 .await
2418 }
2419
2420 #[derive(Clone, Copy, Debug)]
2421 struct InitialState {
2422 eth_interface_online: bool,
2423 }
2424
2425 async fn test_request_with_initial_state<
2433 St: Stream<Item = fnet_interfaces::Event>,
2434 F: FnOnce(fnet_root::InterfacesRequestStream) -> St,
2435 >(
2436 args: impl IntoIterator<Item = RequestArgs>,
2437 root_handler: F,
2438 initial_state: InitialState,
2439 ) -> TestRequestResult {
2440 let scope = fasync::Scope::new();
2441 let result = {
2442 let InitialState { eth_interface_online } = initial_state;
2443
2444 let (mut expected_sink, expected_client, async_work_drain_task) =
2445 crate::client::testutil::new_fake_client::<NetlinkRoute>(
2446 crate::client::testutil::CLIENT_ID_1,
2447 std::iter::empty(),
2448 );
2449 let _join_handle = scope.spawn(async_work_drain_task);
2450 let (mut other_sink, other_client, async_work_drain_task) =
2451 crate::client::testutil::new_fake_client::<NetlinkRoute>(
2452 crate::client::testutil::CLIENT_ID_2,
2453 std::iter::empty(),
2454 );
2455 let _join_handle = scope.spawn(async_work_drain_task);
2456 let Setup {
2457 event_loop_fut,
2458 mut watcher_stream,
2459 request_sink,
2460 interfaces_request_stream,
2461 interfaces_handler_sink: _,
2462 _async_work_sink: _,
2463 } = setup_with_route_clients({
2464 let route_clients = ClientTable::default();
2465 route_clients.add_client(expected_client.clone());
2466 route_clients.add_client(other_client);
2467 route_clients
2468 });
2469 let event_loop_fut = event_loop_fut.fuse();
2470 let mut event_loop_fut = pin!(event_loop_fut);
2471
2472 let watcher_stream_fut = respond_to_watcher(
2473 watcher_stream.by_ref(),
2474 [
2475 fnet_interfaces::Event::Existing(fnet_interfaces::Properties {
2476 id: Some(LO_INTERFACE_ID),
2477 name: Some(LO_NAME.to_string()),
2478 port_class: Some(LOOPBACK.into()),
2479 online: Some(true),
2480 addresses: Some(vec![test_addr(TEST_V6_ADDR), test_addr(TEST_V4_ADDR)]),
2481 has_default_ipv4_route: Some(false),
2482 has_default_ipv6_route: Some(false),
2483 ..Default::default()
2484 }),
2485 fnet_interfaces::Event::Existing(fnet_interfaces::Properties {
2486 id: Some(ETH_INTERFACE_ID),
2487 name: Some(ETH_NAME.to_string()),
2488 port_class: Some(ETHERNET.into()),
2489 online: Some(eth_interface_online),
2490 addresses: Some(vec![test_addr(TEST_V4_ADDR), test_addr(TEST_V6_ADDR)]),
2491 has_default_ipv4_route: Some(false),
2492 has_default_ipv6_route: Some(false),
2493 ..Default::default()
2494 }),
2495 fnet_interfaces::Event::Idle(fnet_interfaces::Empty),
2496 ],
2497 );
2498 futures::select_biased! {
2499 err = event_loop_fut => unreachable!("eventloop should not return: {err:?}"),
2500 () = watcher_stream_fut.fuse() => {},
2501 }
2502 assert_eq!(&expected_sink.take_messages()[..], &[]);
2503 assert_eq!(&other_sink.take_messages()[..], &[]);
2504
2505 let expected_client = &expected_client;
2506 let fut = futures::stream::iter(args).fold(
2507 (Vec::new(), request_sink),
2508 |(mut results, mut request_sink), args| async move {
2509 let (completer, waiter) = oneshot::channel();
2510 request_sink
2511 .send(crate::route_eventloop::UnifiedRequest::InterfacesRequest(Request {
2512 args,
2513 sequence_number: TEST_SEQUENCE_NUMBER,
2514 client: expected_client.clone(),
2515 completer,
2516 }))
2517 .await
2518 .unwrap();
2519 results.push(waiter.await.unwrap());
2520 (results, request_sink)
2521 },
2522 );
2523 let watcher_fut = root_handler(interfaces_request_stream).map(Ok).forward(
2526 futures::sink::unfold(watcher_stream.by_ref(), |st, event| async {
2527 respond_to_watcher(st.by_ref(), [event]).await;
2528 Ok::<_, std::convert::Infallible>(st)
2529 }),
2530 );
2531 let waiter_results = futures::select_biased! {
2532 res = futures::future::join(watcher_fut, event_loop_fut) => {
2533 unreachable!("eventloop/watcher should not return: {res:?}")
2534 },
2535 (results, _request_sink) = fut.fuse() => results
2536 };
2537 assert_eq!(&other_sink.take_messages()[..], &[]);
2538
2539 TestRequestResult { messages: expected_sink.take_messages(), waiter_results }
2540 };
2541 scope.join().await;
2542 result
2543 }
2544
2545 #[test_case(
2546 GetLinkArgs::Dump,
2547 &[LO_INTERFACE_ID, ETH_INTERFACE_ID],
2548 Ok(()); "dump")]
2549 #[test_case(
2550 GetLinkArgs::Get(LinkSpecifier::Index(
2551 NonZeroU32::new(LO_INTERFACE_ID.try_into().unwrap()).unwrap())),
2552 &[LO_INTERFACE_ID],
2553 Ok(()); "id")]
2554 #[test_case(
2555 GetLinkArgs::Get(LinkSpecifier::Index(
2556 NonZeroU32::new(WLAN_INTERFACE_ID.try_into().unwrap()).unwrap())),
2557 &[],
2558 Err(RequestError::UnrecognizedInterface); "id_not_found")]
2559 #[test_case(
2560 GetLinkArgs::Get(LinkSpecifier::Name(LO_NAME.to_string())),
2561 &[LO_INTERFACE_ID],
2562 Ok(()); "name")]
2563 #[test_case(
2564 GetLinkArgs::Get(LinkSpecifier::Name(WLAN_NAME.to_string())),
2565 &[],
2566 Err(RequestError::UnrecognizedInterface); "name_not_found")]
2567 #[fuchsia::test]
2568 async fn test_get_link(
2569 args: GetLinkArgs,
2570 expected_new_links: &[u64],
2571 expected_result: Result<(), RequestError>,
2572 ) {
2573 let is_dump = match args {
2574 GetLinkArgs::Dump => true,
2575 GetLinkArgs::Get(_) => false,
2576 };
2577 let arphrd_ether_u16: u16 = ARPHRD_ETHER as u16;
2579 let arphrd_loopback_u16: u16 = ARPHRD_LOOPBACK as u16;
2581 let expected_messages = expected_new_links
2582 .iter()
2583 .map(|link_id| {
2584 let msg = match *link_id {
2585 LO_INTERFACE_ID => create_netlink_link_message(
2586 LO_INTERFACE_ID,
2587 arphrd_loopback_u16,
2588 ONLINE_IF_FLAGS | net_device_flags_IFF_LOOPBACK,
2589 create_nlas(LO_NAME.to_string(), arphrd_loopback_u16, true, &LO_MAC),
2590 ),
2591 ETH_INTERFACE_ID => create_netlink_link_message(
2592 ETH_INTERFACE_ID,
2593 arphrd_ether_u16,
2594 0,
2595 create_nlas(ETH_NAME.to_string(), arphrd_ether_u16, false, Ð_MAC),
2596 ),
2597 _ => unreachable!("GetLink should only be tested with loopback and ethernet"),
2598 };
2599 SentMessage::unicast(msg.into_rtnl_new_link(TEST_SEQUENCE_NUMBER, is_dump))
2600 })
2601 .collect();
2602
2603 assert_eq!(
2604 test_request(
2605 [RequestArgs::Link(LinkRequestArgs::Get(args))],
2606 expect_only_get_mac_root_requests,
2607 )
2608 .await,
2609 TestRequestResult {
2610 messages: expected_messages,
2611 waiter_results: vec![expected_result],
2612 },
2613 )
2614 }
2615
2616 fn handle_get_admin_for_eth_or_panic(
2617 req: Result<fnet_root::InterfacesRequest, fidl::Error>,
2618 ) -> impl Future<Output = Option<fnet_interfaces_admin::ControlRequestStream>> {
2619 futures::future::ready(match req.unwrap() {
2620 fnet_root::InterfacesRequest::GetAdmin { id, control, control_handle: _ } => {
2621 pretty_assertions::assert_eq!(id, ETH_INTERFACE_ID);
2622 Some(control.into_stream())
2623 }
2624 req => {
2625 handle_get_mac_root_request_or_panic(req);
2626 None
2627 }
2628 })
2629 }
2630
2631 fn expect_get_admin_with_handler<
2641 I: IntoIterator<Item = fnet_interfaces::Event> + 'static,
2642 H: FnMut(fnet_interfaces_admin::ControlRequest) -> I + 'static,
2643 >(
2644 admin_handler: H,
2645 ) -> impl FnOnce(
2646 fnet_root::InterfacesRequestStream,
2647 ) -> Pin<Box<dyn Stream<Item = fnet_interfaces::Event>>> {
2648 move |interfaces_request_stream: fnet_root::InterfacesRequestStream| {
2649 Box::pin(
2650 interfaces_request_stream
2651 .filter_map(|req| handle_get_admin_for_eth_or_panic(req))
2652 .into_future()
2653 .map(|(admin_control_stream, _stream_of_admin_control_streams)| {
2657 admin_control_stream.unwrap()
2658 })
2659 .flatten_stream()
2660 .scan(admin_handler, |admin_handler, req| {
2665 futures::future::ready(Some(futures::stream::iter(admin_handler(
2666 req.unwrap(),
2667 ))))
2668 })
2669 .flatten(),
2670 )
2671 }
2672 }
2673
2674 #[test_case(
2675 InitialState { eth_interface_online: false },
2676 SetLinkArgs{
2677 link: LinkSpecifier::Name(ETH_NAME.to_string()),
2678 enable: None,
2679 },
2680 Ok(true),
2681 Ok(()); "no_change")]
2682 #[test_case(
2683 InitialState { eth_interface_online: false },
2684 SetLinkArgs{
2685 link: LinkSpecifier::Name(WLAN_NAME.to_string()),
2686 enable: None,
2687 },
2688 Ok(true),
2689 Err(RequestError::UnrecognizedInterface); "no_change_name_not_found")]
2690 #[test_case(
2691 InitialState { eth_interface_online: false },
2692 SetLinkArgs {
2693 link: LinkSpecifier::Index(
2694 NonZeroU32::new(WLAN_INTERFACE_ID.try_into().unwrap()).unwrap()),
2695 enable: None,
2696 },
2697 Ok(true),
2698 Err(RequestError::UnrecognizedInterface); "no_change_id_not_found")]
2699 #[test_case(
2700 InitialState { eth_interface_online: false },
2701 SetLinkArgs{
2702 link: LinkSpecifier::Name(ETH_NAME.to_string()),
2703 enable: Some(true),
2704 },
2705 Ok(false),
2706 Ok(()); "enable_no_op_succeeds")]
2707 #[test_case(
2708 InitialState { eth_interface_online: false },
2709 SetLinkArgs{
2710 link: LinkSpecifier::Name(ETH_NAME.to_string()),
2711 enable: Some(true),
2712 },
2713 Ok(true),
2714 Ok(()); "enable_newly_succeeds")]
2715 #[test_case(
2716 InitialState { eth_interface_online: false },
2717 SetLinkArgs{
2718 link: LinkSpecifier::Name(WLAN_NAME.to_string()),
2719 enable: Some(true),
2720 },
2721 Ok(true),
2722 Err(RequestError::UnrecognizedInterface); "enable_not_found")]
2723 #[test_case(
2724 InitialState { eth_interface_online: false },
2725 SetLinkArgs{
2726 link: LinkSpecifier::Name(ETH_NAME.to_string()),
2727 enable: Some(true),
2728 },
2729 Err(()),
2730 Err(RequestError::Unknown); "enable_fails")]
2731 #[test_case(
2732 InitialState { eth_interface_online: false },
2733 SetLinkArgs{
2734 link: LinkSpecifier::Name(ETH_NAME.to_string()),
2735 enable: Some(false),
2736 },
2737 Ok(false),
2738 Ok(()); "disable_no_op_succeeds")]
2739 #[test_case(
2740 InitialState { eth_interface_online: true },
2741 SetLinkArgs{
2742 link: LinkSpecifier::Name(ETH_NAME.to_string()),
2743 enable: Some(false),
2744 },
2745 Ok(true),
2746 Ok(()); "disable_newly_succeeds")]
2747 #[test_case(
2748 InitialState { eth_interface_online: false },
2749 SetLinkArgs{
2750 link: LinkSpecifier::Name(WLAN_NAME.to_string()),
2751 enable: Some(false),
2752 },
2753 Ok(true),
2754 Err(RequestError::UnrecognizedInterface); "disable_not_found")]
2755 #[test_case(
2756 InitialState { eth_interface_online: false },
2757 SetLinkArgs{
2758 link: LinkSpecifier::Name(ETH_NAME.to_string()),
2759 enable: Some(false),
2760 },
2761 Err(()),
2762 Err(RequestError::Unknown); "disable_fails")]
2763 #[fuchsia::test]
2764 async fn test_set_link(
2765 initial_state: InitialState,
2766 args: SetLinkArgs,
2767 control_response: Result<bool, ()>,
2768 expected_result: Result<(), RequestError>,
2769 ) {
2770 let SetLinkArgs { link: _, enable } = args.clone();
2771 let request = RequestArgs::Link(LinkRequestArgs::Set(args));
2772
2773 let control_response_clone = control_response.clone();
2774 let handle_enable =
2775 move |req: fnet_interfaces_admin::ControlRequest| -> Option<fnet_interfaces::Event> {
2776 let responder = match req {
2777 fnet_interfaces_admin::ControlRequest::Enable { responder } => responder,
2778 _ => panic!("unexpected ControlRequest received"),
2779 };
2780 match control_response {
2781 Err(()) => {
2782 responder
2783 .send(Err(fnet_interfaces_admin::ControlEnableError::unknown()))
2784 .expect("should send response");
2785 None
2786 }
2787 Ok(newly_enabled) => {
2788 responder.send(Ok(newly_enabled)).expect("should send response");
2789 newly_enabled.then_some(fnet_interfaces::Event::Changed(
2790 fnet_interfaces::Properties {
2791 id: Some(ETH_INTERFACE_ID),
2792 online: Some(true),
2793 ..fnet_interfaces::Properties::default()
2794 },
2795 ))
2796 }
2797 }
2798 };
2799 let handle_disable =
2800 move |req: fnet_interfaces_admin::ControlRequest| -> Option<fnet_interfaces::Event> {
2801 let responder = match req {
2802 fnet_interfaces_admin::ControlRequest::Disable { responder } => responder,
2803 _ => panic!("unexpected ControlRequest received"),
2804 };
2805 match control_response_clone {
2806 Err(()) => {
2807 responder
2808 .send(Err(fnet_interfaces_admin::ControlDisableError::unknown()))
2809 .expect("should send response");
2810 None
2811 }
2812 Ok(newly_disabled) => {
2813 responder.send(Ok(newly_disabled)).expect("should send response");
2814 newly_disabled.then_some(fnet_interfaces::Event::Changed(
2815 fnet_interfaces::Properties {
2816 id: Some(ETH_INTERFACE_ID),
2817 online: Some(false),
2818 ..fnet_interfaces::Properties::default()
2819 },
2820 ))
2821 }
2822 }
2823 };
2824
2825 let test_result = match enable {
2826 None => {
2827 test_request_with_initial_state(
2828 [request],
2829 expect_only_get_mac_root_requests,
2830 initial_state,
2831 )
2832 .await
2833 }
2834 Some(true) => {
2835 test_request_with_initial_state(
2836 [request],
2837 expect_get_admin_with_handler(handle_enable),
2838 initial_state,
2839 )
2840 .await
2841 }
2842 Some(false) => {
2843 test_request_with_initial_state(
2844 [request],
2845 expect_get_admin_with_handler(handle_disable),
2846 initial_state,
2847 )
2848 .await
2849 }
2850 };
2851
2852 assert_eq!(
2853 test_result,
2854 TestRequestResult {
2855 messages: vec![],
2858 waiter_results: vec![expected_result],
2859 },
2860 )
2861 }
2862
2863 #[test_case(Some(IpVersion::V4); "v4")]
2864 #[test_case(Some(IpVersion::V6); "v6")]
2865 #[test_case(None; "all")]
2866 #[fuchsia::test]
2867 async fn test_get_addr(ip_version_filter: Option<IpVersion>) {
2868 pretty_assertions::assert_eq!(
2869 test_request(
2870 [RequestArgs::Address(AddressRequestArgs::Get(GetAddressArgs::Dump {
2871 ip_version_filter
2872 }))],
2873 expect_only_get_mac_root_requests,
2874 )
2875 .await,
2876 TestRequestResult {
2877 messages: [(LO_INTERFACE_ID, LO_NAME), (ETH_INTERFACE_ID, ETH_NAME)]
2878 .into_iter()
2879 .map(|(id, name)| {
2880 [TEST_V4_ADDR, TEST_V6_ADDR]
2881 .into_iter()
2882 .filter(|fnet::Subnet { addr, prefix_len: _ }| {
2883 ip_version_filter.map_or(true, |ip_version| {
2884 ip_version.eq(&match addr {
2885 fnet::IpAddress::Ipv4(_) => IpVersion::V4,
2886 fnet::IpAddress::Ipv6(_) => IpVersion::V6,
2887 })
2888 })
2889 })
2890 .map(move |addr| {
2891 SentMessage::unicast(
2892 create_address_message(
2893 id.try_into().unwrap(),
2894 addr,
2895 name.to_string(),
2896 IFA_F_PERMANENT,
2897 )
2898 .to_rtnl_new_addr(TEST_SEQUENCE_NUMBER, true),
2899 )
2900 })
2901 })
2902 .flatten()
2903 .collect(),
2904 waiter_results: vec![Ok(())],
2905 },
2906 );
2907 }
2908
2909 #[test_case(
2912 test_addr_subnet_v4(),
2913 None,
2914 true; "v4_no_terminal_new")]
2915 #[test_case(
2916 test_addr_subnet_v6(),
2917 None,
2918 true; "v6_no_terminal_new")]
2919 #[test_case(
2920 test_addr_subnet_v4(),
2921 Some(InterfaceRemovedReason::PortClosed),
2922 true; "v4_port_closed_terminal_new")]
2923 #[test_case(
2924 test_addr_subnet_v6(),
2925 Some(InterfaceRemovedReason::PortClosed),
2926 true; "v6_port_closed_terminal_new")]
2927 #[test_case(
2928 test_addr_subnet_v4(),
2929 Some(InterfaceRemovedReason::User),
2930 true; "v4_user_terminal_new")]
2931 #[test_case(
2932 test_addr_subnet_v6(),
2933 Some(InterfaceRemovedReason::User),
2934 true; "v6_user_terminal_new")]
2935 #[test_case(
2936 test_addr_subnet_v4(),
2937 None,
2938 false; "v4_no_terminal_del")]
2939 #[test_case(
2940 test_addr_subnet_v6(),
2941 None,
2942 false; "v6_no_terminal_del")]
2943 #[test_case(
2944 test_addr_subnet_v4(),
2945 Some(InterfaceRemovedReason::PortClosed),
2946 false; "v4_port_closed_terminal_del")]
2947 #[test_case(
2948 test_addr_subnet_v6(),
2949 Some(InterfaceRemovedReason::PortClosed),
2950 false; "v6_port_closed_terminal_del")]
2951 #[test_case(
2952 test_addr_subnet_v4(),
2953 Some(InterfaceRemovedReason::User),
2954 false; "v4_user_terminal_del")]
2955 #[test_case(
2956 test_addr_subnet_v6(),
2957 Some(InterfaceRemovedReason::User),
2958 false; "v6_user_terminal_del")]
2959 #[fuchsia::test]
2960 async fn test_new_del_addr_interface_removed(
2961 address: AddrSubnetEither,
2962 removal_reason: Option<InterfaceRemovedReason>,
2963 is_new: bool,
2964 ) {
2965 let interface_id = NonZeroU32::new(LO_INTERFACE_ID.try_into().unwrap()).unwrap();
2966 let address_and_interface_id = AddressAndInterfaceArgs { address, interface_id };
2967 pretty_assertions::assert_eq!(
2968 test_request(
2969 [if is_new {
2970 RequestArgs::Address(AddressRequestArgs::New(NewAddressArgs {
2971 address_and_interface_id,
2972 add_subnet_route: false,
2973 }))
2974 } else {
2975 RequestArgs::Address(AddressRequestArgs::Del(DelAddressArgs {
2976 address_and_interface_id,
2977 }))
2978 }],
2979 |interfaces_request_stream| futures::stream::unfold(
2980 interfaces_request_stream,
2981 |interfaces_request_stream| async move {
2982 interfaces_request_stream
2983 .for_each(|req| {
2984 futures::future::ready(match req.unwrap() {
2985 fnet_root::InterfacesRequest::GetAdmin {
2986 id,
2987 control,
2988 control_handle: _,
2989 } => {
2990 pretty_assertions::assert_eq!(id, LO_INTERFACE_ID);
2991 let control = control.into_stream();
2992 let control = control.control_handle();
2993 if let Some(reason) = removal_reason {
2994 control.send_on_interface_removed(reason).unwrap()
2995 }
2996 control.shutdown();
2997 }
2998 req => handle_get_mac_root_request_or_panic(req),
2999 })
3000 })
3001 .await;
3002
3003 unreachable!("interfaces request stream should not end")
3004 },
3005 ),
3006 )
3007 .await,
3008 TestRequestResult {
3009 messages: Vec::new(),
3010 waiter_results: vec![Err(RequestError::UnrecognizedInterface)],
3011 },
3012 )
3013 }
3014
3015 enum AddressRequestKind {
3016 New { add_subnet_route: bool },
3017 Del,
3018 }
3019
3020 #[test_case(
3023 add_test_addr_subnet_v4(),
3024 AddressRequestKind::New { add_subnet_route: false }; "v4_new")]
3025 #[test_case(
3026 add_test_addr_subnet_v6(),
3027 AddressRequestKind::New { add_subnet_route: false }; "v6_new")]
3028 #[test_case(add_test_addr_subnet_v4(), AddressRequestKind::Del; "v4_del")]
3029 #[test_case(add_test_addr_subnet_v6(), AddressRequestKind::Del; "v6_del")]
3030 #[fuchsia::test]
3031 async fn test_unknown_interface_request(address: AddrSubnetEither, kind: AddressRequestKind) {
3032 let interface_id = NonZeroU32::new(WLAN_INTERFACE_ID.try_into().unwrap()).unwrap();
3033 let address_and_interface_id = AddressAndInterfaceArgs { address, interface_id };
3034 pretty_assertions::assert_eq!(
3035 test_request(
3036 [match kind {
3037 AddressRequestKind::New { add_subnet_route } => {
3038 RequestArgs::Address(AddressRequestArgs::New(NewAddressArgs {
3039 address_and_interface_id,
3040 add_subnet_route,
3041 }))
3042 }
3043 AddressRequestKind::Del => {
3044 RequestArgs::Address(AddressRequestArgs::Del(DelAddressArgs {
3045 address_and_interface_id,
3046 }))
3047 }
3048 }],
3049 expect_only_get_mac_root_requests,
3050 )
3051 .await,
3052 TestRequestResult {
3053 messages: Vec::new(),
3054 waiter_results: vec![Err(RequestError::UnrecognizedInterface)],
3055 },
3056 )
3057 }
3058
3059 struct TestInterfaceRequestCase<F> {
3060 address: AddrSubnetEither,
3061 kind: AddressRequestKind,
3062 control_request_handler: F,
3063 }
3064
3065 impl<F> TestInterfaceRequestCase<F> {
3066 fn into_request_args_and_handler(self, interface_id: NonZeroU32) -> (RequestArgs, F) {
3067 let Self { address, kind, control_request_handler } = self;
3068 let address_and_interface_id = AddressAndInterfaceArgs { address, interface_id };
3069 let args = match kind {
3070 AddressRequestKind::New { add_subnet_route } => {
3071 RequestArgs::Address(AddressRequestArgs::New(NewAddressArgs {
3072 address_and_interface_id,
3073 add_subnet_route,
3074 }))
3075 }
3076 AddressRequestKind::Del => {
3077 RequestArgs::Address(AddressRequestArgs::Del(DelAddressArgs {
3078 address_and_interface_id,
3079 }))
3080 }
3081 };
3082
3083 (args, control_request_handler)
3084 }
3085 }
3086
3087 async fn test_maybe_two_interface_requests_on_single_control<
3093 St1: Stream<Item = fnet_interfaces::Event>,
3094 F1: FnMut(fnet_interfaces_admin::ControlRequest) -> St1,
3095 St2: Stream<Item = fnet_interfaces::Event>,
3096 F2: FnMut(fnet_interfaces_admin::ControlRequest) -> St2,
3097 >(
3098 case1: TestInterfaceRequestCase<F1>,
3099 case2: Option<TestInterfaceRequestCase<F2>>,
3100 ) -> TestRequestResult {
3101 let interface_id = NonZeroU32::new(ETH_INTERFACE_ID.try_into().unwrap()).unwrap();
3102 let (args1, mut control_request_handler1) =
3103 case1.into_request_args_and_handler(interface_id);
3104
3105 let (args2, control_request_handler2) = if let Some(case) = case2 {
3106 let (args, control_request_handler) = case.into_request_args_and_handler(interface_id);
3107 (Some(args), Some(control_request_handler))
3108 } else {
3109 (None, None)
3110 };
3111
3112 test_request([args1].into_iter().chain(args2), |interfaces_request_stream| {
3113 interfaces_request_stream
3114 .filter_map(|req| handle_get_admin_for_eth_or_panic(req))
3115 .into_future()
3116 .map(|(admin_control_stream, _stream_of_admin_control_streams)| {
3120 admin_control_stream.unwrap()
3121 })
3122 .flatten_stream()
3123 .into_future()
3124 .map(|(admin_control_req, admin_control_stream)| {
3125 control_request_handler1(admin_control_req.unwrap().unwrap()).chain(
3126 futures::stream::iter(control_request_handler2.map(
3127 |mut control_request_handler2| {
3128 admin_control_stream
3129 .into_future()
3130 .map(move |(admin_control_req, _admin_control_stream)| {
3131 control_request_handler2(
3132 admin_control_req.unwrap().unwrap(),
3133 )
3134 })
3135 .flatten_stream()
3136 },
3137 ))
3138 .flatten(),
3139 )
3140 })
3141 .flatten_stream()
3142 })
3143 .await
3144 }
3145
3146 async fn test_interface_request<
3149 St: Stream<Item = fnet_interfaces::Event>,
3150 F: FnMut(fnet_interfaces_admin::ControlRequest) -> St,
3151 >(
3152 case: TestInterfaceRequestCase<F>,
3153 ) -> TestRequestResult {
3154 test_maybe_two_interface_requests_on_single_control(
3155 case,
3156 None::<TestInterfaceRequestCase<fn(_) -> futures::stream::Pending<_>>>,
3157 )
3158 .await
3159 }
3160
3161 async fn test_new_addr_asp_helper<
3164 St: Stream<Item = fnet_interfaces::Event>,
3165 F: Fn(fnet_interfaces_admin::AddressStateProviderRequestStream) -> St,
3166 >(
3167 address: AddrSubnetEither,
3168 add_subnet_route: bool,
3169 asp_handler: F,
3170 ) -> TestRequestResult {
3171 test_interface_request(TestInterfaceRequestCase {
3172 address,
3173 kind: AddressRequestKind::New { add_subnet_route },
3174 control_request_handler: |req| match req {
3175 fnet_interfaces_admin::ControlRequest::AddAddress {
3176 address: got_address,
3177 parameters,
3178 address_state_provider,
3179 control_handle: _,
3180 } => {
3181 pretty_assertions::assert_eq!(got_address, address.into_ext());
3182 pretty_assertions::assert_eq!(
3183 parameters,
3184 fnet_interfaces_admin::AddressParameters {
3185 add_subnet_route: Some(add_subnet_route),
3186 ..fnet_interfaces_admin::AddressParameters::default()
3187 },
3188 );
3189 asp_handler(address_state_provider.into_stream())
3190 }
3191 req => panic!("unexpected request {req:?}"),
3192 },
3193 })
3194 .await
3195 }
3196
3197 #[test_case(test_addr_subnet_v4(); "v4")]
3200 #[test_case(test_addr_subnet_v6(); "v6")]
3201 #[fuchsia::test]
3202 async fn test_new_addr_drop_asp_immediately(address: AddrSubnetEither) {
3203 pretty_assertions::assert_eq!(
3204 test_new_addr_asp_helper(address, false, |_asp_request_stream| {
3205 futures::stream::empty()
3206 })
3207 .await,
3208 TestRequestResult {
3209 messages: Vec::new(),
3210 waiter_results: vec![Err(RequestError::UnrecognizedInterface)],
3211 },
3212 )
3213 }
3214
3215 async fn test_new_addr_failed_helper(
3218 address: AddrSubnetEither,
3219 reason: AddressRemovalReason,
3220 ) -> TestRequestResult {
3221 test_new_addr_asp_helper(address, true, |asp_request_stream| {
3222 asp_request_stream.control_handle().send_on_address_removed(reason).unwrap();
3223 futures::stream::empty()
3224 })
3225 .await
3226 }
3227
3228 #[test_case(
3231 test_addr_subnet_v4(),
3232 AddressRemovalReason::DadFailed; "v4_dad_failed")]
3233 #[test_case(
3234 test_addr_subnet_v6(),
3235 AddressRemovalReason::DadFailed; "v6_dad_failed")]
3236 #[test_case(
3237 test_addr_subnet_v4(),
3238 AddressRemovalReason::InterfaceRemoved; "v4_interface_removed")]
3239 #[test_case(
3240 test_addr_subnet_v6(),
3241 AddressRemovalReason::InterfaceRemoved; "v6_interface_removed")]
3242 #[test_case(
3243 test_addr_subnet_v4(),
3244 AddressRemovalReason::UserRemoved; "v4_user_removed")]
3245 #[test_case(
3246 test_addr_subnet_v6(),
3247 AddressRemovalReason::UserRemoved; "v6_user_removed")]
3248 #[should_panic(expected = "expected netstack to send initial state before removing")]
3249 #[fuchsia::test]
3250 async fn test_new_addr_failed_unexpected_reason(
3251 address: AddrSubnetEither,
3252 reason: AddressRemovalReason,
3253 ) {
3254 let _: TestRequestResult = test_new_addr_failed_helper(address, reason).await;
3255 }
3256
3257 #[test_case(
3259 test_addr_subnet_v4(),
3260 AddressRemovalReason::Invalid,
3261 RequestError::InvalidRequest; "v4_invalid")]
3262 #[test_case(
3263 test_addr_subnet_v6(),
3264 AddressRemovalReason::Invalid,
3265 RequestError::InvalidRequest; "v6_invalid")]
3266 #[test_case(
3267 test_addr_subnet_v4(),
3268 AddressRemovalReason::AlreadyAssigned,
3269 RequestError::AlreadyExists; "v4_exists")]
3270 #[test_case(
3271 test_addr_subnet_v6(),
3272 AddressRemovalReason::AlreadyAssigned,
3273 RequestError::AlreadyExists; "v6_exists")]
3274 #[fuchsia::test]
3275 async fn test_new_addr_failed(
3276 address: AddrSubnetEither,
3277 reason: AddressRemovalReason,
3278 expected_error: RequestError,
3279 ) {
3280 pretty_assertions::assert_eq!(
3281 test_new_addr_failed_helper(address, reason).await,
3282 TestRequestResult { messages: Vec::new(), waiter_results: vec![Err(expected_error)] },
3283 )
3284 }
3285
3286 async fn test_new_addr_asp_detach_handled_helper<
3289 St: Stream<Item = fnet_interfaces::Event>,
3290 F: Fn(fnet_interfaces_admin::AddressStateProviderRequestStream) -> St,
3291 >(
3292 address: AddrSubnetEither,
3293 add_subnet_route: bool,
3294 asp_handler: F,
3295 ) -> TestRequestResult {
3296 test_new_addr_asp_helper(address, add_subnet_route, |asp_request_stream| {
3297 asp_request_stream
3298 .into_future()
3299 .map(|(asp_request, asp_request_stream)| {
3300 let _: fnet_interfaces_admin::AddressStateProviderControlHandle = asp_request
3301 .expect("eventloop uses ASP before dropping")
3302 .expect("unexpected error while waiting for Detach request")
3303 .into_detach()
3304 .expect("eventloop makes detach request immediately");
3305
3306 asp_handler(asp_request_stream)
3307 })
3308 .flatten_stream()
3309 })
3310 .await
3311 }
3312
3313 #[test_case(test_addr_subnet_v4(); "v4")]
3316 #[test_case(test_addr_subnet_v6(); "v6")]
3317 #[fuchsia::test]
3318 async fn test_new_addr_drop_asp_after_detach(address: AddrSubnetEither) {
3319 pretty_assertions::assert_eq!(
3320 test_new_addr_asp_detach_handled_helper(address, false, |_asp_stream| {
3321 futures::stream::empty()
3322 })
3323 .await,
3324 TestRequestResult {
3325 messages: Vec::new(),
3326 waiter_results: vec![Err(RequestError::UnrecognizedInterface)],
3327 },
3328 )
3329 }
3330
3331 #[test_case(add_test_addr_subnet_v4(); "v4")]
3333 #[test_case(add_test_addr_subnet_v6(); "v6")]
3334 #[fuchsia::test]
3335 async fn test_new_addr_with_address_added_event(address: AddrSubnetEither) {
3336 pretty_assertions::assert_eq!(
3337 test_new_addr_asp_detach_handled_helper(address, true, |asp_request_stream| {
3338 asp_request_stream
3339 .control_handle()
3340 .send_on_address_added()
3341 .expect("send address added");
3342
3343 futures::stream::iter([fnet_interfaces::Event::Changed(
3346 fnet_interfaces::Properties {
3347 id: Some(ETH_INTERFACE_ID.try_into().unwrap()),
3348 addresses: Some(vec![test_addr(address.into_ext())]),
3349 ..fnet_interfaces::Properties::default()
3350 },
3351 )])
3352 })
3353 .await,
3354 TestRequestResult { messages: Vec::new(), waiter_results: vec![Ok(())] },
3355 )
3356 }
3357
3358 #[test_case(
3360 test_addr_subnet_v4(),
3361 InterfaceRemovedReason::DuplicateName; "v4_duplicate_name")]
3362 #[test_case(
3363 test_addr_subnet_v6(),
3364 InterfaceRemovedReason::DuplicateName; "v6_duplicate_name")]
3365 #[test_case(
3366 test_addr_subnet_v4(),
3367 InterfaceRemovedReason::PortAlreadyBound; "v4_port_already_bound")]
3368 #[test_case(
3369 test_addr_subnet_v6(),
3370 InterfaceRemovedReason::PortAlreadyBound; "v6_port_already_bound")]
3371 #[test_case(
3372 test_addr_subnet_v4(),
3373 InterfaceRemovedReason::BadPort; "v4_bad_port")]
3374 #[test_case(
3375 test_addr_subnet_v6(),
3376 InterfaceRemovedReason::BadPort; "v6_bad_port")]
3377 #[should_panic(expected = "unexpected interface removed reason")]
3378 #[fuchsia::test]
3379 async fn test_del_addr_interface_closed_unexpected_reason(
3380 address: AddrSubnetEither,
3381 removal_reason: InterfaceRemovedReason,
3382 ) {
3383 let _: TestRequestResult = test_interface_request(TestInterfaceRequestCase {
3384 address,
3385 kind: AddressRequestKind::Del,
3386 control_request_handler: |req| match req {
3387 fnet_interfaces_admin::ControlRequest::RemoveAddress {
3388 address: got_address,
3389 responder,
3390 } => {
3391 pretty_assertions::assert_eq!(got_address, address.into_ext());
3392 let control_handle = responder.control_handle();
3393 control_handle.send_on_interface_removed(removal_reason).unwrap();
3394 control_handle.shutdown();
3395 futures::stream::empty()
3396 }
3397 req => panic!("unexpected request {req:?}"),
3398 },
3399 })
3400 .await;
3401 }
3402
3403 fn del_addr_test_interface_case(
3404 address: AddrSubnetEither,
3405 response: Result<bool, fnet_interfaces_admin::ControlRemoveAddressError>,
3406 remaining_address: Option<AddrSubnetEither>,
3407 ) -> TestInterfaceRequestCase<
3408 impl FnMut(
3409 fnet_interfaces_admin::ControlRequest,
3410 ) -> futures::stream::Iter<core::array::IntoIter<fnet_interfaces::Event, 1>>,
3411 > {
3412 TestInterfaceRequestCase {
3413 address,
3414 kind: AddressRequestKind::Del,
3415 control_request_handler: move |req| {
3416 match req {
3417 fnet_interfaces_admin::ControlRequest::RemoveAddress {
3418 address: got_address,
3419 responder,
3420 } => {
3421 pretty_assertions::assert_eq!(got_address, address.into_ext());
3422 responder.send(response).unwrap();
3423
3424 futures::stream::iter([fnet_interfaces::Event::Changed(
3427 fnet_interfaces::Properties {
3428 id: Some(ETH_INTERFACE_ID.try_into().unwrap()),
3429 addresses: Some(remaining_address.map_or_else(Vec::new, |addr| {
3430 vec![test_addr(addr.into_ext())]
3431 })),
3432 ..fnet_interfaces::Properties::default()
3433 },
3434 )])
3435 }
3436 req => panic!("unexpected request {req:?}"),
3437 }
3438 },
3439 }
3440 }
3441
3442 #[test_case(
3444 test_addr_subnet_v4(),
3445 Ok(true),
3446 Ok(()); "v4_did_remove")]
3447 #[test_case(
3448 test_addr_subnet_v6(),
3449 Ok(true),
3450 Ok(()); "v6_did_remove")]
3451 #[test_case(
3452 test_addr_subnet_v4(),
3453 Ok(false),
3454 Err(RequestError::AddressNotFound); "v4_did_not_remove")]
3455 #[test_case(
3456 test_addr_subnet_v6(),
3457 Ok(false),
3458 Err(RequestError::AddressNotFound); "v6_did_not_remove")]
3459 #[test_case(
3460 test_addr_subnet_v4(),
3461 Err(fnet_interfaces_admin::ControlRemoveAddressError::unknown()),
3462 Err(RequestError::InvalidRequest); "v4_unrecognized_error")]
3463 #[test_case(
3464 test_addr_subnet_v6(),
3465 Err(fnet_interfaces_admin::ControlRemoveAddressError::unknown()),
3466 Err(RequestError::InvalidRequest); "v6_unrecognized_error")]
3467 #[fuchsia::test]
3468 async fn test_del_addr(
3469 address: AddrSubnetEither,
3470 response: Result<bool, fnet_interfaces_admin::ControlRemoveAddressError>,
3471 waiter_result: Result<(), RequestError>,
3472 ) {
3473 pretty_assertions::assert_eq!(
3474 test_interface_request(del_addr_test_interface_case(address, response, None)).await,
3475 TestRequestResult { messages: Vec::new(), waiter_results: vec![waiter_result] },
3476 )
3477 }
3478
3479 #[fuchsia::test]
3482 async fn test_single_get_admin_for_multiple_interface_requests() {
3483 let first_address = test_addr_subnet_v4();
3484 let second_address = test_addr_subnet_v6();
3485 pretty_assertions::assert_eq!(
3486 test_maybe_two_interface_requests_on_single_control(
3487 del_addr_test_interface_case(first_address, Ok(true), Some(second_address)),
3488 Some(del_addr_test_interface_case(second_address, Ok(true), None)),
3489 )
3490 .await,
3491 TestRequestResult { messages: Vec::new(), waiter_results: vec![Ok(()), Ok(())] },
3492 )
3493 }
3494}