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