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::convert::Infallible as Never;
1565 use std::sync::Arc;
1566
1567 use futures::TryStreamExt as _;
1568 use futures::channel::mpsc;
1569 use futures::future::Future;
1570 use futures::stream::Stream;
1571 use net_declare::{fidl_subnet, net_addr_subnet};
1572
1573 use crate::client::AsyncWorkItem;
1574 use crate::messaging::testutil::FakeSender;
1575 use crate::route_eventloop::{EventLoopComponent, IncludedWorkers, Optional, Required};
1576
1577 pub(crate) const LO_INTERFACE_ID: u64 = 1;
1578 pub(crate) const LO_NAME: &str = "lo";
1579 pub(crate) const ETH_INTERFACE_ID: u64 = 2;
1580 pub(crate) const ETH_NAME: &str = "eth";
1581 pub(crate) const WLAN_INTERFACE_ID: u64 = 3;
1582 pub(crate) const WLAN_NAME: &str = "wlan";
1583 pub(crate) const PPP_INTERFACE_ID: u64 = 4;
1584 pub(crate) const PPP_NAME: &str = "ppp";
1585
1586 pub(crate) const BRIDGE: fnet_interfaces_ext::PortClass =
1587 fnet_interfaces_ext::PortClass::Bridge;
1588 pub(crate) const ETHERNET: fnet_interfaces_ext::PortClass =
1589 fnet_interfaces_ext::PortClass::Ethernet;
1590 pub(crate) const WLAN_CLIENT: fnet_interfaces_ext::PortClass =
1591 fnet_interfaces_ext::PortClass::WlanClient;
1592 pub(crate) const WLAN_AP: fnet_interfaces_ext::PortClass =
1593 fnet_interfaces_ext::PortClass::WlanAp;
1594 pub(crate) const PPP: fnet_interfaces_ext::PortClass = fnet_interfaces_ext::PortClass::Ppp;
1595 pub(crate) const LOOPBACK: fnet_interfaces_ext::PortClass =
1596 fnet_interfaces_ext::PortClass::Loopback;
1597 pub(crate) const TEST_V4_ADDR: fnet::Subnet = fidl_subnet!("192.0.2.1/24");
1598 pub(crate) const TEST_V6_ADDR: fnet::Subnet = fidl_subnet!("2001:db8::1/32");
1599
1600 pub(crate) fn test_addr_subnet_v4() -> AddrSubnetEither {
1602 net_addr_subnet!("192.0.2.1/24")
1603 }
1604
1605 pub(crate) fn test_addr_subnet_v6() -> AddrSubnetEither {
1607 net_addr_subnet!("2001:db8::1/32")
1608 }
1609
1610 pub(crate) fn add_test_addr_subnet_v4() -> AddrSubnetEither {
1612 net_addr_subnet!("192.0.2.2/24")
1613 }
1614
1615 pub(crate) fn add_test_addr_subnet_v6() -> AddrSubnetEither {
1617 net_addr_subnet!("2001:db8::2/32")
1618 }
1619
1620 #[derive(Debug, PartialEq, Eq)]
1621 pub(crate) enum HandledLinkKind {
1622 New,
1623 Del,
1624 }
1625
1626 #[derive(Debug, PartialEq, Eq)]
1627 pub(crate) struct HandledLink {
1628 pub name: String,
1629 pub kind: HandledLinkKind,
1630 }
1631
1632 pub(crate) struct FakeInterfacesHandlerSink(Arc<Mutex<Vec<HandledLink>>>);
1633
1634 impl FakeInterfacesHandlerSink {
1635 pub(crate) fn take_handled(&mut self) -> Vec<HandledLink> {
1636 let Self(rc) = self;
1637 core::mem::take(&mut *rc.lock())
1638 }
1639 }
1640
1641 pub(crate) struct FakeInterfacesHandler(Arc<Mutex<Vec<HandledLink>>>);
1642
1643 impl FakeInterfacesHandler {
1644 pub(crate) fn new() -> (FakeInterfacesHandler, FakeInterfacesHandlerSink) {
1645 let inner = Arc::default();
1646 (FakeInterfacesHandler(Arc::clone(&inner)), FakeInterfacesHandlerSink(inner))
1647 }
1648 }
1649
1650 impl InterfacesHandler for FakeInterfacesHandler {
1651 fn handle_new_link(&mut self, name: &str, _interface_id: NonZeroU64) {
1652 let Self(rc) = self;
1653 rc.lock().push(HandledLink { name: name.to_string(), kind: HandledLinkKind::New })
1654 }
1655
1656 fn handle_deleted_link(&mut self, name: &str) {
1657 let Self(rc) = self;
1658 rc.lock().push(HandledLink { name: name.to_string(), kind: HandledLinkKind::Del })
1659 }
1660 }
1661
1662 enum OnlyInterfaces {}
1663 impl crate::route_eventloop::EventLoopSpec for OnlyInterfaces {
1664 type InterfacesProxy = Required;
1665 type InterfacesStateProxy = Required;
1666 type InterfacesHandler = Required;
1667 type RouteClients = Required;
1668
1669 type V4RoutesState = Optional;
1670 type V6RoutesState = Optional;
1671 type V4RoutesSetProvider = Optional;
1672 type V6RoutesSetProvider = Optional;
1673 type V4RouteTableProvider = Optional;
1674 type V6RouteTableProvider = Optional;
1675
1676 type InterfacesWorker = Required;
1677 type RoutesV4Worker = Optional;
1678 type RoutesV6Worker = Optional;
1679 type RuleV4Worker = Optional;
1680 type RuleV6Worker = Optional;
1681 type NduseroptWorker = Optional;
1682 type NeighborWorker = Optional;
1683 }
1684
1685 pub(crate) struct Setup<E, W> {
1686 pub event_loop_fut: E,
1687 pub watcher_stream: W,
1688 pub request_sink:
1689 mpsc::Sender<crate::route_eventloop::UnifiedRequest<FakeSender<RouteNetlinkMessage>>>,
1690 pub interfaces_request_stream: fnet_root::InterfacesRequestStream,
1691 pub interfaces_handler_sink: FakeInterfacesHandlerSink,
1692 pub _async_work_sink: mpsc::UnboundedSender<AsyncWorkItem<NetlinkRoute>>,
1693 }
1694
1695 pub(crate) fn setup_with_route_clients(
1696 route_clients: ClientTable<NetlinkRoute, FakeSender<RouteNetlinkMessage>>,
1697 ) -> Setup<impl Future<Output = Never>, impl Stream<Item = fnet_interfaces::WatcherRequest>>
1698 {
1699 let (request_sink, request_stream) = mpsc::channel(1);
1700 let (interfaces_handler, interfaces_handler_sink) = FakeInterfacesHandler::new();
1701 let (interfaces_proxy, interfaces) =
1702 fidl::endpoints::create_proxy::<fnet_root::InterfacesMarker>();
1703 let (interfaces_state_proxy, interfaces_state) =
1704 fidl::endpoints::create_proxy::<fnet_interfaces::StateMarker>();
1705 let (async_work_sink, async_work_receiver) = mpsc::unbounded();
1706 let event_loop_inputs = crate::route_eventloop::EventLoopInputs::<_, _, OnlyInterfaces> {
1707 route_clients: EventLoopComponent::Present(route_clients),
1708 interfaces_handler: EventLoopComponent::Present(interfaces_handler),
1709 interfaces_proxy: EventLoopComponent::Present(interfaces_proxy),
1710 interfaces_state_proxy: EventLoopComponent::Present(interfaces_state_proxy),
1711 async_work_receiver,
1712
1713 v4_routes_state: EventLoopComponent::Absent(Optional),
1714 v6_routes_state: EventLoopComponent::Absent(Optional),
1715 v4_main_route_table: EventLoopComponent::Absent(Optional),
1716 v6_main_route_table: EventLoopComponent::Absent(Optional),
1717 v4_route_table_provider: EventLoopComponent::Absent(Optional),
1718 v6_route_table_provider: EventLoopComponent::Absent(Optional),
1719 v4_rule_table: EventLoopComponent::Absent(Optional),
1720 v6_rule_table: EventLoopComponent::Absent(Optional),
1721 neighbors_view: EventLoopComponent::Absent(Optional),
1722 neighbors_controller: EventLoopComponent::Absent(Optional),
1723 ndp_option_watcher_provider: EventLoopComponent::Absent(Optional),
1724
1725 unified_request_stream: request_stream,
1726 };
1727
1728 let interfaces_request_stream = interfaces.into_stream();
1729 let if_stream = interfaces_state.into_stream();
1730 let watcher_stream = if_stream
1731 .and_then(|req| match req {
1732 fnet_interfaces::StateRequest::GetWatcher {
1733 options: _,
1734 watcher,
1735 control_handle: _,
1736 } => futures::future::ready(Ok(watcher.into_stream())),
1737 })
1738 .try_flatten()
1739 .map(|res| res.expect("watcher stream error"));
1740
1741 Setup {
1742 event_loop_fut: async move {
1743 let event_loop = event_loop_inputs
1744 .initialize(IncludedWorkers {
1745 interfaces: EventLoopComponent::Present(()),
1746 routes_v4: EventLoopComponent::Absent(Optional),
1747 routes_v6: EventLoopComponent::Absent(Optional),
1748 rules_v4: EventLoopComponent::Absent(Optional),
1749 rules_v6: EventLoopComponent::Absent(Optional),
1750 nduseropt: EventLoopComponent::Absent(Optional),
1751 neighbors: EventLoopComponent::Absent(Optional),
1752 })
1753 .await;
1754 event_loop.run().await
1755 },
1756 watcher_stream,
1757 request_sink,
1758 interfaces_request_stream,
1759 interfaces_handler_sink,
1760 _async_work_sink: async_work_sink,
1761 }
1762 }
1763
1764 pub(crate) async fn respond_to_watcher<S: Stream<Item = fnet_interfaces::WatcherRequest>>(
1765 stream: S,
1766 updates: impl IntoIterator<Item = fnet_interfaces::Event>,
1767 ) {
1768 stream
1769 .zip(futures::stream::iter(updates.into_iter()))
1770 .for_each(|(req, update)| async move {
1771 match req {
1772 fnet_interfaces::WatcherRequest::Watch { responder } => {
1773 responder.send(&update).expect("send watch response")
1774 }
1775 }
1776 })
1777 .await
1778 }
1779
1780 pub(crate) fn create_netlink_link_message(
1781 id: u64,
1782 link_type: u16,
1783 flags: u32,
1784 nlas: Vec<LinkAttribute>,
1785 ) -> NetlinkLinkMessage {
1786 let mut link_header = LinkHeader::default();
1787 link_header.index = id.try_into().expect("should fit into u32");
1788 link_header.link_layer_type = LinkLayerType::from(link_type);
1789 link_header.flags = LinkFlags::from_bits(flags).unwrap();
1790 link_header.change_mask = LinkFlags::from_bits(u32::MAX).unwrap();
1791
1792 let mut link_message = LinkMessage::default();
1793 link_message.header = link_header;
1794 link_message.attributes = nlas;
1795
1796 NetlinkLinkMessage(link_message)
1797 }
1798
1799 pub(crate) fn create_nlas(
1800 name: String,
1801 link_type: u16,
1802 online: bool,
1803 mac: &Option<fnet::MacAddress>,
1804 ) -> Vec<LinkAttribute> {
1805 [
1806 LinkAttribute::IfName(name),
1807 LinkAttribute::Link(link_type.into()),
1808 LinkAttribute::OperState(if online { State::Up } else { State::Down }),
1809 ]
1810 .into_iter()
1811 .chain(mac.map(|fnet::MacAddress { octets }| LinkAttribute::Address(octets.to_vec())))
1812 .collect()
1813 }
1814
1815 pub(crate) fn create_address_message(
1816 interface_id: u32,
1817 subnet: fnet::Subnet,
1818 interface_name: String,
1819 flags: u32,
1820 ) -> NetlinkAddressMessage {
1821 let mut addr_header = AddressHeader::default();
1822 let (family, addr) = match subnet.addr {
1823 fnet::IpAddress::Ipv4(ip_addr) => {
1824 (AddressFamily::Inet, IpAddr::V4(ip_addr.addr.into()))
1825 }
1826 fnet::IpAddress::Ipv6(ip_addr) => {
1827 (AddressFamily::Inet6, IpAddr::V6(ip_addr.addr.into()))
1828 }
1829 };
1830 addr_header.family = family.into();
1831 addr_header.prefix_len = subnet.prefix_len;
1832 addr_header.flags = AddressHeaderFlags::from_bits(flags as u8).unwrap().bits();
1833 addr_header.index = interface_id.into();
1834
1835 let nlas = vec![
1836 AddressAttribute::Address(addr),
1837 AddressAttribute::Label(interface_name),
1838 AddressAttribute::Flags(AddressFlags::from_bits(flags).unwrap()),
1839 ];
1840
1841 let mut addr_message = AddressMessage::default();
1842 addr_message.header = addr_header;
1843 addr_message.attributes = nlas;
1844 NetlinkAddressMessage(addr_message)
1845 }
1846
1847 pub(crate) fn test_addr_with_assignment_state(
1848 addr: fnet::Subnet,
1849 assignment_state: fnet_interfaces::AddressAssignmentState,
1850 ) -> fnet_interfaces::Address {
1851 fnet_interfaces_ext::Address::<fnet_interfaces_ext::AllInterest> {
1852 addr,
1853 valid_until: fnet_interfaces_ext::PositiveMonotonicInstant::INFINITE_FUTURE,
1854 preferred_lifetime_info: fnet_interfaces_ext::PreferredLifetimeInfo::preferred_forever(
1855 ),
1856 assignment_state,
1857 }
1858 .into()
1859 }
1860
1861 pub(crate) fn test_addr(addr: fnet::Subnet) -> fnet_interfaces::Address {
1862 test_addr_with_assignment_state(addr, fnet_interfaces::AddressAssignmentState::Assigned)
1863 }
1864}
1865
1866#[cfg(test)]
1867mod tests {
1868 use super::testutil::*;
1869 use super::*;
1870
1871 use std::pin::{Pin, pin};
1872
1873 use fidl::endpoints::{ControlHandle as _, RequestStream as _, Responder as _};
1874 use fidl_fuchsia_net as fnet;
1875 use fnet_interfaces::AddressAssignmentState;
1876 use fuchsia_async::{self as fasync};
1877
1878 use assert_matches::assert_matches;
1879 use futures::FutureExt as _;
1880 use futures::sink::SinkExt as _;
1881 use futures::stream::Stream;
1882 use linux_uapi::{IFA_F_PERMANENT, IFA_F_TENTATIVE, rtnetlink_groups_RTNLGRP_IPV4_ROUTE};
1883 use pretty_assertions::assert_eq;
1884 use test_case::test_case;
1885
1886 use crate::messaging::testutil::SentMessage;
1887
1888 const TEST_SEQUENCE_NUMBER: u32 = 1234;
1889
1890 fn create_interface(
1891 id: u64,
1892 name: String,
1893 port_class: fnet_interfaces_ext::PortClass,
1894 online: bool,
1895 addresses: Vec<fnet_interfaces_ext::Address<fnet_interfaces_ext::AllInterest>>,
1896 ) -> fnet_interfaces_ext::Properties<fnet_interfaces_ext::AllInterest> {
1897 fnet_interfaces_ext::Properties {
1898 id: NonZeroU64::new(id).unwrap(),
1899 name,
1900 port_class,
1901 online,
1902 addresses,
1903 has_default_ipv4_route: false,
1904 has_default_ipv6_route: false,
1905 port_identity_koid: None,
1906 }
1907 }
1908
1909 fn create_interface_with_addresses(
1910 id: u64,
1911 name: String,
1912 port_class: fnet_interfaces_ext::PortClass,
1913 online: bool,
1914 ) -> fnet_interfaces_ext::Properties<fnet_interfaces_ext::AllInterest> {
1915 let addresses = vec![
1916 fnet_interfaces_ext::Address {
1917 addr: TEST_V4_ADDR,
1918 valid_until: fnet_interfaces_ext::PositiveMonotonicInstant::INFINITE_FUTURE,
1919 assignment_state: AddressAssignmentState::Assigned,
1920 preferred_lifetime_info:
1921 fnet_interfaces_ext::PreferredLifetimeInfo::preferred_forever(),
1922 },
1923 fnet_interfaces_ext::Address {
1924 addr: TEST_V6_ADDR,
1925 valid_until: fnet_interfaces_ext::PositiveMonotonicInstant::INFINITE_FUTURE,
1926 assignment_state: AddressAssignmentState::Assigned,
1927 preferred_lifetime_info:
1928 fnet_interfaces_ext::PreferredLifetimeInfo::preferred_forever(),
1929 },
1930 ];
1931 create_interface(id, name, port_class, online, addresses)
1932 }
1933
1934 fn create_default_address_messages(
1935 interface_id: u64,
1936 interface_name: String,
1937 flags: u32,
1938 ) -> BTreeMap<fnet::IpAddress, NetlinkAddressMessage> {
1939 let interface_id = interface_id.try_into().expect("should fit into u32");
1940 BTreeMap::from_iter([
1941 (
1942 TEST_V4_ADDR.addr,
1943 create_address_message(interface_id, TEST_V4_ADDR, interface_name.clone(), flags),
1944 ),
1945 (
1946 TEST_V6_ADDR.addr,
1947 create_address_message(interface_id, TEST_V6_ADDR, interface_name, flags),
1948 ),
1949 ])
1950 }
1951
1952 #[test_case(ETHERNET, false, 0, ARPHRD_ETHER)]
1953 #[test_case(ETHERNET, true, ONLINE_IF_FLAGS, ARPHRD_ETHER)]
1954 #[test_case(WLAN_CLIENT, false, net_device_flags_IFF_UP, ARPHRD_ETHER)]
1955 #[test_case(WLAN_CLIENT, true, ONLINE_IF_FLAGS, ARPHRD_ETHER)]
1956 #[test_case(WLAN_AP, false, 0, ARPHRD_ETHER)]
1957 #[test_case(WLAN_AP, true, ONLINE_IF_FLAGS, ARPHRD_ETHER)]
1958 #[test_case(PPP, false, 0, ARPHRD_PPP)]
1959 #[test_case(PPP, true, ONLINE_IF_FLAGS, ARPHRD_PPP)]
1960 #[test_case(LOOPBACK, false, net_device_flags_IFF_LOOPBACK, ARPHRD_LOOPBACK)]
1961 #[test_case(LOOPBACK, true, ONLINE_IF_FLAGS | net_device_flags_IFF_LOOPBACK, ARPHRD_LOOPBACK)]
1962 #[test_case(BRIDGE, false, 0, ARPHRD_ETHER)]
1963 #[test_case(BRIDGE, true, ONLINE_IF_FLAGS, ARPHRD_ETHER)]
1964 fn test_interface_conversion(
1965 port_class: fnet_interfaces_ext::PortClass,
1966 online: bool,
1967 flags: u32,
1968 expected_link_type: u32,
1969 ) {
1970 let expected_link_type = expected_link_type as u16;
1973 let interface_name = LO_NAME.to_string();
1974 let interface =
1975 create_interface(LO_INTERFACE_ID, interface_name.clone(), port_class, online, vec![]);
1976 let actual: NetlinkLinkMessage =
1977 interface_properties_to_link_message(&interface, &LO_MAC.map(|a| a.octets.to_vec()))
1978 .unwrap();
1979
1980 let nlas = create_nlas(interface_name, expected_link_type, online, &LO_MAC);
1981 let expected =
1982 create_netlink_link_message(LO_INTERFACE_ID, expected_link_type, flags, nlas);
1983 pretty_assertions::assert_eq!(actual, expected);
1984 }
1985
1986 #[fuchsia::test]
1987 fn test_oversized_interface_id_link_address_conversion() {
1988 let invalid_interface_id = (u32::MAX as u64) + 1;
1989 let interface =
1990 create_interface(invalid_interface_id, "test".into(), ETHERNET, true, vec![]);
1991
1992 let actual_link_message = interface_properties_to_link_message(&interface, &None);
1993 assert_eq!(
1994 actual_link_message,
1995 Err(NetlinkLinkMessageConversionError::InvalidInterfaceId(invalid_interface_id))
1996 );
1997
1998 assert_eq!(
1999 interface_properties_to_address_messages(&interface),
2000 Err(NetlinkAddressMessageConversionError::InvalidInterfaceId(invalid_interface_id))
2001 );
2002 }
2003
2004 #[fuchsia::test]
2005 fn test_interface_to_address_conversion() {
2006 let interface_name: String = "test".into();
2007 let interface_id = 1;
2008
2009 let interface =
2010 create_interface_with_addresses(interface_id, interface_name.clone(), ETHERNET, true);
2011 let actual = interface_properties_to_address_messages(&interface).unwrap();
2012
2013 let expected =
2014 create_default_address_messages(interface_id, interface_name, IFA_F_PERMANENT);
2015 assert_eq!(actual, expected);
2016 }
2017
2018 #[test]
2019 fn test_into_rtnl_new_link_is_serializable() {
2020 let link = create_netlink_link_message(0, 0, 0, vec![]);
2021 let new_link_message = link.into_rtnl_new_link(UNSPECIFIED_SEQUENCE_NUMBER, false);
2022 let mut buf = vec![0; new_link_message.buffer_len()];
2023 new_link_message.serialize(&mut buf);
2025 }
2026
2027 #[test]
2028 fn test_into_rtnl_del_link_is_serializable() {
2029 let link = create_netlink_link_message(0, 0, 0, vec![]);
2030 let del_link_message = link.into_rtnl_del_link(UNSPECIFIED_SEQUENCE_NUMBER);
2031 let mut buf = vec![0; del_link_message.buffer_len()];
2032 del_link_message.serialize(&mut buf);
2034 }
2035
2036 #[fuchsia::test]
2037 async fn test_deliver_updates() {
2038 let (mut link_sink, link_client, _async_work_drain_task) =
2039 crate::client::testutil::new_fake_client::<NetlinkRoute>(
2040 crate::client::testutil::CLIENT_ID_1,
2041 [ModernGroup(rtnetlink_groups_RTNLGRP_LINK)],
2042 );
2043 let (mut addr4_sink, addr4_client, _async_work_drain_task) =
2044 crate::client::testutil::new_fake_client::<NetlinkRoute>(
2045 crate::client::testutil::CLIENT_ID_2,
2046 [ModernGroup(rtnetlink_groups_RTNLGRP_IPV4_IFADDR)],
2047 );
2048 let (mut addr6_sink, addr6_client, _async_work_drain_task) =
2049 crate::client::testutil::new_fake_client::<NetlinkRoute>(
2050 crate::client::testutil::CLIENT_ID_3,
2051 [ModernGroup(rtnetlink_groups_RTNLGRP_IPV6_IFADDR)],
2052 );
2053 let (mut other_sink, other_client, _async_work_drain_task) =
2054 crate::client::testutil::new_fake_client::<NetlinkRoute>(
2055 crate::client::testutil::CLIENT_ID_4,
2056 [ModernGroup(rtnetlink_groups_RTNLGRP_IPV4_ROUTE)],
2057 );
2058 let (mut all_sink, all_client, _async_work_drain_task) =
2059 crate::client::testutil::new_fake_client::<NetlinkRoute>(
2060 crate::client::testutil::CLIENT_ID_5,
2061 [
2062 ModernGroup(rtnetlink_groups_RTNLGRP_LINK),
2063 ModernGroup(rtnetlink_groups_RTNLGRP_IPV6_IFADDR),
2064 ModernGroup(rtnetlink_groups_RTNLGRP_IPV4_IFADDR),
2065 ],
2066 );
2067 let Setup {
2068 event_loop_fut,
2069 mut watcher_stream,
2070 request_sink: _,
2071 interfaces_request_stream,
2072 mut interfaces_handler_sink,
2073 _async_work_sink: _,
2074 } = setup_with_route_clients({
2075 let route_clients = ClientTable::default();
2076 route_clients.add_client(link_client);
2077 route_clients.add_client(addr4_client);
2078 route_clients.add_client(addr6_client);
2079 route_clients.add_client(other_client);
2080 route_clients.add_client(all_client);
2081 route_clients
2082 });
2083 let event_loop_fut = event_loop_fut.fuse();
2084 let mut event_loop_fut = pin!(event_loop_fut);
2085 let root_interfaces_fut =
2086 handle_only_get_mac_root_requests_fut(interfaces_request_stream).fuse();
2087 let mut root_interfaces_fut = pin!(root_interfaces_fut);
2088
2089 let watcher_stream_fut = respond_to_watcher(
2091 watcher_stream.by_ref(),
2092 [
2093 fnet_interfaces::Event::Existing(fnet_interfaces::Properties {
2094 id: Some(LO_INTERFACE_ID),
2095 name: Some(LO_NAME.to_string()),
2096 port_class: Some(LOOPBACK.into()),
2097 online: Some(false),
2098 addresses: Some(vec![test_addr_with_assignment_state(
2099 TEST_V4_ADDR,
2100 fnet_interfaces::AddressAssignmentState::Assigned,
2101 )]),
2102 has_default_ipv4_route: Some(false),
2103 has_default_ipv6_route: Some(false),
2104 ..Default::default()
2105 }),
2106 fnet_interfaces::Event::Existing(fnet_interfaces::Properties {
2107 id: Some(ETH_INTERFACE_ID),
2108 name: Some(ETH_NAME.to_string()),
2109 port_class: Some(ETHERNET.into()),
2110 online: Some(false),
2111 addresses: Some(vec![
2112 test_addr_with_assignment_state(
2113 TEST_V6_ADDR,
2114 fnet_interfaces::AddressAssignmentState::Unavailable,
2115 ),
2116 test_addr_with_assignment_state(
2117 TEST_V4_ADDR,
2118 fnet_interfaces::AddressAssignmentState::Unavailable,
2119 ),
2120 ]),
2121 has_default_ipv4_route: Some(false),
2122 has_default_ipv6_route: Some(false),
2123 ..Default::default()
2124 }),
2125 fnet_interfaces::Event::Existing(fnet_interfaces::Properties {
2126 id: Some(PPP_INTERFACE_ID),
2127 name: Some(PPP_NAME.to_string()),
2128 port_class: Some(PPP.into()),
2129 online: Some(false),
2130 addresses: Some(vec![
2131 test_addr_with_assignment_state(
2132 TEST_V4_ADDR,
2133 fnet_interfaces::AddressAssignmentState::Assigned,
2134 ),
2135 test_addr_with_assignment_state(
2136 TEST_V6_ADDR,
2137 fnet_interfaces::AddressAssignmentState::Assigned,
2138 ),
2139 ]),
2140 has_default_ipv4_route: Some(false),
2141 has_default_ipv6_route: Some(false),
2142 ..Default::default()
2143 }),
2144 fnet_interfaces::Event::Idle(fnet_interfaces::Empty),
2145 ],
2146 );
2147 futures::select! {
2148 () = watcher_stream_fut.fuse() => {},
2149 () = root_interfaces_fut => {
2150 unreachable!("root interfaces request stream should never end")
2151 }
2152 err = event_loop_fut => unreachable!("eventloop should not return: {err:?}"),
2153 }
2154 assert_eq!(&link_sink.take_messages()[..], &[]);
2155 assert_eq!(&addr4_sink.take_messages()[..], &[]);
2156 assert_eq!(&addr6_sink.take_messages()[..], &[]);
2157 assert_eq!(&other_sink.take_messages()[..], &[]);
2158 assert_eq!(&all_sink.take_messages()[..], &[]);
2159
2160 let watcher_stream_fut = respond_to_watcher(
2161 watcher_stream.by_ref(),
2162 [
2163 fnet_interfaces::Event::Added(fnet_interfaces::Properties {
2164 id: Some(WLAN_INTERFACE_ID),
2165 name: Some(WLAN_NAME.to_string()),
2166 port_class: Some(WLAN_CLIENT.into()),
2167 online: Some(false),
2168 addresses: Some(vec![
2169 test_addr_with_assignment_state(
2170 TEST_V4_ADDR,
2171 fnet_interfaces::AddressAssignmentState::Tentative,
2172 ),
2173 test_addr_with_assignment_state(
2174 TEST_V6_ADDR,
2175 fnet_interfaces::AddressAssignmentState::Tentative,
2176 ),
2177 ]),
2178 has_default_ipv4_route: Some(false),
2179 has_default_ipv6_route: Some(false),
2180 ..Default::default()
2181 }),
2182 fnet_interfaces::Event::Changed(fnet_interfaces::Properties {
2183 id: Some(LO_INTERFACE_ID),
2184 online: Some(true),
2185 addresses: Some(vec![
2186 test_addr_with_assignment_state(
2187 TEST_V4_ADDR,
2188 fnet_interfaces::AddressAssignmentState::Assigned,
2189 ),
2190 test_addr_with_assignment_state(
2191 TEST_V6_ADDR,
2192 fnet_interfaces::AddressAssignmentState::Assigned,
2193 ),
2194 ]),
2195 ..Default::default()
2196 }),
2197 fnet_interfaces::Event::Removed(ETH_INTERFACE_ID),
2198 fnet_interfaces::Event::Changed(fnet_interfaces::Properties {
2199 id: Some(PPP_INTERFACE_ID),
2200 addresses: Some(Vec::new()),
2201 ..Default::default()
2202 }),
2203 fnet_interfaces::Event::Changed(fnet_interfaces::Properties {
2204 id: Some(WLAN_INTERFACE_ID),
2205 has_default_ipv6_route: Some(true),
2206 ..Default::default()
2207 }),
2208 ],
2209 );
2210
2211 futures::select! {
2212 () = watcher_stream_fut.fuse() => {},
2213 () = root_interfaces_fut => {
2214 unreachable!("root interfaces request stream should never end")
2215 }
2216 err = event_loop_fut => unreachable!("eventloop should not return: {err:?}"),
2217 }
2218
2219 assert_matches!(event_loop_fut.now_or_never(), None);
2223
2224 assert_eq!(
2225 interfaces_handler_sink.take_handled(),
2226 [
2227 HandledLink { name: LO_NAME.to_string(), kind: HandledLinkKind::New },
2228 HandledLink { name: ETH_NAME.to_string(), kind: HandledLinkKind::New },
2229 HandledLink { name: PPP_NAME.to_string(), kind: HandledLinkKind::New },
2230 HandledLink { name: WLAN_NAME.to_string(), kind: HandledLinkKind::New },
2231 HandledLink { name: ETH_NAME.to_string(), kind: HandledLinkKind::Del },
2232 ],
2233 );
2234 let arphrd_ether_u16: u16 = ARPHRD_ETHER as u16;
2236 let arphrd_loopback_u16: u16 = ARPHRD_LOOPBACK as u16;
2238 let wlan_link = SentMessage::multicast(
2239 create_netlink_link_message(
2240 WLAN_INTERFACE_ID,
2241 arphrd_ether_u16,
2242 net_device_flags_IFF_UP, create_nlas(WLAN_NAME.to_string(), arphrd_ether_u16, false, &WLAN_MAC),
2244 )
2245 .into_rtnl_new_link(UNSPECIFIED_SEQUENCE_NUMBER, false),
2246 ModernGroup(rtnetlink_groups_RTNLGRP_LINK),
2247 );
2248 let lo_link = SentMessage::multicast(
2249 create_netlink_link_message(
2250 LO_INTERFACE_ID,
2251 arphrd_loopback_u16,
2252 ONLINE_IF_FLAGS | net_device_flags_IFF_LOOPBACK,
2253 create_nlas(LO_NAME.to_string(), arphrd_loopback_u16, true, &LO_MAC),
2254 )
2255 .into_rtnl_new_link(UNSPECIFIED_SEQUENCE_NUMBER, false),
2256 ModernGroup(rtnetlink_groups_RTNLGRP_LINK),
2257 );
2258 let eth_link = SentMessage::multicast(
2259 create_netlink_link_message(
2260 ETH_INTERFACE_ID,
2261 arphrd_ether_u16,
2262 0,
2263 create_nlas(ETH_NAME.to_string(), arphrd_ether_u16, false, Ð_MAC),
2264 )
2265 .into_rtnl_del_link(UNSPECIFIED_SEQUENCE_NUMBER),
2266 ModernGroup(rtnetlink_groups_RTNLGRP_LINK),
2267 );
2268 assert_eq!(
2269 &link_sink.take_messages()[..],
2270 &[wlan_link.clone(), lo_link.clone(), eth_link.clone(),],
2271 );
2272
2273 let wlan_v4_addr = SentMessage::multicast(
2274 create_address_message(
2275 WLAN_INTERFACE_ID.try_into().unwrap(),
2276 TEST_V4_ADDR,
2277 WLAN_NAME.to_string(),
2278 IFA_F_PERMANENT | IFA_F_TENTATIVE,
2279 )
2280 .to_rtnl_new_addr(UNSPECIFIED_SEQUENCE_NUMBER, false),
2281 ModernGroup(rtnetlink_groups_RTNLGRP_IPV4_IFADDR),
2282 );
2283 let eth_v4_addr = SentMessage::multicast(
2284 create_address_message(
2285 ETH_INTERFACE_ID.try_into().unwrap(),
2286 TEST_V4_ADDR,
2287 ETH_NAME.to_string(),
2288 IFA_F_PERMANENT | IFA_F_TENTATIVE,
2289 )
2290 .to_rtnl_del_addr(UNSPECIFIED_SEQUENCE_NUMBER),
2291 ModernGroup(rtnetlink_groups_RTNLGRP_IPV4_IFADDR),
2292 );
2293 let ppp_v4_addr = SentMessage::multicast(
2294 create_address_message(
2295 PPP_INTERFACE_ID.try_into().unwrap(),
2296 TEST_V4_ADDR,
2297 PPP_NAME.to_string(),
2298 IFA_F_PERMANENT,
2299 )
2300 .to_rtnl_del_addr(UNSPECIFIED_SEQUENCE_NUMBER),
2301 ModernGroup(rtnetlink_groups_RTNLGRP_IPV4_IFADDR),
2302 );
2303 assert_eq!(
2304 &addr4_sink.take_messages()[..],
2305 &[wlan_v4_addr.clone(), eth_v4_addr.clone(), ppp_v4_addr.clone(),],
2306 );
2307
2308 let wlan_v6_addr = SentMessage::multicast(
2309 create_address_message(
2310 WLAN_INTERFACE_ID.try_into().unwrap(),
2311 TEST_V6_ADDR,
2312 WLAN_NAME.to_string(),
2313 IFA_F_PERMANENT | IFA_F_TENTATIVE,
2314 )
2315 .to_rtnl_new_addr(UNSPECIFIED_SEQUENCE_NUMBER, false),
2316 ModernGroup(rtnetlink_groups_RTNLGRP_IPV6_IFADDR),
2317 );
2318 let lo_v6_addr = SentMessage::multicast(
2319 create_address_message(
2320 LO_INTERFACE_ID.try_into().unwrap(),
2321 TEST_V6_ADDR,
2322 LO_NAME.to_string(),
2323 IFA_F_PERMANENT,
2324 )
2325 .to_rtnl_new_addr(UNSPECIFIED_SEQUENCE_NUMBER, false),
2326 ModernGroup(rtnetlink_groups_RTNLGRP_IPV6_IFADDR),
2327 );
2328 let eth_v6_addr = SentMessage::multicast(
2329 create_address_message(
2330 ETH_INTERFACE_ID.try_into().unwrap(),
2331 TEST_V6_ADDR,
2332 ETH_NAME.to_string(),
2333 IFA_F_PERMANENT | IFA_F_TENTATIVE,
2334 )
2335 .to_rtnl_del_addr(UNSPECIFIED_SEQUENCE_NUMBER),
2336 ModernGroup(rtnetlink_groups_RTNLGRP_IPV6_IFADDR),
2337 );
2338 let ppp_v6_addr = SentMessage::multicast(
2339 create_address_message(
2340 PPP_INTERFACE_ID.try_into().unwrap(),
2341 TEST_V6_ADDR,
2342 PPP_NAME.to_string(),
2343 IFA_F_PERMANENT,
2344 )
2345 .to_rtnl_del_addr(UNSPECIFIED_SEQUENCE_NUMBER),
2346 ModernGroup(rtnetlink_groups_RTNLGRP_IPV6_IFADDR),
2347 );
2348 assert_eq!(
2349 &addr6_sink.take_messages()[..],
2350 &[wlan_v6_addr.clone(), lo_v6_addr.clone(), eth_v6_addr.clone(), ppp_v6_addr.clone(),],
2351 );
2352
2353 assert_eq!(
2354 &all_sink.take_messages()[..],
2355 &[
2356 wlan_link,
2358 wlan_v4_addr,
2359 wlan_v6_addr,
2360 lo_link,
2361 lo_v6_addr,
2362 eth_v4_addr,
2364 eth_v6_addr,
2365 eth_link,
2366 ppp_v4_addr,
2367 ppp_v6_addr,
2368 ],
2369 );
2370 assert_eq!(&other_sink.take_messages()[..], &[]);
2371 }
2372
2373 const LO_MAC: Option<fnet::MacAddress> = None;
2374 const ETH_MAC: Option<fnet::MacAddress> = Some(fnet::MacAddress { octets: [1, 1, 1, 1, 1, 1] });
2375 const PPP_MAC: Option<fnet::MacAddress> = Some(fnet::MacAddress { octets: [2, 2, 2, 2, 2, 2] });
2376 const WLAN_MAC: Option<fnet::MacAddress> =
2377 Some(fnet::MacAddress { octets: [3, 3, 3, 3, 3, 3] });
2378
2379 fn handle_get_mac_root_request_or_panic(req: fnet_root::InterfacesRequest) {
2380 match req {
2381 fnet_root::InterfacesRequest::GetMac { id, responder } => {
2382 let link_address = match id {
2383 LO_INTERFACE_ID => LO_MAC,
2384 ETH_INTERFACE_ID => ETH_MAC,
2385 PPP_INTERFACE_ID => PPP_MAC,
2386 WLAN_INTERFACE_ID => WLAN_MAC,
2387 id => panic!("unexpected interface ID {id}"),
2388 };
2389
2390 responder.send(Ok(link_address.as_ref())).unwrap()
2391 }
2392 req => panic!("unexpected request {:?}", req),
2393 }
2394 }
2395
2396 fn expect_only_get_mac_root_requests(
2397 interfaces_request_stream: fnet_root::InterfacesRequestStream,
2398 ) -> impl Stream<Item = fnet_interfaces::Event> {
2399 futures::stream::unfold(interfaces_request_stream, |interfaces_request_stream| async move {
2400 interfaces_request_stream
2401 .for_each(|req| async move { handle_get_mac_root_request_or_panic(req.unwrap()) })
2402 .await;
2403
2404 None
2405 })
2406 }
2407
2408 async fn handle_only_get_mac_root_requests_fut(
2409 interfaces_request_stream: fnet_root::InterfacesRequestStream,
2410 ) {
2411 expect_only_get_mac_root_requests(interfaces_request_stream)
2412 .for_each(|item| async move { panic!("unexpected item = {item:?}") })
2413 .await
2414 }
2415
2416 #[derive(Debug, PartialEq)]
2417 struct TestRequestResult {
2418 messages: Vec<SentMessage<RouteNetlinkMessage>>,
2419 waiter_results: Vec<Result<(), RequestError>>,
2420 }
2421
2422 async fn test_request<
2428 St: Stream<Item = fnet_interfaces::Event>,
2429 F: FnOnce(fnet_root::InterfacesRequestStream) -> St,
2430 >(
2431 args: impl IntoIterator<Item = RequestArgs>,
2432 root_handler: F,
2433 ) -> TestRequestResult {
2434 test_request_with_initial_state(
2435 args,
2436 root_handler,
2437 InitialState { eth_interface_online: false },
2438 )
2439 .await
2440 }
2441
2442 #[derive(Clone, Copy, Debug)]
2443 struct InitialState {
2444 eth_interface_online: bool,
2445 }
2446
2447 async fn test_request_with_initial_state<
2455 St: Stream<Item = fnet_interfaces::Event>,
2456 F: FnOnce(fnet_root::InterfacesRequestStream) -> St,
2457 >(
2458 args: impl IntoIterator<Item = RequestArgs>,
2459 root_handler: F,
2460 initial_state: InitialState,
2461 ) -> TestRequestResult {
2462 let scope = fasync::Scope::new();
2463 let result = {
2464 let InitialState { eth_interface_online } = initial_state;
2465
2466 let (mut expected_sink, expected_client, async_work_drain_task) =
2467 crate::client::testutil::new_fake_client::<NetlinkRoute>(
2468 crate::client::testutil::CLIENT_ID_1,
2469 std::iter::empty(),
2470 );
2471 let _join_handle = scope.spawn(async_work_drain_task);
2472 let (mut other_sink, other_client, async_work_drain_task) =
2473 crate::client::testutil::new_fake_client::<NetlinkRoute>(
2474 crate::client::testutil::CLIENT_ID_2,
2475 std::iter::empty(),
2476 );
2477 let _join_handle = scope.spawn(async_work_drain_task);
2478 let Setup {
2479 event_loop_fut,
2480 mut watcher_stream,
2481 request_sink,
2482 interfaces_request_stream,
2483 interfaces_handler_sink: _,
2484 _async_work_sink: _,
2485 } = setup_with_route_clients({
2486 let route_clients = ClientTable::default();
2487 route_clients.add_client(expected_client.clone());
2488 route_clients.add_client(other_client);
2489 route_clients
2490 });
2491 let event_loop_fut = event_loop_fut.fuse();
2492 let mut event_loop_fut = pin!(event_loop_fut);
2493
2494 let watcher_stream_fut = respond_to_watcher(
2495 watcher_stream.by_ref(),
2496 [
2497 fnet_interfaces::Event::Existing(fnet_interfaces::Properties {
2498 id: Some(LO_INTERFACE_ID),
2499 name: Some(LO_NAME.to_string()),
2500 port_class: Some(LOOPBACK.into()),
2501 online: Some(true),
2502 addresses: Some(vec![test_addr(TEST_V6_ADDR), test_addr(TEST_V4_ADDR)]),
2503 has_default_ipv4_route: Some(false),
2504 has_default_ipv6_route: Some(false),
2505 ..Default::default()
2506 }),
2507 fnet_interfaces::Event::Existing(fnet_interfaces::Properties {
2508 id: Some(ETH_INTERFACE_ID),
2509 name: Some(ETH_NAME.to_string()),
2510 port_class: Some(ETHERNET.into()),
2511 online: Some(eth_interface_online),
2512 addresses: Some(vec![test_addr(TEST_V4_ADDR), test_addr(TEST_V6_ADDR)]),
2513 has_default_ipv4_route: Some(false),
2514 has_default_ipv6_route: Some(false),
2515 ..Default::default()
2516 }),
2517 fnet_interfaces::Event::Idle(fnet_interfaces::Empty),
2518 ],
2519 );
2520 futures::select_biased! {
2521 err = event_loop_fut => unreachable!("eventloop should not return: {err:?}"),
2522 () = watcher_stream_fut.fuse() => {},
2523 }
2524 assert_eq!(&expected_sink.take_messages()[..], &[]);
2525 assert_eq!(&other_sink.take_messages()[..], &[]);
2526
2527 let expected_client = &expected_client;
2528 let fut = futures::stream::iter(args).fold(
2529 (Vec::new(), request_sink),
2530 |(mut results, mut request_sink), args| async move {
2531 let (completer, waiter) = oneshot::channel();
2532 request_sink
2533 .send(crate::route_eventloop::UnifiedRequest::InterfacesRequest(Request {
2534 args,
2535 sequence_number: TEST_SEQUENCE_NUMBER,
2536 client: expected_client.clone(),
2537 completer,
2538 }))
2539 .await
2540 .unwrap();
2541 results.push(waiter.await.unwrap());
2542 (results, request_sink)
2543 },
2544 );
2545 let watcher_fut = root_handler(interfaces_request_stream).map(Ok).forward(
2548 futures::sink::unfold(watcher_stream.by_ref(), |st, event| async {
2549 respond_to_watcher(st.by_ref(), [event]).await;
2550 Ok::<_, std::convert::Infallible>(st)
2551 }),
2552 );
2553 let waiter_results = futures::select_biased! {
2554 res = futures::future::join(watcher_fut, event_loop_fut) => {
2555 unreachable!("eventloop/watcher should not return: {res:?}")
2556 },
2557 (results, _request_sink) = fut.fuse() => results
2558 };
2559 assert_eq!(&other_sink.take_messages()[..], &[]);
2560
2561 TestRequestResult { messages: expected_sink.take_messages(), waiter_results }
2562 };
2563 scope.join().await;
2564 result
2565 }
2566
2567 #[test_case(
2568 GetLinkArgs::Dump,
2569 &[LO_INTERFACE_ID, ETH_INTERFACE_ID],
2570 Ok(()); "dump")]
2571 #[test_case(
2572 GetLinkArgs::Get(LinkSpecifier::Index(
2573 NonZeroU32::new(LO_INTERFACE_ID.try_into().unwrap()).unwrap())),
2574 &[LO_INTERFACE_ID],
2575 Ok(()); "id")]
2576 #[test_case(
2577 GetLinkArgs::Get(LinkSpecifier::Index(
2578 NonZeroU32::new(WLAN_INTERFACE_ID.try_into().unwrap()).unwrap())),
2579 &[],
2580 Err(RequestError::UnrecognizedInterface); "id_not_found")]
2581 #[test_case(
2582 GetLinkArgs::Get(LinkSpecifier::Name(LO_NAME.to_string())),
2583 &[LO_INTERFACE_ID],
2584 Ok(()); "name")]
2585 #[test_case(
2586 GetLinkArgs::Get(LinkSpecifier::Name(WLAN_NAME.to_string())),
2587 &[],
2588 Err(RequestError::UnrecognizedInterface); "name_not_found")]
2589 #[fuchsia::test]
2590 async fn test_get_link(
2591 args: GetLinkArgs,
2592 expected_new_links: &[u64],
2593 expected_result: Result<(), RequestError>,
2594 ) {
2595 let is_dump = match args {
2596 GetLinkArgs::Dump => true,
2597 GetLinkArgs::Get(_) => false,
2598 };
2599 let arphrd_ether_u16: u16 = ARPHRD_ETHER as u16;
2601 let arphrd_loopback_u16: u16 = ARPHRD_LOOPBACK as u16;
2603 let expected_messages = expected_new_links
2604 .iter()
2605 .map(|link_id| {
2606 let msg = match *link_id {
2607 LO_INTERFACE_ID => create_netlink_link_message(
2608 LO_INTERFACE_ID,
2609 arphrd_loopback_u16,
2610 ONLINE_IF_FLAGS | net_device_flags_IFF_LOOPBACK,
2611 create_nlas(LO_NAME.to_string(), arphrd_loopback_u16, true, &LO_MAC),
2612 ),
2613 ETH_INTERFACE_ID => create_netlink_link_message(
2614 ETH_INTERFACE_ID,
2615 arphrd_ether_u16,
2616 0,
2617 create_nlas(ETH_NAME.to_string(), arphrd_ether_u16, false, Ð_MAC),
2618 ),
2619 _ => unreachable!("GetLink should only be tested with loopback and ethernet"),
2620 };
2621 SentMessage::unicast(msg.into_rtnl_new_link(TEST_SEQUENCE_NUMBER, is_dump))
2622 })
2623 .collect();
2624
2625 assert_eq!(
2626 test_request(
2627 [RequestArgs::Link(LinkRequestArgs::Get(args))],
2628 expect_only_get_mac_root_requests,
2629 )
2630 .await,
2631 TestRequestResult {
2632 messages: expected_messages,
2633 waiter_results: vec![expected_result],
2634 },
2635 )
2636 }
2637
2638 fn handle_get_admin_for_eth_or_panic(
2639 req: Result<fnet_root::InterfacesRequest, fidl::Error>,
2640 ) -> impl Future<Output = Option<fnet_interfaces_admin::ControlRequestStream>> {
2641 futures::future::ready(match req.unwrap() {
2642 fnet_root::InterfacesRequest::GetAdmin { id, control, control_handle: _ } => {
2643 pretty_assertions::assert_eq!(id, ETH_INTERFACE_ID);
2644 Some(control.into_stream())
2645 }
2646 req => {
2647 handle_get_mac_root_request_or_panic(req);
2648 None
2649 }
2650 })
2651 }
2652
2653 fn expect_get_admin_with_handler<
2663 I: IntoIterator<Item = fnet_interfaces::Event> + 'static,
2664 H: FnMut(fnet_interfaces_admin::ControlRequest) -> I + 'static,
2665 >(
2666 admin_handler: H,
2667 ) -> impl FnOnce(
2668 fnet_root::InterfacesRequestStream,
2669 ) -> Pin<Box<dyn Stream<Item = fnet_interfaces::Event>>> {
2670 move |interfaces_request_stream: fnet_root::InterfacesRequestStream| {
2671 Box::pin(
2672 interfaces_request_stream
2673 .filter_map(|req| handle_get_admin_for_eth_or_panic(req))
2674 .into_future()
2675 .map(|(admin_control_stream, _stream_of_admin_control_streams)| {
2679 admin_control_stream.unwrap()
2680 })
2681 .flatten_stream()
2682 .scan(admin_handler, |admin_handler, req| {
2687 futures::future::ready(Some(futures::stream::iter(admin_handler(
2688 req.unwrap(),
2689 ))))
2690 })
2691 .flatten(),
2692 )
2693 }
2694 }
2695
2696 #[test_case(
2697 InitialState { eth_interface_online: false },
2698 SetLinkArgs{
2699 link: LinkSpecifier::Name(ETH_NAME.to_string()),
2700 enable: None,
2701 },
2702 Ok(true),
2703 Ok(()); "no_change")]
2704 #[test_case(
2705 InitialState { eth_interface_online: false },
2706 SetLinkArgs{
2707 link: LinkSpecifier::Name(WLAN_NAME.to_string()),
2708 enable: None,
2709 },
2710 Ok(true),
2711 Err(RequestError::UnrecognizedInterface); "no_change_name_not_found")]
2712 #[test_case(
2713 InitialState { eth_interface_online: false },
2714 SetLinkArgs {
2715 link: LinkSpecifier::Index(
2716 NonZeroU32::new(WLAN_INTERFACE_ID.try_into().unwrap()).unwrap()),
2717 enable: None,
2718 },
2719 Ok(true),
2720 Err(RequestError::UnrecognizedInterface); "no_change_id_not_found")]
2721 #[test_case(
2722 InitialState { eth_interface_online: false },
2723 SetLinkArgs{
2724 link: LinkSpecifier::Name(ETH_NAME.to_string()),
2725 enable: Some(true),
2726 },
2727 Ok(false),
2728 Ok(()); "enable_no_op_succeeds")]
2729 #[test_case(
2730 InitialState { eth_interface_online: false },
2731 SetLinkArgs{
2732 link: LinkSpecifier::Name(ETH_NAME.to_string()),
2733 enable: Some(true),
2734 },
2735 Ok(true),
2736 Ok(()); "enable_newly_succeeds")]
2737 #[test_case(
2738 InitialState { eth_interface_online: false },
2739 SetLinkArgs{
2740 link: LinkSpecifier::Name(WLAN_NAME.to_string()),
2741 enable: Some(true),
2742 },
2743 Ok(true),
2744 Err(RequestError::UnrecognizedInterface); "enable_not_found")]
2745 #[test_case(
2746 InitialState { eth_interface_online: false },
2747 SetLinkArgs{
2748 link: LinkSpecifier::Name(ETH_NAME.to_string()),
2749 enable: Some(true),
2750 },
2751 Err(()),
2752 Err(RequestError::Unknown); "enable_fails")]
2753 #[test_case(
2754 InitialState { eth_interface_online: false },
2755 SetLinkArgs{
2756 link: LinkSpecifier::Name(ETH_NAME.to_string()),
2757 enable: Some(false),
2758 },
2759 Ok(false),
2760 Ok(()); "disable_no_op_succeeds")]
2761 #[test_case(
2762 InitialState { eth_interface_online: true },
2763 SetLinkArgs{
2764 link: LinkSpecifier::Name(ETH_NAME.to_string()),
2765 enable: Some(false),
2766 },
2767 Ok(true),
2768 Ok(()); "disable_newly_succeeds")]
2769 #[test_case(
2770 InitialState { eth_interface_online: false },
2771 SetLinkArgs{
2772 link: LinkSpecifier::Name(WLAN_NAME.to_string()),
2773 enable: Some(false),
2774 },
2775 Ok(true),
2776 Err(RequestError::UnrecognizedInterface); "disable_not_found")]
2777 #[test_case(
2778 InitialState { eth_interface_online: false },
2779 SetLinkArgs{
2780 link: LinkSpecifier::Name(ETH_NAME.to_string()),
2781 enable: Some(false),
2782 },
2783 Err(()),
2784 Err(RequestError::Unknown); "disable_fails")]
2785 #[fuchsia::test]
2786 async fn test_set_link(
2787 initial_state: InitialState,
2788 args: SetLinkArgs,
2789 control_response: Result<bool, ()>,
2790 expected_result: Result<(), RequestError>,
2791 ) {
2792 let SetLinkArgs { link: _, enable } = args.clone();
2793 let request = RequestArgs::Link(LinkRequestArgs::Set(args));
2794
2795 let control_response_clone = control_response.clone();
2796 let handle_enable =
2797 move |req: fnet_interfaces_admin::ControlRequest| -> Option<fnet_interfaces::Event> {
2798 let responder = match req {
2799 fnet_interfaces_admin::ControlRequest::Enable { responder } => responder,
2800 _ => panic!("unexpected ControlRequest received"),
2801 };
2802 match control_response {
2803 Err(()) => {
2804 responder
2805 .send(Err(fnet_interfaces_admin::ControlEnableError::unknown()))
2806 .expect("should send response");
2807 None
2808 }
2809 Ok(newly_enabled) => {
2810 responder.send(Ok(newly_enabled)).expect("should send response");
2811 newly_enabled.then_some(fnet_interfaces::Event::Changed(
2812 fnet_interfaces::Properties {
2813 id: Some(ETH_INTERFACE_ID),
2814 online: Some(true),
2815 ..fnet_interfaces::Properties::default()
2816 },
2817 ))
2818 }
2819 }
2820 };
2821 let handle_disable =
2822 move |req: fnet_interfaces_admin::ControlRequest| -> Option<fnet_interfaces::Event> {
2823 let responder = match req {
2824 fnet_interfaces_admin::ControlRequest::Disable { responder } => responder,
2825 _ => panic!("unexpected ControlRequest received"),
2826 };
2827 match control_response_clone {
2828 Err(()) => {
2829 responder
2830 .send(Err(fnet_interfaces_admin::ControlDisableError::unknown()))
2831 .expect("should send response");
2832 None
2833 }
2834 Ok(newly_disabled) => {
2835 responder.send(Ok(newly_disabled)).expect("should send response");
2836 newly_disabled.then_some(fnet_interfaces::Event::Changed(
2837 fnet_interfaces::Properties {
2838 id: Some(ETH_INTERFACE_ID),
2839 online: Some(false),
2840 ..fnet_interfaces::Properties::default()
2841 },
2842 ))
2843 }
2844 }
2845 };
2846
2847 let test_result = match enable {
2848 None => {
2849 test_request_with_initial_state(
2850 [request],
2851 expect_only_get_mac_root_requests,
2852 initial_state,
2853 )
2854 .await
2855 }
2856 Some(true) => {
2857 test_request_with_initial_state(
2858 [request],
2859 expect_get_admin_with_handler(handle_enable),
2860 initial_state,
2861 )
2862 .await
2863 }
2864 Some(false) => {
2865 test_request_with_initial_state(
2866 [request],
2867 expect_get_admin_with_handler(handle_disable),
2868 initial_state,
2869 )
2870 .await
2871 }
2872 };
2873
2874 assert_eq!(
2875 test_result,
2876 TestRequestResult {
2877 messages: vec![],
2880 waiter_results: vec![expected_result],
2881 },
2882 )
2883 }
2884
2885 #[test_case(Some(IpVersion::V4); "v4")]
2886 #[test_case(Some(IpVersion::V6); "v6")]
2887 #[test_case(None; "all")]
2888 #[fuchsia::test]
2889 async fn test_get_addr(ip_version_filter: Option<IpVersion>) {
2890 pretty_assertions::assert_eq!(
2891 test_request(
2892 [RequestArgs::Address(AddressRequestArgs::Get(GetAddressArgs::Dump {
2893 ip_version_filter
2894 }))],
2895 expect_only_get_mac_root_requests,
2896 )
2897 .await,
2898 TestRequestResult {
2899 messages: [(LO_INTERFACE_ID, LO_NAME), (ETH_INTERFACE_ID, ETH_NAME)]
2900 .into_iter()
2901 .map(|(id, name)| {
2902 [TEST_V4_ADDR, TEST_V6_ADDR]
2903 .into_iter()
2904 .filter(|fnet::Subnet { addr, prefix_len: _ }| {
2905 ip_version_filter.map_or(true, |ip_version| {
2906 ip_version.eq(&match addr {
2907 fnet::IpAddress::Ipv4(_) => IpVersion::V4,
2908 fnet::IpAddress::Ipv6(_) => IpVersion::V6,
2909 })
2910 })
2911 })
2912 .map(move |addr| {
2913 SentMessage::unicast(
2914 create_address_message(
2915 id.try_into().unwrap(),
2916 addr,
2917 name.to_string(),
2918 IFA_F_PERMANENT,
2919 )
2920 .to_rtnl_new_addr(TEST_SEQUENCE_NUMBER, true),
2921 )
2922 })
2923 })
2924 .flatten()
2925 .collect(),
2926 waiter_results: vec![Ok(())],
2927 },
2928 );
2929 }
2930
2931 #[test_case(
2934 test_addr_subnet_v4(),
2935 None,
2936 true; "v4_no_terminal_new")]
2937 #[test_case(
2938 test_addr_subnet_v6(),
2939 None,
2940 true; "v6_no_terminal_new")]
2941 #[test_case(
2942 test_addr_subnet_v4(),
2943 Some(InterfaceRemovedReason::PortClosed),
2944 true; "v4_port_closed_terminal_new")]
2945 #[test_case(
2946 test_addr_subnet_v6(),
2947 Some(InterfaceRemovedReason::PortClosed),
2948 true; "v6_port_closed_terminal_new")]
2949 #[test_case(
2950 test_addr_subnet_v4(),
2951 Some(InterfaceRemovedReason::User),
2952 true; "v4_user_terminal_new")]
2953 #[test_case(
2954 test_addr_subnet_v6(),
2955 Some(InterfaceRemovedReason::User),
2956 true; "v6_user_terminal_new")]
2957 #[test_case(
2958 test_addr_subnet_v4(),
2959 None,
2960 false; "v4_no_terminal_del")]
2961 #[test_case(
2962 test_addr_subnet_v6(),
2963 None,
2964 false; "v6_no_terminal_del")]
2965 #[test_case(
2966 test_addr_subnet_v4(),
2967 Some(InterfaceRemovedReason::PortClosed),
2968 false; "v4_port_closed_terminal_del")]
2969 #[test_case(
2970 test_addr_subnet_v6(),
2971 Some(InterfaceRemovedReason::PortClosed),
2972 false; "v6_port_closed_terminal_del")]
2973 #[test_case(
2974 test_addr_subnet_v4(),
2975 Some(InterfaceRemovedReason::User),
2976 false; "v4_user_terminal_del")]
2977 #[test_case(
2978 test_addr_subnet_v6(),
2979 Some(InterfaceRemovedReason::User),
2980 false; "v6_user_terminal_del")]
2981 #[fuchsia::test]
2982 async fn test_new_del_addr_interface_removed(
2983 address: AddrSubnetEither,
2984 removal_reason: Option<InterfaceRemovedReason>,
2985 is_new: bool,
2986 ) {
2987 let interface_id = NonZeroU32::new(LO_INTERFACE_ID.try_into().unwrap()).unwrap();
2988 let address_and_interface_id = AddressAndInterfaceArgs { address, interface_id };
2989 pretty_assertions::assert_eq!(
2990 test_request(
2991 [if is_new {
2992 RequestArgs::Address(AddressRequestArgs::New(NewAddressArgs {
2993 address_and_interface_id,
2994 add_subnet_route: false,
2995 }))
2996 } else {
2997 RequestArgs::Address(AddressRequestArgs::Del(DelAddressArgs {
2998 address_and_interface_id,
2999 }))
3000 }],
3001 |interfaces_request_stream| futures::stream::unfold(
3002 interfaces_request_stream,
3003 |interfaces_request_stream| async move {
3004 interfaces_request_stream
3005 .for_each(|req| {
3006 futures::future::ready(match req.unwrap() {
3007 fnet_root::InterfacesRequest::GetAdmin {
3008 id,
3009 control,
3010 control_handle: _,
3011 } => {
3012 pretty_assertions::assert_eq!(id, LO_INTERFACE_ID);
3013 let control = control.into_stream();
3014 let control = control.control_handle();
3015 if let Some(reason) = removal_reason {
3016 control.send_on_interface_removed(reason).unwrap()
3017 }
3018 control.shutdown();
3019 }
3020 req => handle_get_mac_root_request_or_panic(req),
3021 })
3022 })
3023 .await;
3024
3025 unreachable!("interfaces request stream should not end")
3026 },
3027 ),
3028 )
3029 .await,
3030 TestRequestResult {
3031 messages: Vec::new(),
3032 waiter_results: vec![Err(RequestError::UnrecognizedInterface)],
3033 },
3034 )
3035 }
3036
3037 enum AddressRequestKind {
3038 New { add_subnet_route: bool },
3039 Del,
3040 }
3041
3042 #[test_case(
3045 add_test_addr_subnet_v4(),
3046 AddressRequestKind::New { add_subnet_route: false }; "v4_new")]
3047 #[test_case(
3048 add_test_addr_subnet_v6(),
3049 AddressRequestKind::New { add_subnet_route: false }; "v6_new")]
3050 #[test_case(add_test_addr_subnet_v4(), AddressRequestKind::Del; "v4_del")]
3051 #[test_case(add_test_addr_subnet_v6(), AddressRequestKind::Del; "v6_del")]
3052 #[fuchsia::test]
3053 async fn test_unknown_interface_request(address: AddrSubnetEither, kind: AddressRequestKind) {
3054 let interface_id = NonZeroU32::new(WLAN_INTERFACE_ID.try_into().unwrap()).unwrap();
3055 let address_and_interface_id = AddressAndInterfaceArgs { address, interface_id };
3056 pretty_assertions::assert_eq!(
3057 test_request(
3058 [match kind {
3059 AddressRequestKind::New { add_subnet_route } => {
3060 RequestArgs::Address(AddressRequestArgs::New(NewAddressArgs {
3061 address_and_interface_id,
3062 add_subnet_route,
3063 }))
3064 }
3065 AddressRequestKind::Del => {
3066 RequestArgs::Address(AddressRequestArgs::Del(DelAddressArgs {
3067 address_and_interface_id,
3068 }))
3069 }
3070 }],
3071 expect_only_get_mac_root_requests,
3072 )
3073 .await,
3074 TestRequestResult {
3075 messages: Vec::new(),
3076 waiter_results: vec![Err(RequestError::UnrecognizedInterface)],
3077 },
3078 )
3079 }
3080
3081 struct TestInterfaceRequestCase<F> {
3082 address: AddrSubnetEither,
3083 kind: AddressRequestKind,
3084 control_request_handler: F,
3085 }
3086
3087 impl<F> TestInterfaceRequestCase<F> {
3088 fn into_request_args_and_handler(self, interface_id: NonZeroU32) -> (RequestArgs, F) {
3089 let Self { address, kind, control_request_handler } = self;
3090 let address_and_interface_id = AddressAndInterfaceArgs { address, interface_id };
3091 let args = match kind {
3092 AddressRequestKind::New { add_subnet_route } => {
3093 RequestArgs::Address(AddressRequestArgs::New(NewAddressArgs {
3094 address_and_interface_id,
3095 add_subnet_route,
3096 }))
3097 }
3098 AddressRequestKind::Del => {
3099 RequestArgs::Address(AddressRequestArgs::Del(DelAddressArgs {
3100 address_and_interface_id,
3101 }))
3102 }
3103 };
3104
3105 (args, control_request_handler)
3106 }
3107 }
3108
3109 async fn test_maybe_two_interface_requests_on_single_control<
3115 St1: Stream<Item = fnet_interfaces::Event>,
3116 F1: FnMut(fnet_interfaces_admin::ControlRequest) -> St1,
3117 St2: Stream<Item = fnet_interfaces::Event>,
3118 F2: FnMut(fnet_interfaces_admin::ControlRequest) -> St2,
3119 >(
3120 case1: TestInterfaceRequestCase<F1>,
3121 case2: Option<TestInterfaceRequestCase<F2>>,
3122 ) -> TestRequestResult {
3123 let interface_id = NonZeroU32::new(ETH_INTERFACE_ID.try_into().unwrap()).unwrap();
3124 let (args1, mut control_request_handler1) =
3125 case1.into_request_args_and_handler(interface_id);
3126
3127 let (args2, control_request_handler2) = if let Some(case) = case2 {
3128 let (args, control_request_handler) = case.into_request_args_and_handler(interface_id);
3129 (Some(args), Some(control_request_handler))
3130 } else {
3131 (None, None)
3132 };
3133
3134 test_request([args1].into_iter().chain(args2), |interfaces_request_stream| {
3135 interfaces_request_stream
3136 .filter_map(|req| handle_get_admin_for_eth_or_panic(req))
3137 .into_future()
3138 .map(|(admin_control_stream, _stream_of_admin_control_streams)| {
3142 admin_control_stream.unwrap()
3143 })
3144 .flatten_stream()
3145 .into_future()
3146 .map(|(admin_control_req, admin_control_stream)| {
3147 control_request_handler1(admin_control_req.unwrap().unwrap()).chain(
3148 futures::stream::iter(control_request_handler2.map(
3149 |mut control_request_handler2| {
3150 admin_control_stream
3151 .into_future()
3152 .map(move |(admin_control_req, _admin_control_stream)| {
3153 control_request_handler2(
3154 admin_control_req.unwrap().unwrap(),
3155 )
3156 })
3157 .flatten_stream()
3158 },
3159 ))
3160 .flatten(),
3161 )
3162 })
3163 .flatten_stream()
3164 })
3165 .await
3166 }
3167
3168 async fn test_interface_request<
3171 St: Stream<Item = fnet_interfaces::Event>,
3172 F: FnMut(fnet_interfaces_admin::ControlRequest) -> St,
3173 >(
3174 case: TestInterfaceRequestCase<F>,
3175 ) -> TestRequestResult {
3176 test_maybe_two_interface_requests_on_single_control(
3177 case,
3178 None::<TestInterfaceRequestCase<fn(_) -> futures::stream::Pending<_>>>,
3179 )
3180 .await
3181 }
3182
3183 async fn test_new_addr_asp_helper<
3186 St: Stream<Item = fnet_interfaces::Event>,
3187 F: Fn(fnet_interfaces_admin::AddressStateProviderRequestStream) -> St,
3188 >(
3189 address: AddrSubnetEither,
3190 add_subnet_route: bool,
3191 asp_handler: F,
3192 ) -> TestRequestResult {
3193 test_interface_request(TestInterfaceRequestCase {
3194 address,
3195 kind: AddressRequestKind::New { add_subnet_route },
3196 control_request_handler: |req| match req {
3197 fnet_interfaces_admin::ControlRequest::AddAddress {
3198 address: got_address,
3199 parameters,
3200 address_state_provider,
3201 control_handle: _,
3202 } => {
3203 pretty_assertions::assert_eq!(got_address, address.into_ext());
3204 pretty_assertions::assert_eq!(
3205 parameters,
3206 fnet_interfaces_admin::AddressParameters {
3207 add_subnet_route: Some(add_subnet_route),
3208 ..fnet_interfaces_admin::AddressParameters::default()
3209 },
3210 );
3211 asp_handler(address_state_provider.into_stream())
3212 }
3213 req => panic!("unexpected request {req:?}"),
3214 },
3215 })
3216 .await
3217 }
3218
3219 #[test_case(test_addr_subnet_v4(); "v4")]
3222 #[test_case(test_addr_subnet_v6(); "v6")]
3223 #[fuchsia::test]
3224 async fn test_new_addr_drop_asp_immediately(address: AddrSubnetEither) {
3225 pretty_assertions::assert_eq!(
3226 test_new_addr_asp_helper(address, false, |_asp_request_stream| {
3227 futures::stream::empty()
3228 })
3229 .await,
3230 TestRequestResult {
3231 messages: Vec::new(),
3232 waiter_results: vec![Err(RequestError::UnrecognizedInterface)],
3233 },
3234 )
3235 }
3236
3237 async fn test_new_addr_failed_helper(
3240 address: AddrSubnetEither,
3241 reason: AddressRemovalReason,
3242 ) -> TestRequestResult {
3243 test_new_addr_asp_helper(address, true, |asp_request_stream| {
3244 asp_request_stream.control_handle().send_on_address_removed(reason).unwrap();
3245 futures::stream::empty()
3246 })
3247 .await
3248 }
3249
3250 #[test_case(
3253 test_addr_subnet_v4(),
3254 AddressRemovalReason::DadFailed; "v4_dad_failed")]
3255 #[test_case(
3256 test_addr_subnet_v6(),
3257 AddressRemovalReason::DadFailed; "v6_dad_failed")]
3258 #[test_case(
3259 test_addr_subnet_v4(),
3260 AddressRemovalReason::InterfaceRemoved; "v4_interface_removed")]
3261 #[test_case(
3262 test_addr_subnet_v6(),
3263 AddressRemovalReason::InterfaceRemoved; "v6_interface_removed")]
3264 #[test_case(
3265 test_addr_subnet_v4(),
3266 AddressRemovalReason::UserRemoved; "v4_user_removed")]
3267 #[test_case(
3268 test_addr_subnet_v6(),
3269 AddressRemovalReason::UserRemoved; "v6_user_removed")]
3270 #[should_panic(expected = "expected netstack to send initial state before removing")]
3271 #[fuchsia::test]
3272 async fn test_new_addr_failed_unexpected_reason(
3273 address: AddrSubnetEither,
3274 reason: AddressRemovalReason,
3275 ) {
3276 let _: TestRequestResult = test_new_addr_failed_helper(address, reason).await;
3277 }
3278
3279 #[test_case(
3281 test_addr_subnet_v4(),
3282 AddressRemovalReason::Invalid,
3283 RequestError::InvalidRequest; "v4_invalid")]
3284 #[test_case(
3285 test_addr_subnet_v6(),
3286 AddressRemovalReason::Invalid,
3287 RequestError::InvalidRequest; "v6_invalid")]
3288 #[test_case(
3289 test_addr_subnet_v4(),
3290 AddressRemovalReason::AlreadyAssigned,
3291 RequestError::AlreadyExists; "v4_exists")]
3292 #[test_case(
3293 test_addr_subnet_v6(),
3294 AddressRemovalReason::AlreadyAssigned,
3295 RequestError::AlreadyExists; "v6_exists")]
3296 #[fuchsia::test]
3297 async fn test_new_addr_failed(
3298 address: AddrSubnetEither,
3299 reason: AddressRemovalReason,
3300 expected_error: RequestError,
3301 ) {
3302 pretty_assertions::assert_eq!(
3303 test_new_addr_failed_helper(address, reason).await,
3304 TestRequestResult { messages: Vec::new(), waiter_results: vec![Err(expected_error)] },
3305 )
3306 }
3307
3308 async fn test_new_addr_asp_detach_handled_helper<
3311 St: Stream<Item = fnet_interfaces::Event>,
3312 F: Fn(fnet_interfaces_admin::AddressStateProviderRequestStream) -> St,
3313 >(
3314 address: AddrSubnetEither,
3315 add_subnet_route: bool,
3316 asp_handler: F,
3317 ) -> TestRequestResult {
3318 test_new_addr_asp_helper(address, add_subnet_route, |asp_request_stream| {
3319 asp_request_stream
3320 .into_future()
3321 .map(|(asp_request, asp_request_stream)| {
3322 let _: fnet_interfaces_admin::AddressStateProviderControlHandle = asp_request
3323 .expect("eventloop uses ASP before dropping")
3324 .expect("unexpected error while waiting for Detach request")
3325 .into_detach()
3326 .expect("eventloop makes detach request immediately");
3327
3328 asp_handler(asp_request_stream)
3329 })
3330 .flatten_stream()
3331 })
3332 .await
3333 }
3334
3335 #[test_case(test_addr_subnet_v4(); "v4")]
3338 #[test_case(test_addr_subnet_v6(); "v6")]
3339 #[fuchsia::test]
3340 async fn test_new_addr_drop_asp_after_detach(address: AddrSubnetEither) {
3341 pretty_assertions::assert_eq!(
3342 test_new_addr_asp_detach_handled_helper(address, false, |_asp_stream| {
3343 futures::stream::empty()
3344 })
3345 .await,
3346 TestRequestResult {
3347 messages: Vec::new(),
3348 waiter_results: vec![Err(RequestError::UnrecognizedInterface)],
3349 },
3350 )
3351 }
3352
3353 #[test_case(add_test_addr_subnet_v4(); "v4")]
3355 #[test_case(add_test_addr_subnet_v6(); "v6")]
3356 #[fuchsia::test]
3357 async fn test_new_addr_with_address_added_event(address: AddrSubnetEither) {
3358 pretty_assertions::assert_eq!(
3359 test_new_addr_asp_detach_handled_helper(address, true, |asp_request_stream| {
3360 asp_request_stream
3361 .control_handle()
3362 .send_on_address_added()
3363 .expect("send address added");
3364
3365 futures::stream::iter([fnet_interfaces::Event::Changed(
3368 fnet_interfaces::Properties {
3369 id: Some(ETH_INTERFACE_ID.try_into().unwrap()),
3370 addresses: Some(vec![test_addr(address.into_ext())]),
3371 ..fnet_interfaces::Properties::default()
3372 },
3373 )])
3374 })
3375 .await,
3376 TestRequestResult { messages: Vec::new(), waiter_results: vec![Ok(())] },
3377 )
3378 }
3379
3380 #[test_case(
3382 test_addr_subnet_v4(),
3383 InterfaceRemovedReason::DuplicateName; "v4_duplicate_name")]
3384 #[test_case(
3385 test_addr_subnet_v6(),
3386 InterfaceRemovedReason::DuplicateName; "v6_duplicate_name")]
3387 #[test_case(
3388 test_addr_subnet_v4(),
3389 InterfaceRemovedReason::PortAlreadyBound; "v4_port_already_bound")]
3390 #[test_case(
3391 test_addr_subnet_v6(),
3392 InterfaceRemovedReason::PortAlreadyBound; "v6_port_already_bound")]
3393 #[test_case(
3394 test_addr_subnet_v4(),
3395 InterfaceRemovedReason::BadPort; "v4_bad_port")]
3396 #[test_case(
3397 test_addr_subnet_v6(),
3398 InterfaceRemovedReason::BadPort; "v6_bad_port")]
3399 #[should_panic(expected = "unexpected interface removed reason")]
3400 #[fuchsia::test]
3401 async fn test_del_addr_interface_closed_unexpected_reason(
3402 address: AddrSubnetEither,
3403 removal_reason: InterfaceRemovedReason,
3404 ) {
3405 let _: TestRequestResult = test_interface_request(TestInterfaceRequestCase {
3406 address,
3407 kind: AddressRequestKind::Del,
3408 control_request_handler: |req| match req {
3409 fnet_interfaces_admin::ControlRequest::RemoveAddress {
3410 address: got_address,
3411 responder,
3412 } => {
3413 pretty_assertions::assert_eq!(got_address, address.into_ext());
3414 let control_handle = responder.control_handle();
3415 control_handle.send_on_interface_removed(removal_reason).unwrap();
3416 control_handle.shutdown();
3417 futures::stream::empty()
3418 }
3419 req => panic!("unexpected request {req:?}"),
3420 },
3421 })
3422 .await;
3423 }
3424
3425 fn del_addr_test_interface_case(
3426 address: AddrSubnetEither,
3427 response: Result<bool, fnet_interfaces_admin::ControlRemoveAddressError>,
3428 remaining_address: Option<AddrSubnetEither>,
3429 ) -> TestInterfaceRequestCase<
3430 impl FnMut(
3431 fnet_interfaces_admin::ControlRequest,
3432 ) -> futures::stream::Iter<core::array::IntoIter<fnet_interfaces::Event, 1>>,
3433 > {
3434 TestInterfaceRequestCase {
3435 address,
3436 kind: AddressRequestKind::Del,
3437 control_request_handler: move |req| {
3438 match req {
3439 fnet_interfaces_admin::ControlRequest::RemoveAddress {
3440 address: got_address,
3441 responder,
3442 } => {
3443 pretty_assertions::assert_eq!(got_address, address.into_ext());
3444 responder.send(response).unwrap();
3445
3446 futures::stream::iter([fnet_interfaces::Event::Changed(
3449 fnet_interfaces::Properties {
3450 id: Some(ETH_INTERFACE_ID.try_into().unwrap()),
3451 addresses: Some(remaining_address.map_or_else(Vec::new, |addr| {
3452 vec![test_addr(addr.into_ext())]
3453 })),
3454 ..fnet_interfaces::Properties::default()
3455 },
3456 )])
3457 }
3458 req => panic!("unexpected request {req:?}"),
3459 }
3460 },
3461 }
3462 }
3463
3464 #[test_case(
3466 test_addr_subnet_v4(),
3467 Ok(true),
3468 Ok(()); "v4_did_remove")]
3469 #[test_case(
3470 test_addr_subnet_v6(),
3471 Ok(true),
3472 Ok(()); "v6_did_remove")]
3473 #[test_case(
3474 test_addr_subnet_v4(),
3475 Ok(false),
3476 Err(RequestError::AddressNotFound); "v4_did_not_remove")]
3477 #[test_case(
3478 test_addr_subnet_v6(),
3479 Ok(false),
3480 Err(RequestError::AddressNotFound); "v6_did_not_remove")]
3481 #[test_case(
3482 test_addr_subnet_v4(),
3483 Err(fnet_interfaces_admin::ControlRemoveAddressError::unknown()),
3484 Err(RequestError::InvalidRequest); "v4_unrecognized_error")]
3485 #[test_case(
3486 test_addr_subnet_v6(),
3487 Err(fnet_interfaces_admin::ControlRemoveAddressError::unknown()),
3488 Err(RequestError::InvalidRequest); "v6_unrecognized_error")]
3489 #[fuchsia::test]
3490 async fn test_del_addr(
3491 address: AddrSubnetEither,
3492 response: Result<bool, fnet_interfaces_admin::ControlRemoveAddressError>,
3493 waiter_result: Result<(), RequestError>,
3494 ) {
3495 pretty_assertions::assert_eq!(
3496 test_interface_request(del_addr_test_interface_case(address, response, None)).await,
3497 TestRequestResult { messages: Vec::new(), waiter_results: vec![waiter_result] },
3498 )
3499 }
3500
3501 #[fuchsia::test]
3504 async fn test_single_get_admin_for_multiple_interface_requests() {
3505 let first_address = test_addr_subnet_v4();
3506 let second_address = test_addr_subnet_v6();
3507 pretty_assertions::assert_eq!(
3508 test_maybe_two_interface_requests_on_single_control(
3509 del_addr_test_interface_case(first_address, Ok(true), Some(second_address)),
3510 Some(del_addr_test_interface_case(second_address, Ok(true), None)),
3511 )
3512 .await,
3513 TestRequestResult { messages: Vec::new(), waiter_results: vec![Ok(()), Ok(())] },
3514 )
3515 }
3516}