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