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