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