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