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