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