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