Skip to main content

netcfg/
lib.rs

1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#![deny(unused)]
6
7mod devices;
8mod dhcpv4;
9mod dhcpv6;
10mod dns;
11mod errors;
12mod filter;
13mod interface;
14mod masquerade;
15pub mod network;
16mod socketproxy;
17mod virtualization;
18
19use ::dhcpv4::protocol::FromFidlExt as _;
20use std::collections::hash_map::Entry;
21use std::collections::{HashMap, HashSet};
22use std::fmt::{self, Display};
23use std::num::NonZeroU64;
24use std::pin::{Pin, pin};
25use std::str::FromStr;
26use std::{fs, io, path};
27
28use fidl::endpoints::{ProtocolMarker, RequestStream as _, Responder as _};
29use fidl_fuchsia_net_ext::{self as fnet_ext, DisplayExt as _, IpExt as _};
30use fidl_fuchsia_net_interfaces_ext::{self as fnet_interfaces_ext, Update as _};
31use fuchsia_component::client::{clone_namespace_svc, new_protocol_connector_in_dir};
32use fuchsia_component::server::{ServiceFs, ServiceFsDir};
33use fuchsia_fs::directory as fvfs_watcher;
34
35use crate::network::PropertyUpdate;
36
37use {
38    fidl_fuchsia_io as fio, fidl_fuchsia_net as fnet, fidl_fuchsia_net_dhcp as fnet_dhcp,
39    fidl_fuchsia_net_dhcp_ext as fnet_dhcp_ext, fidl_fuchsia_net_dhcpv6 as fnet_dhcpv6,
40    fidl_fuchsia_net_filter as fnet_filter,
41    fidl_fuchsia_net_filter_deprecated as fnet_filter_deprecated,
42    fidl_fuchsia_net_interfaces as fnet_interfaces,
43    fidl_fuchsia_net_interfaces_admin as fnet_interfaces_admin,
44    fidl_fuchsia_net_masquerade as fnet_masquerade,
45    fidl_fuchsia_net_matchers_ext as fnet_matchers_ext, fidl_fuchsia_net_name as fnet_name,
46    fidl_fuchsia_net_ndp as fnet_ndp, fidl_fuchsia_net_policy_properties as fnp_properties,
47    fidl_fuchsia_net_policy_socketproxy as fnp_socketproxy,
48    fidl_fuchsia_net_resources as fnet_resources,
49    fidl_fuchsia_net_routes_admin as fnet_routes_admin,
50    fidl_fuchsia_net_routes_ext as fnet_routes_ext, fidl_fuchsia_net_stack as fnet_stack,
51    fidl_fuchsia_net_virtualization as fnet_virtualization, fuchsia_async as fasync,
52};
53
54use anyhow::{Context as _, anyhow};
55use assert_matches::assert_matches;
56use async_trait::async_trait;
57use async_utils::stream::WithTag as _;
58use dns_server_watcher::{DEFAULT_DNS_PORT, DnsServers, DnsServersUpdateSource};
59use futures::stream::BoxStream;
60use futures::{FutureExt, StreamExt as _, TryFutureExt as _, TryStreamExt as _};
61use log::{debug, error, info, trace, warn};
62use net_declare::fidl_ip_v4;
63use net_declare::net::prefix_length_v4;
64use net_types::ip::{IpAddress as _, Ipv4, Ipv6, PrefixLength};
65use serde::{Deserialize, Serialize};
66use thiserror::Error;
67
68use self::devices::DeviceInfo;
69use self::errors::{ContextExt as _, accept_error};
70use self::filter::{FilterControl, FilterEnabledState};
71use self::interface::{
72    DeviceInfoRef, InterfaceNamingIdentifier, NetstackManagedRoutesDesignation, ProvisioningAction,
73    ProvisioningType,
74};
75use self::masquerade::MasqueradeHandler;
76use self::socketproxy::SocketProxyState;
77
78/// Interface Identifier
79#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
80pub struct InterfaceId(NonZeroU64);
81
82impl InterfaceId {
83    const fn new(raw: u64) -> Option<Self> {
84        // Note: use a match here because `Option::map` is non-const.
85        match NonZeroU64::new(raw) {
86            Some(id) => Some(InterfaceId(id)),
87            None => None,
88        }
89    }
90
91    pub const fn get(self) -> u64 {
92        self.0.get()
93    }
94}
95
96impl Display for InterfaceId {
97    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98        write!(f, "{}", self.0)
99    }
100}
101
102impl fnet_interfaces_ext::TryFromMaybeNonZero for InterfaceId {
103    fn try_from(value: u64) -> Result<Self, fnet_interfaces_ext::ZeroError> {
104        Self::new(value).ok_or(fnet_interfaces_ext::ZeroError {})
105    }
106}
107
108impl TryFrom<u64> for InterfaceId {
109    type Error = std::num::TryFromIntError;
110    fn try_from(val: u64) -> Result<Self, Self::Error> {
111        Ok(Self(NonZeroU64::try_from(val)?))
112    }
113}
114
115impl TryFrom<u32> for InterfaceId {
116    type Error = std::num::TryFromIntError;
117    fn try_from(val: u32) -> Result<Self, Self::Error> {
118        Ok(Self(NonZeroU64::try_from(val as u64)?))
119    }
120}
121
122impl From<NonZeroU64> for InterfaceId {
123    fn from(val: NonZeroU64) -> Self {
124        Self(val)
125    }
126}
127
128impl From<InterfaceId> for NonZeroU64 {
129    fn from(InterfaceId(val): InterfaceId) -> Self {
130        val
131    }
132}
133
134impl From<InterfaceId> for u64 {
135    fn from(val: InterfaceId) -> Self {
136        val.get()
137    }
138}
139
140/// Interface metrics.
141///
142/// Interface metrics are used to sort the route table. An interface with a
143/// lower metric is favored over one with a higher metric.
144#[derive(Clone, Copy, Debug, Deserialize, PartialEq)]
145pub struct Metric(u32);
146
147impl Default for Metric {
148    // A default value of 600 is chosen for Metric: this provides plenty of space for routing
149    // policy on devices with many interfaces (physical or logical) while remaining in the same
150    // magnitude as our current default ethernet metric.
151    fn default() -> Self {
152        Self(600)
153    }
154}
155
156impl std::fmt::Display for Metric {
157    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
158        let Metric(u) = self;
159        write!(f, "{}", u)
160    }
161}
162
163impl From<Metric> for u32 {
164    fn from(Metric(u): Metric) -> u32 {
165        u
166    }
167}
168
169/// A node that represents the directory it is in.
170///
171/// `/dir` and `/dir/.` point to the same directory.
172const THIS_DIRECTORY: &str = ".";
173
174/// The prefix length for the address assigned to a WLAN AP interface.
175const WLAN_AP_PREFIX_LEN: PrefixLength<Ipv4> = prefix_length_v4!(29);
176
177/// The address for the network the WLAN AP interface is a part of.
178const WLAN_AP_NETWORK_ADDR: fnet::Ipv4Address = fidl_ip_v4!("192.168.255.248");
179
180/// The lease time for a DHCP lease.
181///
182/// 1 day in seconds.
183const WLAN_AP_DHCP_LEASE_TIME_SECONDS: u32 = 24 * 60 * 60;
184
185/// A map of DNS server watcher streams that yields `DnsServerWatcherEvent` as DNS
186/// server updates become available.
187///
188/// DNS server watcher streams may be added or removed at runtime as the watchers
189/// are started or stopped.
190type DnsServerWatchers<'a> = async_utils::stream::StreamMap<
191    DnsServersUpdateSource,
192    BoxStream<'a, (DnsServersUpdateSource, Result<Vec<fnet_name::DnsServer_>, fidl::Error>)>,
193>;
194
195type DelegatedNetworksStream = futures::future::Either<
196    fnp_socketproxy::NetworkRegistryRequestStream,
197    futures::stream::Empty<Result<fnp_socketproxy::NetworkRegistryRequest, fidl::Error>>,
198>;
199
200/// Defines log levels.
201#[derive(Debug, Copy, Clone)]
202pub struct LogLevel(diagnostics_log::Severity);
203
204impl Default for LogLevel {
205    fn default() -> Self {
206        Self(diagnostics_log::Severity::Info)
207    }
208}
209
210impl FromStr for LogLevel {
211    type Err = anyhow::Error;
212
213    fn from_str(s: &str) -> Result<Self, anyhow::Error> {
214        match s.to_uppercase().as_str() {
215            "TRACE" => Ok(Self(diagnostics_log::Severity::Trace)),
216            "DEBUG" => Ok(Self(diagnostics_log::Severity::Debug)),
217            "INFO" => Ok(Self(diagnostics_log::Severity::Info)),
218            "WARN" => Ok(Self(diagnostics_log::Severity::Warn)),
219            "ERROR" => Ok(Self(diagnostics_log::Severity::Error)),
220            "FATAL" => Ok(Self(diagnostics_log::Severity::Fatal)),
221            _ => Err(anyhow::anyhow!("unrecognized log level = {}", s)),
222        }
223    }
224}
225
226/// Network Configuration tool.
227///
228/// Configures network components in response to events.
229#[derive(argh::FromArgs, Debug)]
230struct Opt {
231    /// minimum severity for logs
232    #[argh(option, default = "Default::default()")]
233    min_severity: LogLevel,
234
235    /// config file to use
236    #[argh(option, default = "\"/netcfg-config/netcfg_default.json5\".to_string()")]
237    config_data: String,
238}
239
240#[derive(Debug, Deserialize)]
241#[serde(deny_unknown_fields)]
242pub struct DnsConfig {
243    pub servers: Vec<std::net::IpAddr>,
244}
245
246#[derive(Debug, Deserialize)]
247#[serde(deny_unknown_fields)]
248pub struct FilterConfig {
249    pub rules: Vec<String>,
250    pub nat_rules: Vec<String>,
251    pub rdr_rules: Vec<String>,
252}
253
254#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, Serialize, Deserialize)]
255#[serde(deny_unknown_fields, rename_all = "lowercase")]
256pub enum InterfaceType {
257    Ethernet,
258    // NB: Serde alias provides backwards compatibility.
259    #[serde(alias = "wlan")]
260    WlanClient,
261    // NB: Serde alias provides backwards compatibility.
262    #[serde(alias = "ap")]
263    WlanAp,
264    Blackhole,
265}
266
267impl TryFrom<fidl_fuchsia_hardware_network::PortClass> for InterfaceType {
268    type Error = UnknownPortClassError;
269    fn try_from(port_class: fidl_fuchsia_hardware_network::PortClass) -> Result<Self, Self::Error> {
270        let device_class = DeviceClass::try_from(port_class)?;
271        Ok(device_class.into())
272    }
273}
274
275impl From<DeviceClass> for InterfaceType {
276    fn from(device_class: DeviceClass) -> Self {
277        match device_class {
278            DeviceClass::WlanClient => InterfaceType::WlanClient,
279            DeviceClass::Blackhole => InterfaceType::Blackhole,
280            DeviceClass::WlanAp => InterfaceType::WlanAp,
281            DeviceClass::Ethernet
282            | DeviceClass::Virtual
283            | DeviceClass::Ppp
284            | DeviceClass::Bridge => InterfaceType::Ethernet,
285            // TODO(https://fxbug.dev/349810498): Add a first party
286            // `InterfaceType` variant for lowpan interfaces.
287            DeviceClass::Lowpan => InterfaceType::Ethernet,
288        }
289    }
290}
291#[cfg_attr(test, derive(PartialEq))]
292#[derive(Debug, Default, Deserialize)]
293#[serde(deny_unknown_fields)]
294pub struct InterfaceMetrics {
295    #[serde(default)]
296    pub wlan_metric: Metric,
297    #[serde(default)]
298    pub eth_metric: Metric,
299    #[serde(default)]
300    pub blackhole_metric: Metric,
301}
302
303#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Deserialize)]
304#[serde(deny_unknown_fields, rename_all = "lowercase")]
305pub enum DeviceClass {
306    Virtual,
307    Ethernet,
308    // NB: Serde alias provides backwards compatibility.
309    #[serde(alias = "wlan")]
310    WlanClient,
311    Ppp,
312    Bridge,
313    WlanAp,
314    Lowpan,
315    Blackhole,
316}
317
318#[derive(Debug, Error)]
319#[error("unknown port class with ordinal: {unknown_ordinal}")]
320pub struct UnknownPortClassError {
321    unknown_ordinal: u16,
322}
323
324impl TryFrom<fidl_fuchsia_hardware_network::PortClass> for DeviceClass {
325    type Error = UnknownPortClassError;
326    fn try_from(port_class: fidl_fuchsia_hardware_network::PortClass) -> Result<Self, Self::Error> {
327        match port_class {
328            fidl_fuchsia_hardware_network::PortClass::Virtual => Ok(DeviceClass::Virtual),
329            fidl_fuchsia_hardware_network::PortClass::Ethernet => Ok(DeviceClass::Ethernet),
330            fidl_fuchsia_hardware_network::PortClass::WlanClient => Ok(DeviceClass::WlanClient),
331            fidl_fuchsia_hardware_network::PortClass::Ppp => Ok(DeviceClass::Ppp),
332            fidl_fuchsia_hardware_network::PortClass::Bridge => Ok(DeviceClass::Bridge),
333            fidl_fuchsia_hardware_network::PortClass::WlanAp => Ok(DeviceClass::WlanAp),
334            fidl_fuchsia_hardware_network::PortClass::Lowpan => Ok(DeviceClass::Lowpan),
335            fidl_fuchsia_hardware_network::PortClass::__SourceBreaking { unknown_ordinal } => {
336                Err(UnknownPortClassError { unknown_ordinal })
337            }
338        }
339    }
340}
341
342#[derive(Debug, PartialEq, Deserialize)]
343#[serde(transparent)]
344struct AllowedDeviceClasses(HashSet<DeviceClass>);
345
346impl Default for AllowedDeviceClasses {
347    fn default() -> Self {
348        // When new variants are added, this exhaustive match will cause a compilation failure as a
349        // reminder to add the new variant to the default array.
350        match DeviceClass::Virtual {
351            DeviceClass::Virtual
352            | DeviceClass::Ethernet
353            | DeviceClass::WlanClient
354            | DeviceClass::Ppp
355            | DeviceClass::Bridge
356            | DeviceClass::WlanAp
357            | DeviceClass::Lowpan
358            | DeviceClass::Blackhole => {}
359        }
360        Self(HashSet::from([
361            DeviceClass::Virtual,
362            DeviceClass::Ethernet,
363            DeviceClass::WlanClient,
364            DeviceClass::Ppp,
365            DeviceClass::Bridge,
366            DeviceClass::WlanAp,
367            DeviceClass::Lowpan,
368            DeviceClass::Blackhole,
369        ]))
370    }
371}
372
373// TODO(https://github.com/serde-rs/serde/issues/368): use an inline literal for the default value
374// rather than defining a one-off function.
375fn dhcpv6_enabled_default() -> bool {
376    true
377}
378
379#[derive(Debug, Default, Deserialize, PartialEq)]
380#[serde(deny_unknown_fields, rename_all = "lowercase")]
381struct ForwardedDeviceClasses {
382    #[serde(default)]
383    pub ipv4: HashSet<DeviceClass>,
384    #[serde(default)]
385    pub ipv6: HashSet<DeviceClass>,
386}
387
388#[derive(Debug, Deserialize)]
389#[serde(deny_unknown_fields)]
390struct Config {
391    pub dns_config: DnsConfig,
392    pub filter_config: FilterConfig,
393    pub filter_enabled_interface_types: HashSet<InterfaceType>,
394    // NB: `InterfaceMetrics` custom default behavior sets all included
395    // interface types to the same metric. It is encouraged to set at least one
396    // metric for predictable packet routing on a multinetworked system.
397    #[serde(default)]
398    pub interface_metrics: InterfaceMetrics,
399    // NB: `AllowedDeviceClasses` custom default behavior is permissive. To
400    // permit a subset of device classes, specify this in configuration.
401    #[serde(default)]
402    pub allowed_upstream_device_classes: AllowedDeviceClasses,
403    // NB: See above.
404    #[serde(default)]
405    pub allowed_bridge_upstream_device_classes: AllowedDeviceClasses,
406    // TODO(https://fxbug.dev/42173732): default to false.
407    #[serde(default = "dhcpv6_enabled_default")]
408    pub enable_dhcpv6: bool,
409    // NB: `ForwardedDeviceClasses` custom default behavior is restrictive. To
410    // permit a subset of device classes, specify this in configuration.
411    #[serde(default)]
412    pub forwarded_device_classes: ForwardedDeviceClasses,
413    #[serde(default)]
414    pub interface_naming_policy: Vec<interface::NamingRule>,
415    #[serde(default)]
416    pub interface_provisioning_policy: Vec<interface::ProvisioningRule>,
417    /// The names of blackhole interfaces to install.
418    ///
419    /// The interfaces are named exactly as specified in the configuration, and are not under
420    /// the influence of `interface_naming_policy`.
421    #[serde(default)]
422    pub blackhole_interfaces: Vec<String>,
423    // Whether to use protocols provided by the socketproxy component.
424    #[serde(default)]
425    pub enable_socket_proxy: bool,
426}
427
428impl Config {
429    pub fn load<P: AsRef<path::Path>>(path: P) -> Result<Self, anyhow::Error> {
430        let path = path.as_ref();
431        let file = fs::File::open(path)
432            .with_context(|| format!("could not open the config file {}", path.display()))?;
433        let config = serde_json5::from_reader(io::BufReader::new(file))
434            .with_context(|| format!("could not deserialize the config file {}", path.display()))?;
435        Ok(config)
436    }
437}
438
439#[derive(Clone, Debug)]
440struct InterfaceConfig {
441    name: String,
442    metric: u32,
443    netstack_managed_routes_designation: Option<NetstackManagedRoutesDesignation>,
444}
445
446#[derive(Debug)]
447struct InterfaceState {
448    // Hold on to control to enforce interface ownership, even if unused.
449    control: fidl_fuchsia_net_interfaces_ext::admin::Control,
450    device_class: DeviceClass,
451    config: InterfaceConfigState,
452    provisioning: interface::ProvisioningType,
453}
454
455/// State for an interface.
456#[derive(Debug)]
457enum InterfaceConfigState {
458    Host(HostInterfaceState),
459    WlanAp(WlanApInterfaceState),
460    Blackhole(BlackholeInterfaceState),
461}
462
463#[derive(Debug)]
464enum Dhcpv4ClientState {
465    NotRunning,
466    Running(dhcpv4::ClientState),
467    ScheduledRestart(Pin<Box<fasync::Timer>>),
468}
469
470#[derive(Debug)]
471struct HostInterfaceState {
472    dhcpv4_client: Dhcpv4ClientState,
473    dhcpv6_client_state: Option<dhcpv6::ClientState>,
474    // The PD configuration to use for the DHCPv6 client on this interface.
475    dhcpv6_pd_config: Option<fnet_dhcpv6::PrefixDelegationConfig>,
476    interface_admin_auth: fnet_resources::GrantForInterfaceAuthorization,
477    interface_naming_id: interface::InterfaceNamingIdentifier,
478}
479
480#[derive(Debug)]
481struct WlanApInterfaceState {
482    interface_naming_id: interface::InterfaceNamingIdentifier,
483}
484
485#[derive(Debug)]
486struct BlackholeInterfaceState;
487
488impl InterfaceState {
489    async fn new_host(
490        interface_naming_id: interface::InterfaceNamingIdentifier,
491        control: fidl_fuchsia_net_interfaces_ext::admin::Control,
492        device_class: DeviceClass,
493        dhcpv6_pd_config: Option<fnet_dhcpv6::PrefixDelegationConfig>,
494        provisioning: interface::ProvisioningType,
495    ) -> Result<Self, errors::Error> {
496        let interface_admin_auth =
497            control.get_authorization_for_interface().await.map_err(|e| {
498                errors::Error::NonFatal(anyhow::anyhow!(
499                    "error getting authorization for interface: {}",
500                    e
501                ))
502            })?;
503        Ok(Self {
504            control,
505            config: InterfaceConfigState::Host(HostInterfaceState {
506                dhcpv4_client: Dhcpv4ClientState::NotRunning,
507                dhcpv6_client_state: None,
508                dhcpv6_pd_config,
509                interface_admin_auth,
510                interface_naming_id,
511            }),
512            device_class,
513            provisioning,
514        })
515    }
516
517    fn new_wlan_ap(
518        interface_naming_id: interface::InterfaceNamingIdentifier,
519        control: fidl_fuchsia_net_interfaces_ext::admin::Control,
520        device_class: DeviceClass,
521        provisioning: interface::ProvisioningType,
522    ) -> Self {
523        Self {
524            control,
525            device_class,
526            config: InterfaceConfigState::WlanAp(WlanApInterfaceState { interface_naming_id }),
527            provisioning,
528        }
529    }
530
531    fn new_blackhole(
532        control: fidl_fuchsia_net_interfaces_ext::admin::Control,
533        provisioning: interface::ProvisioningType,
534    ) -> Self {
535        Self {
536            control,
537            device_class: DeviceClass::Blackhole,
538            config: InterfaceConfigState::Blackhole(BlackholeInterfaceState),
539            provisioning,
540        }
541    }
542
543    fn is_wlan_ap(&self) -> bool {
544        let Self { config, .. } = self;
545        match config {
546            InterfaceConfigState::Host(_) | InterfaceConfigState::Blackhole(_) => false,
547            InterfaceConfigState::WlanAp(_) => true,
548        }
549    }
550
551    fn interface_naming_id(&self) -> Option<&InterfaceNamingIdentifier> {
552        match &self.config {
553            InterfaceConfigState::Host(HostInterfaceState { interface_naming_id, .. })
554            | InterfaceConfigState::WlanAp(WlanApInterfaceState { interface_naming_id, .. }) => {
555                Some(interface_naming_id)
556            }
557            InterfaceConfigState::Blackhole(_) => None,
558        }
559    }
560
561    /// Handles the interface being discovered.
562    async fn on_discovery(
563        &mut self,
564        properties: &fnet_interfaces_ext::Properties<fnet_interfaces_ext::DefaultInterest>,
565        dhcpv4_client_provider: Option<&fnet_dhcp::ClientProviderProxy>,
566        dhcpv6_client_provider: Option<&fnet_dhcpv6::ClientProviderProxy>,
567        dhcpv4_server: Option<&fnet_dhcp::Server_Proxy>,
568        route_set_provider: &fnet_routes_admin::RouteTableV4Proxy,
569        socket_proxy_state: &mut Option<SocketProxyState>,
570        watchers: &mut DnsServerWatchers<'_>,
571        dhcpv4_configuration_streams: &mut dhcpv4::ConfigurationStreamMap,
572        dhcpv6_prefixes_streams: &mut dhcpv6::PrefixesStreamMap,
573    ) -> Result<(), errors::Error> {
574        let Self { config, provisioning, .. } = self;
575        let fnet_interfaces_ext::Properties {
576            online,
577            has_default_ipv4_route,
578            has_default_ipv6_route,
579            ..
580        } = properties;
581
582        // Netcfg won't handle interface update results for a delegated
583        // interface.
584        debug_assert!(provisioning == &interface::ProvisioningType::Local);
585
586        // Note: No discovery actions are needed for offline interfaces.
587        if !online {
588            return Ok(());
589        }
590
591        // When the socketproxy is enabled, communicate the presence of the
592        // new network to the socketproxy.
593        if let Some(state) = socket_proxy_state {
594            // TODO(https://fxbug.dev/390709467): Involve Reachability state when evaluating whether
595            // to add discovered interfaces with Internet to the socketproxy.
596            //
597            // Verify that the interface has a v4 or v6 default route, as this
598            // is a signal that there might be a higher layer of connectivity.
599            if *has_default_ipv4_route || *has_default_ipv6_route {
600                state.handle_interface_new_candidate(properties).await;
601            }
602        }
603
604        match config {
605            InterfaceConfigState::Host(HostInterfaceState {
606                dhcpv4_client,
607                dhcpv6_client_state,
608                dhcpv6_pd_config,
609                interface_admin_auth,
610                interface_naming_id,
611            }) => {
612                let interface_id = properties.id.try_into().expect("should be nonzero");
613                NetCfg::handle_dhcpv4_client_start(
614                    interface_id,
615                    &properties.name,
616                    dhcpv4_client,
617                    dhcpv4_client_provider,
618                    route_set_provider,
619                    interface_admin_auth,
620                    dhcpv4_configuration_streams,
621                )
622                .await?;
623
624                if let Some(dhcpv6_client_provider) = dhcpv6_client_provider {
625                    let sockaddr = start_dhcpv6_client(
626                        properties,
627                        dhcpv6::duid(interface_naming_id.mac),
628                        dhcpv6_client_provider,
629                        dhcpv6_pd_config.clone(),
630                        watchers,
631                        dhcpv6_prefixes_streams,
632                    )?;
633                    *dhcpv6_client_state = sockaddr.map(dhcpv6::ClientState::new);
634                }
635            }
636            InterfaceConfigState::WlanAp(WlanApInterfaceState { interface_naming_id: _ }) => {
637                if let Some(dhcpv4_server) = dhcpv4_server {
638                    dhcpv4::start_server(dhcpv4_server)
639                        .await
640                        .context("error starting DHCP server")?
641                }
642            }
643            InterfaceConfigState::Blackhole(BlackholeInterfaceState) => {}
644        }
645
646        Ok(())
647    }
648}
649
650/// Network Configuration state.
651pub struct NetCfg<'a> {
652    stack: fnet_stack::StackProxy,
653    lookup_admin: fnet_name::LookupAdminProxy,
654    filter_control: FilterControl,
655    interface_state: fnet_interfaces::StateProxy,
656    installer: fidl_fuchsia_net_interfaces_admin::InstallerProxy,
657    dhcp_server: Option<fnet_dhcp::Server_Proxy>,
658    dhcpv4_client_provider: Option<fnet_dhcp::ClientProviderProxy>,
659    dhcpv6_client_provider: Option<fnet_dhcpv6::ClientProviderProxy>,
660    route_set_v4_provider: fnet_routes_admin::RouteTableV4Proxy,
661
662    socket_proxy_state: Option<SocketProxyState>,
663    locally_provisioned_network_rule_set:
664        Option<(fnet_routes_admin::RuleSetV4Proxy, fnet_routes_admin::RuleSetV6Proxy)>,
665
666    filter_enabled_state: FilterEnabledState,
667
668    // TODO(https://fxbug.dev/42146318): These hashmaps are all indexed by
669    // interface ID and store per-interface state, and should be merged.
670    interface_states: HashMap<InterfaceId, InterfaceState>,
671    interface_properties: HashMap<
672        InterfaceId,
673        fnet_interfaces_ext::PropertiesAndState<(), fnet_interfaces_ext::DefaultInterest>,
674    >,
675    interface_metrics: InterfaceMetrics,
676
677    dns_servers: DnsServers,
678    dns_server_watch_responders: dns::DnsServerWatchResponders,
679    route_advertisement_watcher_provider:
680        Option<fnet_ndp::RouterAdvertisementOptionWatcherProviderProxy>,
681
682    forwarded_device_classes: ForwardedDeviceClasses,
683
684    allowed_upstream_device_classes: &'a HashSet<DeviceClass>,
685
686    dhcpv4_configuration_streams: dhcpv4::ConfigurationStreamMap,
687
688    dhcpv6_prefix_provider_handler: Option<dhcpv6::PrefixProviderHandler>,
689    dhcpv6_prefixes_streams: dhcpv6::PrefixesStreamMap,
690
691    // Policy configuration to determine the name of an interface.
692    interface_naming_config: interface::InterfaceNamingConfig,
693    // Policy configuration to determine whether to provision an interface.
694    interface_provisioning_policy: Vec<interface::ProvisioningRule>,
695
696    // NetworkProperty Watchers
697    netpol_networks_service: network::NetpolNetworksService,
698
699    enable_socket_proxy: bool,
700}
701
702/// Returns a [`fnet_name::DnsServer_`] with a static source from a [`std::net::IpAddr`].
703fn static_source_from_ip(f: std::net::IpAddr) -> fnet_name::DnsServer_ {
704    let socket_addr = match fnet_ext::IpAddress(f).into() {
705        fnet::IpAddress::Ipv4(addr) => fnet::SocketAddress::Ipv4(fnet::Ipv4SocketAddress {
706            address: addr,
707            port: DEFAULT_DNS_PORT,
708        }),
709        fnet::IpAddress::Ipv6(addr) => fnet::SocketAddress::Ipv6(fnet::Ipv6SocketAddress {
710            address: addr,
711            port: DEFAULT_DNS_PORT,
712            zone_index: 0,
713        }),
714    };
715
716    fnet_name::DnsServer_ {
717        address: Some(socket_addr),
718        source: Some(fnet_name::DnsServerSource::StaticSource(
719            fnet_name::StaticDnsServerSource::default(),
720        )),
721        ..Default::default()
722    }
723}
724
725/// Connect to a service, returning an error if the service does not exist in
726/// the service directory.
727async fn svc_connect<S: fidl::endpoints::DiscoverableProtocolMarker>(
728    svc_dir: &fio::DirectoryProxy,
729) -> Result<S::Proxy, anyhow::Error> {
730    optional_svc_connect::<S>(svc_dir)
731        .await?
732        .ok_or_else(|| anyhow::anyhow!("service does not exist"))
733}
734
735/// Attempt to connect to a service, returning `None` if the service does not
736/// exist in the service directory.
737async fn optional_svc_connect<S: fidl::endpoints::DiscoverableProtocolMarker>(
738    svc_dir: &fio::DirectoryProxy,
739) -> Result<Option<S::Proxy>, anyhow::Error> {
740    let req = new_protocol_connector_in_dir::<S>(&svc_dir);
741    if !req.exists().await.context("error checking for service existence")? {
742        Ok(None)
743    } else {
744        req.connect().context("error connecting to service").map(Some)
745    }
746}
747
748/// Start a DHCPv6 client if there is a unicast link-local IPv6 address in `addresses` to use as
749/// the address.
750fn start_dhcpv6_client(
751    fnet_interfaces_ext::Properties {
752        id,
753        online,
754        name,
755        addresses,
756        port_class: _,
757        has_default_ipv4_route: _,
758        has_default_ipv6_route: _,
759        port_identity_koid: _,
760    }: &fnet_interfaces_ext::Properties<fnet_interfaces_ext::DefaultInterest>,
761    duid: fnet_dhcpv6::Duid,
762    dhcpv6_client_provider: &fnet_dhcpv6::ClientProviderProxy,
763    pd_config: Option<fnet_dhcpv6::PrefixDelegationConfig>,
764    watchers: &mut DnsServerWatchers<'_>,
765    dhcpv6_prefixes_streams: &mut dhcpv6::PrefixesStreamMap,
766) -> Result<Option<fnet::Ipv6SocketAddress>, errors::Error> {
767    let id = InterfaceId::from(*id);
768    if !online {
769        return Ok(None);
770    }
771
772    let sockaddr = if let Some(sockaddr) = addresses.iter().find_map(
773        |&fnet_interfaces_ext::Address {
774             addr: fnet::Subnet { addr, prefix_len: _ },
775             valid_until: _,
776             preferred_lifetime_info: _,
777             assignment_state,
778         }| {
779            assert_eq!(assignment_state, fnet_interfaces::AddressAssignmentState::Assigned);
780            match addr {
781                fnet::IpAddress::Ipv6(address) => {
782                    if address.is_unicast_link_local() {
783                        Some(fnet::Ipv6SocketAddress {
784                            address,
785                            port: fnet_dhcpv6::DEFAULT_CLIENT_PORT,
786                            zone_index: id.get(),
787                        })
788                    } else {
789                        None
790                    }
791                }
792                fnet::IpAddress::Ipv4(_) => None,
793            }
794        },
795    ) {
796        sockaddr
797    } else {
798        return Ok(None);
799    };
800
801    if matches!(pd_config, Some(fnet_dhcpv6::PrefixDelegationConfig::Prefix(_))) {
802        // We debug-log the `PrefixDelegationConfig` below. This is okay for
803        // now because we do not use the prefix variant, but if we did we need
804        // to support pretty-printing prefixes as it is considered PII and only
805        // pretty-printed prefixes/addresses are properly redacted.
806        todo!("https://fxbug.dev/42069036: Support pretty-printing configured prefix");
807    }
808
809    let source = DnsServersUpdateSource::Dhcpv6 { interface_id: id.get() };
810    assert!(
811        !watchers.contains_key(&source) && !dhcpv6_prefixes_streams.contains_key(&id),
812        "interface with id={} already has a DHCPv6 client",
813        id
814    );
815
816    let (dns_servers_stream, prefixes_stream) = dhcpv6::start_client(
817        dhcpv6_client_provider,
818        id,
819        sockaddr,
820        duid,
821        pd_config.clone(),
822    )
823        .with_context(|| {
824            format!(
825                "failed to start DHCPv6 client on interface {} (id={}) w/ sockaddr {} and PD config {:?}",
826                name,
827                id,
828                sockaddr.display_ext(),
829                pd_config,
830            )
831        })?;
832    if let Some(o) = watchers.insert(source, dns_servers_stream.tagged(source).boxed()) {
833        let _: Pin<Box<BoxStream<'_, _>>> = o;
834        unreachable!("DNS server watchers must not contain key {:?}", source);
835    }
836    if let Some(o) = dhcpv6_prefixes_streams.insert(id, prefixes_stream.tagged(id)) {
837        let _: Pin<Box<dhcpv6::InterfaceIdTaggedPrefixesStream>> = o;
838        unreachable!("DHCPv6 prefixes streams must not contain key {:?}", id);
839    }
840
841    info!(
842        "started DHCPv6 client on host interface {} (id={}) w/ sockaddr {} and PD config {:?}",
843        name,
844        id,
845        sockaddr.display_ext(),
846        pd_config,
847    );
848
849    Ok(Some(sockaddr))
850}
851
852enum RequestStream {
853    Virtualization(fnet_virtualization::ControlRequestStream),
854    Dhcpv6PrefixProvider(fnet_dhcpv6::PrefixProviderRequestStream),
855    Masquerade(fnet_masquerade::FactoryRequestStream),
856    DnsServerWatcher(fnet_name::DnsServerWatcherRequestStream),
857    NetworkAttributes(fnp_properties::NetworksRequestStream),
858    DelegatedNetworks(fnp_socketproxy::NetworkRegistryRequestStream),
859    NetworkTokenResolver(fnp_properties::NetworkTokenResolverRequestStream),
860}
861
862impl std::fmt::Debug for RequestStream {
863    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
864        match *self {
865            RequestStream::Virtualization(_) => write!(f, "Virtualization"),
866            RequestStream::Dhcpv6PrefixProvider(_) => write!(f, "Dhcpv6PrefixProvider"),
867            RequestStream::Masquerade(_) => write!(f, "Masquerade"),
868            RequestStream::DnsServerWatcher(_) => write!(f, "DnsServerWatcher"),
869            RequestStream::NetworkAttributes(_) => write!(f, "NetworkAttributes"),
870            RequestStream::DelegatedNetworks(_) => write!(f, "DelegatedNetworks"),
871            RequestStream::NetworkTokenResolver(_) => write!(f, "NetworkTokenResolver"),
872        }
873    }
874}
875
876#[derive(Debug, PartialEq)]
877enum AllowClientRestart {
878    No,
879    Yes,
880}
881
882#[must_use]
883#[derive(Debug, PartialEq)]
884enum Dhcpv4ConfigurationHandlerResult {
885    ContinueOperation,
886    ClientStopped(AllowClientRestart),
887}
888
889// Events associated with provisioning a device.
890#[derive(Debug)]
891enum ProvisioningEvent {
892    InterfaceWatcherResult(
893        Result<
894            Option<fnet_interfaces_ext::EventWithInterest<fnet_interfaces_ext::DefaultInterest>>,
895            fidl::Error,
896        >,
897    ),
898    DnsWatcherResult(
899        Option<(
900            dns_server_watcher::DnsServersUpdateSource,
901            Result<Vec<fnet_name::DnsServer_>, fidl::Error>,
902        )>,
903    ),
904    RequestStream(Option<RequestStream>),
905    Dhcpv4Configuration(
906        Option<(InterfaceId, Result<fnet_dhcp_ext::Configuration, fnet_dhcp_ext::Error>)>,
907    ),
908    Dhcpv4ClientDelayedStart(InterfaceId),
909    Dhcpv6PrefixProviderRequest(Result<fnet_dhcpv6::PrefixProviderRequest, fidl::Error>),
910    Dhcpv6PrefixControlRequest(
911        Option<Result<Option<fnet_dhcpv6::PrefixControlRequest>, fidl::Error>>,
912    ),
913    Dhcpv6Prefixes(Option<(InterfaceId, Result<Vec<fnet_dhcpv6::Prefix>, fidl::Error>)>),
914    DnsServerWatcherRequest(
915        (dns::ConnectionId, Result<fnet_name::DnsServerWatcherRequest, fidl::Error>),
916    ),
917    VirtualizationEvent(virtualization::Event),
918    MasqueradeEvent(masquerade::Event),
919}
920
921// Per RFC 2131 "The client SHOULD wait a minimum of ten seconds before
922// restarting the configuration process to avoid excessive network traffic in
923// case of looping."
924const DHCP_CLIENT_RESTART_WAIT_TIME: std::time::Duration = std::time::Duration::from_secs(10);
925
926impl<'a> NetCfg<'a> {
927    async fn new(
928        filter_enabled_interface_types: HashSet<InterfaceType>,
929        interface_metrics: InterfaceMetrics,
930        enable_dhcpv6: bool,
931        forwarded_device_classes: ForwardedDeviceClasses,
932        allowed_upstream_device_classes: &'a HashSet<DeviceClass>,
933        interface_naming_policy: Vec<interface::NamingRule>,
934        interface_provisioning_policy: Vec<interface::ProvisioningRule>,
935        enable_socket_proxy: bool,
936    ) -> Result<NetCfg<'a>, anyhow::Error> {
937        let svc_dir = clone_namespace_svc().context("error cloning svc directory handle")?;
938        let stack = svc_connect::<fnet_stack::StackMarker>(&svc_dir)
939            .await
940            .context("could not connect to stack")?;
941        let lookup_admin = svc_connect::<fnet_name::LookupAdminMarker>(&svc_dir)
942            .await
943            .context("could not connect to lookup admin")?;
944
945        let filter_control = {
946            let filter_deprecated =
947                optional_svc_connect::<fnet_filter_deprecated::FilterMarker>(&svc_dir)
948                    .await
949                    .context("could not connect to filter deprecated")?;
950            let filter_current = optional_svc_connect::<fnet_filter::ControlMarker>(&svc_dir)
951                .await
952                .context("could not connect to filter")?;
953            filter::FilterControl::new(filter_deprecated, filter_current).await?
954        };
955
956        let interface_state = svc_connect::<fnet_interfaces::StateMarker>(&svc_dir)
957            .await
958            .context("could not connect to interfaces state")?;
959        let dhcp_server = optional_svc_connect::<fnet_dhcp::Server_Marker>(&svc_dir)
960            .await
961            .context("could not connect to DHCP Server")?;
962        let dhcpv4_client_provider = {
963            let provider = optional_svc_connect::<fnet_dhcp::ClientProviderMarker>(&svc_dir)
964                .await
965                .context("could not connect to DHCPv4 client provider")?;
966            match provider {
967                Some(provider) => dhcpv4::probe_for_presence(&provider).await.then_some(provider),
968                None => None,
969            }
970        };
971        let dhcpv6_client_provider = if enable_dhcpv6 {
972            let dhcpv6_client_provider =
973                optional_svc_connect::<fnet_dhcpv6::ClientProviderMarker>(&svc_dir)
974                    .await
975                    .context("could not connect to DHCPv6 client provider")?;
976            dhcpv6_client_provider
977        } else {
978            None
979        };
980        let route_set_v4_provider = svc_connect::<fnet_routes_admin::RouteTableV4Marker>(&svc_dir)
981            .await
982            .context("could not connect to fuchsia.net.routes.admin.RouteTableV4")?;
983        let installer = svc_connect::<fnet_interfaces_admin::InstallerMarker>(&svc_dir)
984            .await
985            .context("could not connect to installer")?;
986        let route_advertisement_watcher_provider = optional_svc_connect::<
987            fnet_ndp::RouterAdvertisementOptionWatcherProviderMarker,
988        >(&svc_dir)
989        .await
990        .context("could not connect to fuchsia.net.ndp.RouteAdvertisementOptionWatcherProvider")?;
991        let socket_proxy_state = if enable_socket_proxy {
992            let fuchsia_networks = fuchsia_component::client::connect_to_protocol::<
993                fnp_socketproxy::FuchsiaNetworksMarker,
994            >()
995            .context("could not connect to Fuchsia Networks Marker")?;
996            Some(SocketProxyState::new(fuchsia_networks))
997        } else {
998            None
999        };
1000        let interface_naming_config =
1001            interface::InterfaceNamingConfig::from_naming_rules(interface_naming_policy);
1002
1003        Ok(NetCfg {
1004            stack,
1005            lookup_admin,
1006            filter_control,
1007            interface_state,
1008            dhcp_server,
1009            installer,
1010            dhcpv4_client_provider,
1011            dhcpv6_client_provider,
1012            route_set_v4_provider,
1013            socket_proxy_state,
1014            locally_provisioned_network_rule_set: None,
1015            interface_naming_config,
1016            filter_enabled_state: FilterEnabledState::new(filter_enabled_interface_types),
1017            interface_properties: Default::default(),
1018            interface_states: Default::default(),
1019            interface_metrics,
1020            dns_servers: Default::default(),
1021            dns_server_watch_responders: Default::default(),
1022            route_advertisement_watcher_provider,
1023            forwarded_device_classes,
1024            dhcpv4_configuration_streams: dhcpv4::ConfigurationStreamMap::empty(),
1025            dhcpv6_prefix_provider_handler: None,
1026            dhcpv6_prefixes_streams: dhcpv6::PrefixesStreamMap::empty(),
1027            allowed_upstream_device_classes,
1028            interface_provisioning_policy,
1029            netpol_networks_service: Default::default(),
1030            enable_socket_proxy,
1031        })
1032    }
1033
1034    /// Updates the DNS servers used by the DNS resolver.
1035    async fn update_dns_servers(
1036        &mut self,
1037        source: DnsServersUpdateSource,
1038        servers: Vec<fnet_name::DnsServer_>,
1039    ) {
1040        dns::update_servers(
1041            &self.lookup_admin,
1042            &mut self.dns_servers,
1043            &mut self.dns_server_watch_responders,
1044            &mut self.netpol_networks_service,
1045            source,
1046            servers,
1047        )
1048        .await
1049    }
1050
1051    /// Handles the completion of the DNS server watcher associated with `source`.
1052    ///
1053    /// Clears the servers for `source` and removes the watcher from `dns_watchers`.
1054    async fn handle_dns_server_watcher_done(
1055        &mut self,
1056        source: DnsServersUpdateSource,
1057        dns_watchers: &mut DnsServerWatchers<'_>,
1058    ) -> Result<(), anyhow::Error> {
1059        match source {
1060            DnsServersUpdateSource::Default => {
1061                panic!("should not have a DNS server watcher for the default source");
1062            }
1063            DnsServersUpdateSource::Dhcpv4 { interface_id } => {
1064                unreachable!(
1065                    "DHCPv4 configurations are not obtained through DNS server watcher; \
1066                     interface_id={}",
1067                    interface_id,
1068                )
1069            }
1070            DnsServersUpdateSource::Netstack => Ok(()),
1071            DnsServersUpdateSource::Dhcpv6 { interface_id } => {
1072                let interface_id = interface_id.try_into().expect("should be nonzero");
1073                let InterfaceState { config, provisioning, .. } = self
1074                    .interface_states
1075                    .get_mut(&interface_id)
1076                    .unwrap_or_else(|| panic!("no interface state found for id={}", interface_id));
1077
1078                // Netcfg won't start a DHCPv6 client for a delegated interface.
1079                debug_assert!(provisioning == &interface::ProvisioningType::Local);
1080
1081                match config {
1082                    InterfaceConfigState::Host(HostInterfaceState {
1083                        dhcpv4_client: _,
1084                        dhcpv6_client_state,
1085                        dhcpv6_pd_config: _,
1086                        interface_admin_auth: _,
1087                        interface_naming_id: _,
1088                    }) => {
1089                        let _: dhcpv6::ClientState =
1090                            dhcpv6_client_state.take().unwrap_or_else(|| {
1091                                panic!(
1092                                    "DHCPv6 was not being performed on host interface with id={}",
1093                                    interface_id
1094                                )
1095                            });
1096
1097                        // If the DNS server watcher is done, that means the server-end
1098                        // of the channel is closed meaning DHCPv6 has been stopped.
1099                        // Perform the cleanup for our end of the DHCPv6 client
1100                        // (which will update DNS servers) and send an prefix update to any
1101                        // blocked watchers of fuchsia.net.dhcpv6/PrefixControl.WatchPrefix.
1102                        dhcpv6::stop_client(
1103                            &self.lookup_admin,
1104                            &mut self.dns_servers,
1105                            &mut self.dns_server_watch_responders,
1106                            &mut self.netpol_networks_service,
1107                            interface_id,
1108                            dns_watchers,
1109                            &mut self.dhcpv6_prefixes_streams,
1110                        )
1111                        .await;
1112
1113                        dhcpv6::maybe_send_watch_prefix_response(
1114                            &self.interface_states,
1115                            &self.allowed_upstream_device_classes,
1116                            self.dhcpv6_prefix_provider_handler.as_mut(),
1117                        )
1118                    }
1119                    InterfaceConfigState::WlanAp(WlanApInterfaceState {
1120                        interface_naming_id: _,
1121                    }) => {
1122                        panic!(
1123                            "should not have a DNS watcher for a WLAN AP interface with id={}",
1124                            interface_id
1125                        );
1126                    }
1127                    InterfaceConfigState::Blackhole(BlackholeInterfaceState) => {
1128                        panic!(
1129                            "should not have a DNS watcher for a blackhole interface with id={}",
1130                            interface_id
1131                        );
1132                    }
1133                }
1134            }
1135            DnsServersUpdateSource::Ndp { interface_id } => {
1136                let interface_id = interface_id.try_into().expect("should be nonzero");
1137                let InterfaceState { config, provisioning, .. } = self
1138                    .interface_states
1139                    .get_mut(&interface_id)
1140                    .unwrap_or_else(|| panic!("no interface state found for id={}", interface_id));
1141
1142                // Netcfg won't watch NDP servers for a delegated interface.
1143                debug_assert!(provisioning == &interface::ProvisioningType::Local);
1144
1145                match config {
1146                    InterfaceConfigState::Host(HostInterfaceState { .. }) => {
1147                        Ok(dns::remove_rdnss_watcher(
1148                            &self.lookup_admin,
1149                            &mut self.dns_servers,
1150                            &mut self.dns_server_watch_responders,
1151                            &mut self.netpol_networks_service,
1152                            interface_id,
1153                            dns_watchers,
1154                        )
1155                        .await)
1156                    }
1157                    InterfaceConfigState::WlanAp(WlanApInterfaceState { .. }) => {
1158                        panic!(
1159                            "should not have a NDP DNS watcher for a WLAN AP interface with id={}",
1160                            interface_id
1161                        );
1162                    }
1163                    InterfaceConfigState::Blackhole(BlackholeInterfaceState) => {
1164                        panic!(
1165                            "should not have a NDP DNS watcher for a blackhole interface with id={}",
1166                            interface_id
1167                        );
1168                    }
1169                }
1170            }
1171            DnsServersUpdateSource::SocketProxy => {
1172                // Remove the SocketProxy DNS servers when the server watcher is complete.
1173                Ok(self.update_dns_servers(source, vec![]).await)
1174            }
1175        }
1176    }
1177
1178    /// Run the network configuration eventloop.
1179    ///
1180    /// The device directory will be monitored for device events and the netstack will be
1181    /// configured with a new interface on new device discovery.
1182    async fn run(
1183        &mut self,
1184        mut virtualization_handler: impl virtualization::Handler,
1185    ) -> Result<(), anyhow::Error> {
1186        let netdev_stream =
1187            self.create_device_stream().await.context("create netdevice stream")?.fuse();
1188        let mut netdev_stream = pin!(netdev_stream);
1189
1190        let if_watcher_event_stream =
1191            fnet_interfaces_ext::event_stream_from_state(&self.interface_state, Default::default())
1192                .context("error creating interface watcher event stream")?
1193                .fuse();
1194        let mut if_watcher_event_stream = pin!(if_watcher_event_stream);
1195
1196        let dns_server_watcher =
1197            fuchsia_component::client::connect_to_protocol::<fnet_name::DnsServerWatcherMarker>()
1198                .context("error connecting to dns server watcher")?;
1199        let netstack_dns_server_stream = dns_server_watcher::new_dns_server_stream(
1200            DnsServersUpdateSource::Netstack,
1201            dns_server_watcher,
1202        )
1203        .boxed();
1204
1205        let dns_watchers = DnsServerWatchers::empty();
1206        // `Fuse` (the return of `fuse`) guarantees that once the underlying stream is
1207        // exhausted, future attempts to poll the stream will return `None`. This would
1208        // be undesirable if we needed to support a scenario where all streams are
1209        // exhausted before adding a new stream to the `StreamMap`. However,
1210        // `netstack_dns_server_stream` is not expected to end so we can fuse the
1211        // `StreamMap` without issue.
1212        let mut dns_watchers = dns_watchers.fuse();
1213
1214        assert!(
1215            dns_watchers
1216                .get_mut()
1217                .insert(DnsServersUpdateSource::Netstack, netstack_dns_server_stream)
1218                .is_none(),
1219            "dns watchers should be empty"
1220        );
1221
1222        if self.enable_socket_proxy {
1223            let socketproxy_dns_server_watcher = fuchsia_component::client::connect_to_protocol::<
1224                fnp_socketproxy::DnsServerWatcherMarker,
1225            >()
1226            .context("error connecting to socketproxy dns server watcher")?;
1227            let socketproxy_dns_server_stream =
1228                dns_server_watcher::new_dns_server_stream_socketproxy(
1229                    socketproxy_dns_server_watcher,
1230                )
1231                .boxed();
1232
1233            assert!(
1234                dns_watchers
1235                    .get_mut()
1236                    .insert(DnsServersUpdateSource::SocketProxy, socketproxy_dns_server_stream)
1237                    .is_none(),
1238                "dns watchers should be empty"
1239            );
1240        }
1241
1242        let mut masquerade_handler = MasqueradeHandler::default();
1243
1244        // Serve fuchsia.net.virtualization/Control.
1245        let mut fs = ServiceFs::new_local();
1246        let _: &mut ServiceFsDir<'_, _> = fs
1247            .dir("svc")
1248            .add_fidl_service(RequestStream::Virtualization)
1249            .add_fidl_service(RequestStream::Dhcpv6PrefixProvider)
1250            .add_fidl_service(RequestStream::Masquerade)
1251            .add_fidl_service(RequestStream::DnsServerWatcher)
1252            .add_fidl_service(RequestStream::NetworkAttributes)
1253            .add_fidl_service(RequestStream::DelegatedNetworks)
1254            .add_fidl_service(RequestStream::NetworkTokenResolver);
1255        let _: &mut ServiceFs<_> =
1256            fs.take_and_serve_directory_handle().context("take and serve directory handle")?;
1257        let mut fs = fs.fuse();
1258
1259        let mut dhcpv6_prefix_provider_requests =
1260            futures::stream::SelectAll::<fnet_dhcpv6::PrefixProviderRequestStream>::new();
1261
1262        // Maintain a queue of virtualization events to be dispatched to the virtualization handler.
1263        let mut virtualization_events =
1264            futures::stream::SelectAll::<virtualization::EventStream>::new();
1265
1266        // Maintain a queue of masquerade events to be dispatched to the masquerade handler.
1267        let mut masquerade_events = futures::stream::SelectAll::<masquerade::EventStream>::new();
1268
1269        let mut dns_server_watcher_incoming_requests =
1270            dns::DnsServerWatcherRequestStreams::default();
1271
1272        let mut networks_request_streams =
1273            network::ConnectionTagged::<fnp_properties::NetworksRequestStream>::default();
1274        let mut network_token_resolver_request_streams =
1275            futures::stream::SelectAll::<fnp_properties::NetworkTokenResolverRequestStream>::new();
1276
1277        let mut delegated_networks_stream =
1278            DelegatedNetworksStream::Right(futures_util::stream::empty());
1279
1280        // Lifecycle handle takes no args, must be set to zero.
1281        // See zircon/processargs.h.
1282        const LIFECYCLE_HANDLE_ARG: u16 = 0;
1283        let lifecycle = fuchsia_runtime::take_startup_handle(fuchsia_runtime::HandleInfo::new(
1284            fuchsia_runtime::HandleType::Lifecycle,
1285            LIFECYCLE_HANDLE_ARG,
1286        ))
1287        .ok_or_else(|| anyhow::anyhow!("lifecycle handle not present"))?;
1288        let lifecycle = fuchsia_async::Channel::from_channel(lifecycle.into());
1289        let mut lifecycle = fidl_fuchsia_process_lifecycle::LifecycleRequestStream::from_channel(
1290            fidl::AsyncChannel::from(lifecycle),
1291        );
1292
1293        debug!("starting eventloop...");
1294
1295        enum Event {
1296            NetworkDeviceResult(Result<Option<devices::NetworkDeviceInstance>, anyhow::Error>),
1297            LifecycleRequest(
1298                Result<Option<fidl_fuchsia_process_lifecycle::LifecycleRequest>, fidl::Error>,
1299            ),
1300            NetworkAttributesRequest(
1301                (network::ConnectionId, Result<fnp_properties::NetworksRequest, fidl::Error>),
1302            ),
1303            NetworkTokenResolverRequest(
1304                Result<fnp_properties::NetworkTokenResolverRequest, fidl::Error>,
1305            ),
1306            DelegatedNetworksUpdate(Result<fnp_socketproxy::NetworkRegistryRequest, fidl::Error>),
1307            ProvisioningEvent(ProvisioningEvent),
1308        }
1309
1310        loop {
1311            let mut dhcpv6_prefix_control_fut = futures::future::OptionFuture::from(
1312                self.dhcpv6_prefix_provider_handler
1313                    .as_mut()
1314                    .map(dhcpv6::PrefixProviderHandler::try_next_prefix_control_request),
1315            );
1316            let mut delayed_dhcpv4_client_starts = self
1317                .interface_states
1318                .iter_mut()
1319                .filter_map(|(id, InterfaceState { config, .. })| match config {
1320                    InterfaceConfigState::Host(HostInterfaceState {
1321                        dhcpv4_client,
1322                        dhcpv6_client_state: _,
1323                        dhcpv6_pd_config: _,
1324                        interface_admin_auth: _,
1325                        interface_naming_id: _,
1326                    }) => match dhcpv4_client {
1327                        Dhcpv4ClientState::NotRunning => None,
1328                        Dhcpv4ClientState::Running(_) => None,
1329                        Dhcpv4ClientState::ScheduledRestart(timer) => Some(timer.map(|()| *id)),
1330                    },
1331                    InterfaceConfigState::WlanAp(_) | InterfaceConfigState::Blackhole(_) => None,
1332                })
1333                .collect::<futures::stream::FuturesUnordered<_>>();
1334            let event = futures::select! {
1335                netdev_res = netdev_stream.try_next() => {
1336                    Event::NetworkDeviceResult(netdev_res)
1337                }
1338                req = lifecycle.try_next() => {
1339                    Event::LifecycleRequest(req)
1340                }
1341                if_watcher_res = if_watcher_event_stream.try_next() => {
1342                    Event::ProvisioningEvent(
1343                        ProvisioningEvent::InterfaceWatcherResult(if_watcher_res)
1344                    )
1345                }
1346                dns_watchers_res = dns_watchers.next() => {
1347                    Event::ProvisioningEvent(
1348                        ProvisioningEvent::DnsWatcherResult(dns_watchers_res)
1349                    )
1350                }
1351                req_stream = fs.next() => {
1352                    Event::ProvisioningEvent(
1353                        ProvisioningEvent::RequestStream(req_stream)
1354                    )
1355                }
1356                dhcpv4_configuration = self.dhcpv4_configuration_streams.next() => {
1357                    Event::ProvisioningEvent(
1358                        ProvisioningEvent::Dhcpv4Configuration(dhcpv4_configuration)
1359                    )
1360                }
1361                interface_id = delayed_dhcpv4_client_starts.select_next_some() => {
1362                    Event::ProvisioningEvent(
1363                        ProvisioningEvent::Dhcpv4ClientDelayedStart(interface_id)
1364                    )
1365                }
1366                dhcpv6_prefix_req = dhcpv6_prefix_provider_requests.select_next_some() => {
1367                    Event::ProvisioningEvent(
1368                        ProvisioningEvent::Dhcpv6PrefixProviderRequest(dhcpv6_prefix_req)
1369                    )
1370                }
1371                dhcpv6_prefix_control_req = dhcpv6_prefix_control_fut => {
1372                    Event::ProvisioningEvent(
1373                        ProvisioningEvent::Dhcpv6PrefixControlRequest(dhcpv6_prefix_control_req)
1374                    )
1375                }
1376                dhcpv6_prefixes = self.dhcpv6_prefixes_streams.next() => {
1377                    Event::ProvisioningEvent(
1378                        ProvisioningEvent::Dhcpv6Prefixes(dhcpv6_prefixes)
1379                    )
1380                }
1381                req = dns_server_watcher_incoming_requests.select_next_some() => {
1382                    Event::ProvisioningEvent(
1383                        ProvisioningEvent::DnsServerWatcherRequest(req)
1384                    )
1385                }
1386                virt_event = virtualization_events.select_next_some() => {
1387                    Event::ProvisioningEvent(
1388                        ProvisioningEvent::VirtualizationEvent(virt_event)
1389                    )
1390                }
1391                masq_event = masquerade_events.select_next_some() => {
1392                    Event::ProvisioningEvent(
1393                        ProvisioningEvent::MasqueradeEvent(
1394                            masq_event.context("error while receiving MasqueradeEvent")?)
1395                        )
1396                }
1397                net_attr_req = networks_request_streams.select_next_some() => {
1398                    Event::NetworkAttributesRequest(net_attr_req)
1399                }
1400                net_tok_admin_req = network_token_resolver_request_streams.select_next_some() => {
1401                    Event::NetworkTokenResolverRequest(net_tok_admin_req)
1402                }
1403                delegated_networks_update = delegated_networks_stream.select_next_some() => {
1404                    Event::DelegatedNetworksUpdate(delegated_networks_update)
1405                }
1406                complete => return Err(anyhow::anyhow!("eventloop ended unexpectedly")),
1407            };
1408
1409            // `delayed_dhcpv4_client_starts` mutably borrows the delayed-start timers from `self`'s
1410            // InterfaceState map, so we have to drop it here to regain access to `self`.
1411            drop(delayed_dhcpv4_client_starts);
1412            match event {
1413                Event::NetworkDeviceResult(netdev_res) => {
1414                    let instance =
1415                        netdev_res.context("error retrieving netdev instance")?.ok_or_else(
1416                            || anyhow::anyhow!("netdev instance watcher stream ended unexpectedly"),
1417                        )?;
1418                    // DNS watchers must be propagated to start an RA NDP watcher for the interface
1419                    // prior to the interface getting enabled in the Netstack.
1420                    self.handle_device_instance(instance, dns_watchers.get_mut())
1421                        .await
1422                        .context("handle netdev instance")?
1423                }
1424                Event::LifecycleRequest(req) => {
1425                    let req = req.context("lifecycle request")?.ok_or_else(|| {
1426                        anyhow::anyhow!("LifecycleRequestStream ended unexpectedly")
1427                    })?;
1428                    match req {
1429                        fidl_fuchsia_process_lifecycle::LifecycleRequest::Stop {
1430                            control_handle,
1431                        } => {
1432                            info!("received shutdown request");
1433                            // Shutdown request is acknowledged by the lifecycle
1434                            // channel shutting down. Intentionally leak the
1435                            // channel so it'll only be closed on process
1436                            // termination, allowing clean process termination
1437                            // to always be observed.
1438
1439                            // Must drop the control_handle to unwrap the
1440                            // lifecycle channel.
1441                            std::mem::drop(control_handle);
1442                            let (inner, _terminated): (_, bool) = lifecycle.into_inner();
1443                            let inner = std::sync::Arc::try_unwrap(inner).map_err(
1444                                |_: std::sync::Arc<_>| {
1445                                    anyhow::anyhow!("failed to retrieve lifecycle channel")
1446                                },
1447                            )?;
1448                            let inner: zx::Channel = inner.into_channel().into_zx_channel();
1449                            std::mem::forget(inner);
1450
1451                            return Ok(());
1452                        }
1453                    }
1454                }
1455                Event::NetworkAttributesRequest((id, req)) => {
1456                    self.netpol_networks_service
1457                        .handle_network_attributes_request(id, req)
1458                        .await
1459                        .unwrap_or_else(|e| {
1460                            error!("Could not handle network attributes request: {e:?}")
1461                        });
1462                }
1463                Event::NetworkTokenResolverRequest(req) => {
1464                    self.netpol_networks_service
1465                        .handle_network_token_resolver_request(req)
1466                        .await
1467                        .unwrap_or_else(|e| {
1468                            error!("Could not handle network token resolver request: {e:?}")
1469                        });
1470                }
1471                Event::DelegatedNetworksUpdate(update) => {
1472                    self.netpol_networks_service
1473                        .handle_delegated_networks_update(update)
1474                        .await
1475                        .unwrap_or_else(|e| {
1476                            error!("Could not handle delegated network update: {e:?}")
1477                        });
1478                }
1479                Event::ProvisioningEvent(event) => {
1480                    self.handle_provisioning_event(
1481                        event,
1482                        dns_watchers.get_mut(),
1483                        &mut dhcpv6_prefix_provider_requests,
1484                        &mut dns_server_watcher_incoming_requests,
1485                        &mut virtualization_handler,
1486                        &mut virtualization_events,
1487                        &mut masquerade_handler,
1488                        &mut masquerade_events,
1489                        &mut networks_request_streams,
1490                        &mut delegated_networks_stream,
1491                        &mut network_token_resolver_request_streams,
1492                    )
1493                    .await?
1494                }
1495            }
1496        }
1497    }
1498
1499    async fn handle_provisioning_event(
1500        &mut self,
1501        event: ProvisioningEvent,
1502        dns_watchers: &mut DnsServerWatchers<'_>,
1503        dhcpv6_prefix_provider_requests: &mut futures::stream::SelectAll<
1504            fnet_dhcpv6::PrefixProviderRequestStream,
1505        >,
1506        dns_server_watcher_incoming_requests: &mut dns::DnsServerWatcherRequestStreams,
1507        virtualization_handler: &mut impl virtualization::Handler,
1508        virtualization_events: &mut futures::stream::SelectAll<virtualization::EventStream>,
1509        masquerade_handler: &mut MasqueradeHandler,
1510        masquerade_events: &mut futures::stream::SelectAll<masquerade::EventStream>,
1511        networks_request_streams: &mut network::ConnectionTagged<
1512            fnp_properties::NetworksRequestStream,
1513        >,
1514        delegated_networks_stream: &mut DelegatedNetworksStream,
1515        network_token_resolver_request_streams: &mut futures::stream::SelectAll<
1516            fnp_properties::NetworkTokenResolverRequestStream,
1517        >,
1518    ) -> Result<(), anyhow::Error> {
1519        match event {
1520            ProvisioningEvent::InterfaceWatcherResult(if_watcher_res) => {
1521                let event = if_watcher_res
1522                    .unwrap_or_else(|err| exit_with_fidl_error(err))
1523                    .expect("watcher stream never returns None");
1524                trace!("got interfaces watcher event = {:?}", event);
1525
1526                self.handle_interface_watcher_event(event, dns_watchers, virtualization_handler)
1527                    .await
1528                    .context("handle interface watcher event")?;
1529            }
1530            ProvisioningEvent::DnsWatcherResult(dns_watchers_res) => {
1531                let (source, res) = dns_watchers_res.ok_or_else(|| {
1532                    anyhow::anyhow!("dns watchers stream should never be exhausted")
1533                })?;
1534                let servers = match res {
1535                    Ok(s) => s,
1536                    Err(e) => {
1537                        // TODO(https://fxbug.dev/42135335): Restart the DNS server watcher.
1538                        warn!(
1539                            "non-fatal error getting next event from DNS server watcher stream
1540                            with source = {:?}: {:?}",
1541                            source, e
1542                        );
1543                        self.handle_dns_server_watcher_done(source, dns_watchers)
1544                            .await
1545                            .with_context(|| {
1546                                format!(
1547                                    "error handling completion of DNS server watcher for \
1548                                    {:?}",
1549                                    source
1550                                )
1551                            })?;
1552                        return Ok(());
1553                    }
1554                };
1555
1556                self.update_dns_servers(source, servers).await;
1557            }
1558            // TODO(https://fxbug.dev/42080722): Add tests to ensure we do not offer
1559            // these services when interface has ProvisioningType::Delegated
1560            // state.
1561            ProvisioningEvent::RequestStream(req_stream) => {
1562                match req_stream.context("ServiceFs ended unexpectedly")? {
1563                    RequestStream::Virtualization(req_stream) => virtualization_handler
1564                        .handle_event(
1565                            virtualization::Event::ControlRequestStream(req_stream),
1566                            virtualization_events,
1567                        )
1568                        .await
1569                        .context("handle virtualization event")
1570                        .or_else(errors::Error::accept_non_fatal)?,
1571                    RequestStream::Dhcpv6PrefixProvider(req_stream) => {
1572                        dhcpv6_prefix_provider_requests.push(req_stream);
1573                    }
1574                    RequestStream::Masquerade(req_stream) => {
1575                        masquerade_handler
1576                            .handle_event(
1577                                masquerade::Event::FactoryRequestStream(req_stream),
1578                                masquerade_events,
1579                                &mut self.filter_control,
1580                                &mut self.filter_enabled_state,
1581                                &self.interface_states,
1582                            )
1583                            .await
1584                    }
1585                    RequestStream::DnsServerWatcher(req_stream) => {
1586                        dns_server_watcher_incoming_requests.handle_request_stream(req_stream);
1587                    }
1588                    RequestStream::NetworkAttributes(req_stream) => {
1589                        networks_request_streams.push(req_stream)
1590                    }
1591                    RequestStream::DelegatedNetworks(req_stream) => {
1592                        if let futures::future::Either::Right(_) = delegated_networks_stream {
1593                            *delegated_networks_stream = futures::future::Either::Left(req_stream);
1594                        } else {
1595                            error!(
1596                                "Only one instance of fidl.net.policy.socketproxy/NetworkRegistry \
1597                                 may be active at a time"
1598                            );
1599                        }
1600                    }
1601                    RequestStream::NetworkTokenResolver(req_stream) => {
1602                        network_token_resolver_request_streams.push(req_stream)
1603                    }
1604                };
1605            }
1606            ProvisioningEvent::Dhcpv4Configuration(config) => {
1607                let (interface_id, config) =
1608                    config.expect("DHCPv4 configuration stream is never exhausted");
1609                match self.handle_dhcpv4_configuration(interface_id, config).await {
1610                    Dhcpv4ConfigurationHandlerResult::ContinueOperation => (),
1611                    Dhcpv4ConfigurationHandlerResult::ClientStopped(allow_restart) => {
1612                        let interface_name = self
1613                            .interface_properties
1614                            .get(&interface_id)
1615                            .map(
1616                                |fnet_interfaces_ext::PropertiesAndState {
1617                                     state: (),
1618                                     properties: fnet_interfaces_ext::Properties { name, .. },
1619                                 }| name.as_str(),
1620                            )
1621                            .unwrap_or("<removed>");
1622                        let state = self
1623                            .interface_states
1624                            .get_mut(&interface_id)
1625                            .map(
1626                                |InterfaceState {
1627                                     control,
1628                                     device_class: _,
1629                                     config,
1630                                     provisioning: _,
1631                                 }| {
1632                                    match config {
1633                                        InterfaceConfigState::Host(HostInterfaceState {
1634                                            dhcpv4_client,
1635                                            dhcpv6_client_state: _,
1636                                            dhcpv6_pd_config: _,
1637                                            interface_admin_auth: _,
1638                                            interface_naming_id: _,
1639                                        }) => Some((dhcpv4_client, control)),
1640                                        InterfaceConfigState::WlanAp(_)
1641                                        | InterfaceConfigState::Blackhole(_) => None,
1642                                    }
1643                                },
1644                            )
1645                            .flatten();
1646
1647                        match state {
1648                            None => {
1649                                log::error!(
1650                                    "Trying to handle DHCPv4 client shutdown \
1651                                (id={interface_id}), but no client is running on that interface"
1652                                );
1653                            }
1654                            Some((dhcpv4_client, control)) => {
1655                                Self::handle_dhcpv4_client_stop(
1656                                    interface_id,
1657                                    interface_name,
1658                                    dhcpv4_client,
1659                                    &mut self.dhcpv4_configuration_streams,
1660                                    &mut self.dns_servers,
1661                                    &mut self.dns_server_watch_responders,
1662                                    &mut self.netpol_networks_service,
1663                                    control,
1664                                    &self.lookup_admin,
1665                                    dhcpv4::AlreadyObservedClientExit::Yes,
1666                                )
1667                                .await;
1668
1669                                match allow_restart {
1670                                    AllowClientRestart::No => (),
1671                                    AllowClientRestart::Yes => {
1672                                        // The client exited due to an unexpected error. Schedule it
1673                                        // to be restarted after waiting a backoff period.
1674                                        *dhcpv4_client =
1675                                            Dhcpv4ClientState::ScheduledRestart(Box::pin(
1676                                                fasync::Timer::new(DHCP_CLIENT_RESTART_WAIT_TIME),
1677                                            ));
1678                                    }
1679                                }
1680                            }
1681                        }
1682                    }
1683                }
1684            }
1685            ProvisioningEvent::Dhcpv4ClientDelayedStart(interface_id) => {
1686                self.on_delayed_dhcpv4_client_start(interface_id)
1687                    .await
1688                    .or_else(errors::Error::accept_non_fatal)?;
1689            }
1690            ProvisioningEvent::Dhcpv6PrefixProviderRequest(res) => {
1691                match res {
1692                    Ok(fnet_dhcpv6::PrefixProviderRequest::AcquirePrefix {
1693                        config,
1694                        prefix,
1695                        control_handle: _,
1696                    }) => {
1697                        self.handle_dhcpv6_acquire_prefix(config, prefix, dns_watchers)
1698                            .await
1699                            .or_else(errors::Error::accept_non_fatal)?;
1700                    }
1701                    Err(e) => {
1702                        error!("fuchsia.net.dhcpv6/PrefixProvider request error: {:?}", e)
1703                    }
1704                };
1705            }
1706            ProvisioningEvent::Dhcpv6PrefixControlRequest(req) => {
1707                let res = req.context(
1708                    "PrefixControl OptionFuture will only be selected if it is not None",
1709                )?;
1710                match res {
1711                    Err(e) => {
1712                        error!("fuchsia.net.dhcpv6/PrefixControl request stream error: {:?}", e);
1713                        self.on_dhcpv6_prefix_control_close(dns_watchers).await;
1714                    }
1715                    Ok(None) => {
1716                        info!("fuchsia.net.dhcpv6/PrefixControl closed by client");
1717                        self.on_dhcpv6_prefix_control_close(dns_watchers).await;
1718                    }
1719                    Ok(Some(fnet_dhcpv6::PrefixControlRequest::WatchPrefix { responder })) => {
1720                        self.handle_watch_prefix(responder, dns_watchers)
1721                            .await
1722                            .context("handle PrefixControl.WatchPrefix")
1723                            .unwrap_or_else(accept_error);
1724                    }
1725                };
1726            }
1727            ProvisioningEvent::Dhcpv6Prefixes(prefixes) => {
1728                let (interface_id, res) =
1729                    prefixes.context("DHCPv6 watch prefixes stream map can never be exhausted")?;
1730                self.handle_dhcpv6_prefixes(interface_id, res, dns_watchers)
1731                    .await
1732                    .unwrap_or_else(accept_error);
1733            }
1734            ProvisioningEvent::DnsServerWatcherRequest((id, req)) => {
1735                self.dns_server_watch_responders.handle_request(id, req, &self.dns_servers)?;
1736            }
1737            ProvisioningEvent::VirtualizationEvent(event) => {
1738                virtualization_handler
1739                    .handle_event(event, virtualization_events)
1740                    .await
1741                    .context("handle virtualization event")
1742                    .or_else(errors::Error::accept_non_fatal)?;
1743            }
1744            ProvisioningEvent::MasqueradeEvent(event) => {
1745                masquerade_handler
1746                    .handle_event(
1747                        event,
1748                        masquerade_events,
1749                        &mut self.filter_control,
1750                        &mut self.filter_enabled_state,
1751                        &self.interface_states,
1752                    )
1753                    .await
1754            }
1755        };
1756        return Ok(());
1757    }
1758
1759    async fn handle_dhcpv4_client_stop(
1760        id: InterfaceId,
1761        name: &str,
1762        dhcpv4_client: &mut Dhcpv4ClientState,
1763        configuration_streams: &mut dhcpv4::ConfigurationStreamMap,
1764        dns_servers: &mut DnsServers,
1765        dns_server_watch_responders: &mut dns::DnsServerWatchResponders,
1766        netpol_networks_service: &mut network::NetpolNetworksService,
1767        control: &fnet_interfaces_ext::admin::Control,
1768        lookup_admin: &fnet_name::LookupAdminProxy,
1769        already_observed_client_exit: dhcpv4::AlreadyObservedClientExit,
1770    ) {
1771        match std::mem::replace(dhcpv4_client, Dhcpv4ClientState::NotRunning) {
1772            Dhcpv4ClientState::NotRunning => (),
1773            Dhcpv4ClientState::Running(c) => {
1774                dhcpv4::stop_client(
1775                    id,
1776                    name,
1777                    c,
1778                    configuration_streams,
1779                    dns_servers,
1780                    dns_server_watch_responders,
1781                    netpol_networks_service,
1782                    control,
1783                    lookup_admin,
1784                    already_observed_client_exit,
1785                )
1786                .await;
1787            }
1788            Dhcpv4ClientState::ScheduledRestart(_) => (),
1789        }
1790    }
1791
1792    async fn handle_dhcpv4_client_start(
1793        id: InterfaceId,
1794        name: &str,
1795        dhcpv4_client: &mut Dhcpv4ClientState,
1796        dhcpv4_client_provider: Option<&fnet_dhcp::ClientProviderProxy>,
1797        route_set_provider: &fnet_routes_admin::RouteTableV4Proxy,
1798        interface_admin_auth: &fnet_resources::GrantForInterfaceAuthorization,
1799        configuration_streams: &mut dhcpv4::ConfigurationStreamMap,
1800    ) -> Result<(), errors::Error> {
1801        *dhcpv4_client = match dhcpv4_client_provider {
1802            None => Dhcpv4ClientState::NotRunning,
1803            Some(p) => Dhcpv4ClientState::Running(
1804                dhcpv4::start_client(
1805                    id,
1806                    name,
1807                    p,
1808                    route_set_provider,
1809                    interface_admin_auth,
1810                    configuration_streams,
1811                )
1812                .await?,
1813            ),
1814        };
1815        Ok(())
1816    }
1817
1818    async fn handle_dhcpv4_client_update(
1819        id: InterfaceId,
1820        name: &str,
1821        online: bool,
1822        dhcpv4_client: &mut Dhcpv4ClientState,
1823        dhcpv4_client_provider: Option<&fnet_dhcp::ClientProviderProxy>,
1824        configuration_streams: &mut dhcpv4::ConfigurationStreamMap,
1825        dns_servers: &mut DnsServers,
1826        dns_server_watch_responders: &mut dns::DnsServerWatchResponders,
1827        netpol_networks_service: &mut network::NetpolNetworksService,
1828        control: &fnet_interfaces_ext::admin::Control,
1829        lookup_admin: &fnet_name::LookupAdminProxy,
1830        route_set_provider: &fnet_routes_admin::RouteTableV4Proxy,
1831        interface_admin_auth: &fnet_resources::GrantForInterfaceAuthorization,
1832    ) -> Result<(), errors::Error> {
1833        if online {
1834            Self::handle_dhcpv4_client_start(
1835                id,
1836                name,
1837                dhcpv4_client,
1838                dhcpv4_client_provider,
1839                route_set_provider,
1840                interface_admin_auth,
1841                configuration_streams,
1842            )
1843            .await?
1844        } else {
1845            Self::handle_dhcpv4_client_stop(
1846                id,
1847                name,
1848                dhcpv4_client,
1849                configuration_streams,
1850                dns_servers,
1851                dns_server_watch_responders,
1852                netpol_networks_service,
1853                control,
1854                lookup_admin,
1855                dhcpv4::AlreadyObservedClientExit::No,
1856            )
1857            .await
1858        }
1859
1860        Ok(())
1861    }
1862
1863    async fn on_delayed_dhcpv4_client_start(
1864        &mut self,
1865        interface_id: InterfaceId,
1866    ) -> Result<(), errors::Error> {
1867        let Self {
1868            dhcpv4_client_provider,
1869            route_set_v4_provider,
1870            interface_states,
1871            interface_properties,
1872            dhcpv4_configuration_streams,
1873            ..
1874        } = self;
1875
1876        let (dhcpv4_client, interface_admin_auth) = match interface_states
1877            .get_mut(&interface_id)
1878            .and_then(|InterfaceState { config, .. }| match config {
1879                InterfaceConfigState::Host(HostInterfaceState {
1880                    dhcpv4_client,
1881                    dhcpv6_client_state: _,
1882                    dhcpv6_pd_config: _,
1883                    interface_admin_auth,
1884                    interface_naming_id: _,
1885                }) => Some((dhcpv4_client, interface_admin_auth)),
1886                InterfaceConfigState::WlanAp(_) | InterfaceConfigState::Blackhole(_) => None,
1887            }) {
1888            Some(state) => state,
1889            None => {
1890                // It's fine for the interface to have been removed before we
1891                // got around to restarting the client.
1892                return Ok(());
1893            }
1894        };
1895
1896        match dhcpv4_client {
1897            Dhcpv4ClientState::NotRunning => (),
1898            Dhcpv4ClientState::Running(_) => {
1899                // We already restarted the client before reaching this point.
1900                return Ok(());
1901            }
1902            Dhcpv4ClientState::ScheduledRestart(_) => {
1903                *dhcpv4_client = Dhcpv4ClientState::NotRunning;
1904            }
1905        };
1906
1907        let properties = match interface_properties.get(&interface_id) {
1908            Some(fnet_interfaces_ext::PropertiesAndState { properties, state: () }) => properties,
1909            None => return Ok(()),
1910        };
1911
1912        Self::handle_dhcpv4_client_start(
1913            properties.id.into(),
1914            &properties.name,
1915            dhcpv4_client,
1916            dhcpv4_client_provider.as_ref(),
1917            route_set_v4_provider,
1918            interface_admin_auth,
1919            dhcpv4_configuration_streams,
1920        )
1921        .await
1922    }
1923
1924    /// Handles an interface watcher event (existing, added, changed, or removed).
1925    async fn handle_interface_watcher_event(
1926        &mut self,
1927        event: fnet_interfaces_ext::EventWithInterest<fnet_interfaces_ext::DefaultInterest>,
1928        watchers: &mut DnsServerWatchers<'_>,
1929        virtualization_handler: &mut impl virtualization::Handler,
1930    ) -> Result<(), anyhow::Error> {
1931        let Self {
1932            interface_properties,
1933            dns_servers,
1934            dns_server_watch_responders,
1935            netpol_networks_service,
1936            interface_states,
1937            lookup_admin,
1938            dhcp_server,
1939            dhcpv4_client_provider,
1940            dhcpv6_client_provider,
1941            route_set_v4_provider,
1942            socket_proxy_state,
1943            dhcpv4_configuration_streams,
1944            dhcpv6_prefixes_streams,
1945            allowed_upstream_device_classes,
1946            dhcpv6_prefix_provider_handler,
1947            ..
1948        } = self;
1949        let update_result = interface_properties
1950            .update(event)
1951            .context("failed to update interface properties with watcher event")?;
1952
1953        // When the underlying interface id for the event represents an
1954        // interface netcfg installed, determine whether the event should
1955        // be ignored given the interface's provisioning policy.
1956        if let Some(id) = match &update_result {
1957            fnet_interfaces_ext::UpdateResult::NoChange => None,
1958            fnet_interfaces_ext::UpdateResult::Existing { properties, state: _ } => {
1959                Some(properties.id)
1960            }
1961            fnet_interfaces_ext::UpdateResult::Added { properties, state: _ } => {
1962                Some(properties.id)
1963            }
1964            fnet_interfaces_ext::UpdateResult::Changed { previous: _, current, state: _ } => {
1965                Some(current.id)
1966            }
1967            fnet_interfaces_ext::UpdateResult::Removed(
1968                fnet_interfaces_ext::PropertiesAndState { properties, state: _ },
1969            ) => Some(properties.id),
1970        } {
1971            if let Some(&InterfaceState { provisioning, .. }) = interface_states.get(&id.into()) {
1972                if provisioning == interface::ProvisioningType::Delegated {
1973                    // Ignore result handling, which prevents provisioning
1974                    // activity from starting such as DHCP and DHCPv6 clients.
1975                    debug!(
1976                        "ignoring interface watcher event because provisioning \
1977                    is delegated for this interface: {:?}",
1978                        &update_result
1979                    );
1980                    return Ok(());
1981                }
1982            }
1983        }
1984
1985        Self::handle_interface_update_result(
1986            &update_result,
1987            watchers,
1988            dns_servers,
1989            dns_server_watch_responders,
1990            netpol_networks_service,
1991            interface_states,
1992            dhcpv4_configuration_streams,
1993            dhcpv6_prefixes_streams,
1994            lookup_admin,
1995            dhcp_server,
1996            dhcpv4_client_provider,
1997            dhcpv6_client_provider,
1998            route_set_v4_provider,
1999            socket_proxy_state,
2000        )
2001        .await
2002        .context("handle interface update")
2003        .or_else(errors::Error::accept_non_fatal)?;
2004
2005        // The interface watcher event may have disabled DHCPv6 on the interface
2006        // so respond accordingly.
2007        dhcpv6::maybe_send_watch_prefix_response(
2008            interface_states,
2009            allowed_upstream_device_classes,
2010            dhcpv6_prefix_provider_handler.as_mut(),
2011        )
2012        .context("maybe send PrefixControl.WatchPrefix response")?;
2013
2014        virtualization_handler
2015            .handle_interface_update_result(&update_result)
2016            .await
2017            .context("handle interface update for virtualization")
2018            .or_else(errors::Error::accept_non_fatal)
2019    }
2020
2021    // This method takes mutable references to several fields of `NetCfg` separately as parameters,
2022    // rather than `&mut self` directly, because `update_result` already holds a reference into
2023    // `self.interface_properties`.
2024    async fn handle_interface_update_result(
2025        update_result: &fnet_interfaces_ext::UpdateResult<
2026            '_,
2027            (),
2028            fnet_interfaces_ext::DefaultInterest,
2029        >,
2030        watchers: &mut DnsServerWatchers<'_>,
2031        dns_servers: &mut DnsServers,
2032        dns_server_watch_responders: &mut dns::DnsServerWatchResponders,
2033        netpol_networks_service: &mut network::NetpolNetworksService,
2034        interface_states: &mut HashMap<InterfaceId, InterfaceState>,
2035        dhcpv4_configuration_streams: &mut dhcpv4::ConfigurationStreamMap,
2036        dhcpv6_prefixes_streams: &mut dhcpv6::PrefixesStreamMap,
2037        lookup_admin: &fnet_name::LookupAdminProxy,
2038        dhcp_server: &Option<fnet_dhcp::Server_Proxy>,
2039        dhcpv4_client_provider: &Option<fnet_dhcp::ClientProviderProxy>,
2040        dhcpv6_client_provider: &Option<fnet_dhcpv6::ClientProviderProxy>,
2041        route_set_v4_provider: &fnet_routes_admin::RouteTableV4Proxy,
2042        socket_proxy_state: &mut Option<SocketProxyState>,
2043    ) -> Result<(), errors::Error> {
2044        match update_result {
2045            fnet_interfaces_ext::UpdateResult::Added { properties, state: _ } => {
2046                match interface_states.get_mut(&properties.id.into()) {
2047                    Some(state) => state
2048                        .on_discovery(
2049                            properties,
2050                            dhcpv4_client_provider.as_ref(),
2051                            dhcpv6_client_provider.as_ref(),
2052                            dhcp_server.as_ref(),
2053                            route_set_v4_provider,
2054                            socket_proxy_state,
2055                            watchers,
2056                            dhcpv4_configuration_streams,
2057                            dhcpv6_prefixes_streams,
2058                        )
2059                        .await
2060                        .context("failed to handle interface added event"),
2061                    // An interface netcfg won't be configuring was added, do nothing.
2062                    None => Ok(()),
2063                }
2064            }
2065            fnet_interfaces_ext::UpdateResult::Existing { properties, state: _ } => {
2066                match interface_states.get_mut(&properties.id.into()) {
2067                    Some(state) => state
2068                        .on_discovery(
2069                            properties,
2070                            dhcpv4_client_provider.as_ref(),
2071                            dhcpv6_client_provider.as_ref(),
2072                            dhcp_server.as_ref(),
2073                            route_set_v4_provider,
2074                            socket_proxy_state,
2075                            watchers,
2076                            dhcpv4_configuration_streams,
2077                            dhcpv6_prefixes_streams,
2078                        )
2079                        .await
2080                        .context("failed to handle existing interface event"),
2081                    // An interface netcfg won't be configuring was discovered, do nothing.
2082                    None => Ok(()),
2083                }
2084            }
2085            fnet_interfaces_ext::UpdateResult::Changed {
2086                previous: previous_properties,
2087                current: current_properties,
2088                state: _,
2089            } => {
2090                let fnet_interfaces::Properties { online: previous_online, .. } =
2091                    previous_properties;
2092                let &fnet_interfaces_ext::Properties { id, name, online, addresses, .. } =
2093                    current_properties;
2094                match interface_states.get_mut(&(*id).into()) {
2095                    // An interface netcfg is not configuring was changed, do nothing.
2096                    None => return Ok(()),
2097                    Some(InterfaceState {
2098                        config:
2099                            InterfaceConfigState::Host(HostInterfaceState {
2100                                dhcpv4_client,
2101                                dhcpv6_client_state,
2102                                dhcpv6_pd_config,
2103                                interface_admin_auth,
2104                                interface_naming_id,
2105                            }),
2106                        control,
2107                        ..
2108                    }) => {
2109                        if previous_online.is_some() {
2110                            Self::handle_dhcpv4_client_update(
2111                                (*id).into(),
2112                                name,
2113                                *online,
2114                                dhcpv4_client,
2115                                dhcpv4_client_provider.as_ref(),
2116                                dhcpv4_configuration_streams,
2117                                dns_servers,
2118                                dns_server_watch_responders,
2119                                netpol_networks_service,
2120                                control,
2121                                lookup_admin,
2122                                route_set_v4_provider,
2123                                interface_admin_auth,
2124                            )
2125                            .await?;
2126                        }
2127
2128                        // When the socket proxy is present, communicate whether the
2129                        // interface has gained or lost candidacy.
2130                        if let Some(state) = socket_proxy_state {
2131                            match socketproxy::determine_interface_state_changed(
2132                                &previous_properties,
2133                                &current_properties,
2134                            ) {
2135                                Some(true) => {
2136                                    info!(
2137                                        "Interface {} (id={}) is now eligible to be \
2138                                        added to the socket-proxy, attempting addition",
2139                                        name, id
2140                                    );
2141                                    state.handle_interface_new_candidate(&current_properties).await
2142                                }
2143                                Some(false) => {
2144                                    info!(
2145                                        "Interface {} (id={}) is no longer eligible to be \
2146                                        in the socket-proxy, attempting removal",
2147                                        name, id
2148                                    );
2149                                    state
2150                                        .handle_interface_no_longer_candidate(InterfaceId(*id))
2151                                        .await
2152                                }
2153                                None => (),
2154                            }
2155                        }
2156
2157                        let dhcpv6_client_provider =
2158                            if let Some(dhcpv6_client_provider) = dhcpv6_client_provider {
2159                                dhcpv6_client_provider
2160                            } else {
2161                                return Ok(());
2162                            };
2163
2164                        if !online {
2165                            // Stop DHCPv6 client if interface went down.
2166                            let dhcpv6::ClientState { sockaddr, prefixes: _ } =
2167                                match dhcpv6_client_state.take() {
2168                                    Some(s) => s,
2169                                    None => return Ok(()),
2170                                };
2171
2172                            info!(
2173                                "host interface {} (id={}) went down \
2174                                so stopping DHCPv6 client w/ sockaddr = {}",
2175                                name,
2176                                id,
2177                                sockaddr.display_ext(),
2178                            );
2179
2180                            return Ok(dhcpv6::stop_client(
2181                                &lookup_admin,
2182                                dns_servers,
2183                                dns_server_watch_responders,
2184                                netpol_networks_service,
2185                                (*id).into(),
2186                                watchers,
2187                                dhcpv6_prefixes_streams,
2188                            )
2189                            .await);
2190                        }
2191
2192                        // Stop the DHCPv6 client if its address can no longer be found on the
2193                        // interface.
2194                        if let Some(dhcpv6::ClientState { sockaddr, prefixes: _ }) =
2195                            dhcpv6_client_state
2196                        {
2197                            let &mut fnet::Ipv6SocketAddress { address, port: _, zone_index: _ } =
2198                                sockaddr;
2199                            if !addresses.iter().any(
2200                                |&fnet_interfaces_ext::Address {
2201                                     addr: fnet::Subnet { addr, prefix_len: _ },
2202                                     valid_until: _,
2203                                     preferred_lifetime_info: _,
2204                                     assignment_state,
2205                                 }| {
2206                                    assert_eq!(
2207                                        assignment_state,
2208                                        fnet_interfaces::AddressAssignmentState::Assigned
2209                                    );
2210                                    addr == fnet::IpAddress::Ipv6(address)
2211                                },
2212                            ) {
2213                                let sockaddr = *sockaddr;
2214                                *dhcpv6_client_state = None;
2215
2216                                info!(
2217                                    "stopping DHCPv6 client on host interface {} (id={}) \
2218                                    w/ removed sockaddr = {}",
2219                                    name,
2220                                    id,
2221                                    sockaddr.display_ext(),
2222                                );
2223
2224                                dhcpv6::stop_client(
2225                                    &lookup_admin,
2226                                    dns_servers,
2227                                    dns_server_watch_responders,
2228                                    netpol_networks_service,
2229                                    (*id).into(),
2230                                    watchers,
2231                                    dhcpv6_prefixes_streams,
2232                                )
2233                                .await;
2234                            }
2235                        }
2236
2237                        // Start a DHCPv6 client if there isn't one.
2238                        if dhcpv6_client_state.is_none() {
2239                            let sockaddr = start_dhcpv6_client(
2240                                current_properties,
2241                                dhcpv6::duid(interface_naming_id.mac),
2242                                &dhcpv6_client_provider,
2243                                dhcpv6_pd_config.clone(),
2244                                watchers,
2245                                dhcpv6_prefixes_streams,
2246                            )?;
2247                            *dhcpv6_client_state = sockaddr.map(dhcpv6::ClientState::new);
2248                        }
2249
2250                        Ok(())
2251                    }
2252                    Some(InterfaceState {
2253                        config:
2254                            InterfaceConfigState::WlanAp(WlanApInterfaceState {
2255                                interface_naming_id: _,
2256                            }),
2257                        ..
2258                    }) => {
2259                        // TODO(https://fxbug.dev/42133555): Stop the DHCP server when the address it is
2260                        // listening on is removed.
2261                        let dhcp_server = if let Some(dhcp_server) = dhcp_server {
2262                            dhcp_server
2263                        } else {
2264                            return Ok(());
2265                        };
2266
2267                        if previous_online
2268                            .map_or(true, |previous_online| previous_online == *online)
2269                        {
2270                            return Ok(());
2271                        }
2272
2273                        if *online {
2274                            info!(
2275                                "WLAN AP interface {} (id={}) came up so starting DHCP server",
2276                                name, id
2277                            );
2278                            dhcpv4::start_server(&dhcp_server)
2279                                .await
2280                                .context("error starting DHCP server")
2281                        } else {
2282                            info!(
2283                                "WLAN AP interface {} (id={}) went down so stopping DHCP server",
2284                                name, id
2285                            );
2286                            dhcpv4::stop_server(&dhcp_server)
2287                                .await
2288                                .context("error stopping DHCP server")
2289                        }
2290                    }
2291                    Some(InterfaceState {
2292                        config: InterfaceConfigState::Blackhole(BlackholeInterfaceState),
2293                        ..
2294                    }) => return Ok(()),
2295                }
2296            }
2297            fnet_interfaces_ext::UpdateResult::Removed(
2298                fnet_interfaces_ext::PropertiesAndState {
2299                    properties: fnet_interfaces_ext::Properties { id, name, .. },
2300                    state: (),
2301                },
2302            ) => {
2303                let network_id = network::NetworkId::fuchsia(*id);
2304                netpol_networks_service
2305                    .update(network::PropertyUpdate::ChangeNetwork(
2306                        network_id,
2307                        network::NetworkUpdate::Remove,
2308                    ))
2309                    .await;
2310                netpol_networks_service.remove_network(network_id).await;
2311                match interface_states.remove(&(*id).into()) {
2312                    // An interface netcfg was not responsible for configuring was removed, do
2313                    // nothing.
2314                    None => Ok(()),
2315                    Some(InterfaceState { config, control, .. }) => {
2316                        // When the socket proxy is present, communicate the
2317                        // removal of the Fuchsia network.
2318                        if let Some(state) = socket_proxy_state {
2319                            state.handle_interface_no_longer_candidate(InterfaceId(*id)).await;
2320                        }
2321                        match config {
2322                            InterfaceConfigState::Host(HostInterfaceState {
2323                                mut dhcpv4_client,
2324                                mut dhcpv6_client_state,
2325                                dhcpv6_pd_config: _,
2326                                interface_admin_auth: _,
2327                                interface_naming_id: _,
2328                            }) => {
2329                                let interface_id: InterfaceId = (*id).into();
2330                                Self::handle_dhcpv4_client_stop(
2331                                    interface_id,
2332                                    name,
2333                                    &mut dhcpv4_client,
2334                                    dhcpv4_configuration_streams,
2335                                    dns_servers,
2336                                    dns_server_watch_responders,
2337                                    netpol_networks_service,
2338                                    &control,
2339                                    lookup_admin,
2340                                    dhcpv4::AlreadyObservedClientExit::No,
2341                                )
2342                                    .await;
2343
2344
2345                                let dhcpv6::ClientState {
2346                                    sockaddr,
2347                                    prefixes: _,
2348                                } = match dhcpv6_client_state.take() {
2349                                    Some(s) => s,
2350                                    None => return Ok(()),
2351                                };
2352
2353                                info!(
2354                                    "host interface {} (id={}) removed \
2355                                    so stopping DHCPv6 client w/ sockaddr = {}",
2356                                    name,
2357                                    id,
2358                                    sockaddr.display_ext()
2359                                );
2360
2361                                dhcpv6::stop_client(
2362                                    &lookup_admin,
2363                                    dns_servers,
2364                                    dns_server_watch_responders,
2365                                    netpol_networks_service,
2366                                    interface_id,
2367                                    watchers,
2368                                    dhcpv6_prefixes_streams,
2369                                )
2370                                .await;
2371
2372                                Ok(dns::remove_rdnss_watcher(
2373                                    &lookup_admin,
2374                                    dns_servers,
2375                                    dns_server_watch_responders,
2376                                    netpol_networks_service,
2377                                    interface_id,
2378                                    watchers,
2379                                )
2380                                .await)
2381                            }
2382                            InterfaceConfigState::WlanAp(WlanApInterfaceState {
2383                                interface_naming_id: _,
2384                            }) => {
2385                                if let Some(dhcp_server) = dhcp_server {
2386                                    // The DHCP server should only run on the WLAN AP interface, so stop it
2387                                    // since the AP interface is removed.
2388                                    info!(
2389                                        "WLAN AP interface {} (id={}) is removed, stopping DHCP server",
2390                                        name, id
2391                                    );
2392                                    dhcpv4::stop_server(&dhcp_server)
2393                                        .await
2394                                        .context("error stopping DHCP server")
2395                                } else {
2396                                    Ok(())
2397                                }
2398                            }
2399                            InterfaceConfigState::Blackhole(BlackholeInterfaceState) => {
2400                                Ok(())
2401                            }
2402                        }
2403                        .context("failed to handle interface removed event")
2404                    }
2405                }
2406            }
2407            fnet_interfaces_ext::UpdateResult::NoChange => Ok(()),
2408        }
2409    }
2410
2411    /// Handle an event from `D`'s device directory.
2412    async fn create_device_stream(
2413        &self,
2414    ) -> Result<
2415        impl futures::Stream<Item = Result<devices::NetworkDeviceInstance, anyhow::Error>> + use<>,
2416        anyhow::Error,
2417    > {
2418        let installer = self.installer.clone();
2419        let directory = fuchsia_fs::directory::open_in_namespace(
2420            devices::NetworkDeviceInstance::PATH,
2421            fio::PERM_READABLE,
2422        )
2423        .with_context(|| format!("error opening netdevice directory"))?;
2424        let stream_of_streams = fvfs_watcher::Watcher::new(&directory)
2425            .await
2426            .with_context(|| {
2427                format!("creating watcher for {}", devices::NetworkDeviceInstance::PATH)
2428            })?
2429            .err_into()
2430            .try_filter_map(move |fvfs_watcher::WatchMessage { event, filename }| {
2431                let installer = installer.clone();
2432                async move {
2433                    trace!("got {:?} event for {}", event, filename.display());
2434
2435                    if filename.to_str() == Some(THIS_DIRECTORY) {
2436                        debug!("skipping device w/ filename = {}", filename.display());
2437                        return Ok(None);
2438                    }
2439
2440                    match event {
2441                        fvfs_watcher::WatchEvent::ADD_FILE | fvfs_watcher::WatchEvent::EXISTING => {
2442                            let filepath = path::Path::new(devices::NetworkDeviceInstance::PATH)
2443                                .join(filename);
2444                            info!("found new network device at {:?}", filepath);
2445                            match devices::NetworkDeviceInstance::get_instance_stream(
2446                                &installer, &filepath,
2447                            )
2448                            .await
2449                            .context("create instance stream")
2450                            {
2451                                Ok(stream) => Ok(Some(
2452                                    stream
2453                                        .filter_map(move |r| {
2454                                            futures::future::ready(match r {
2455                                                Ok(instance) => Some(Ok(instance)),
2456                                                Err(errors::Error::NonFatal(nonfatal)) => {
2457                                                    error!(
2458                                        "non-fatal error operating device stream for {:?}: {:?}",
2459                                        filepath,
2460                                        nonfatal
2461                                    );
2462                                                    None
2463                                                }
2464                                                Err(errors::Error::Fatal(fatal)) => {
2465                                                    Some(Err(fatal))
2466                                                }
2467                                            })
2468                                        })
2469                                        // Need to box the stream to combine it
2470                                        // with flatten_unordered because it's
2471                                        // not Unpin.
2472                                        .boxed(),
2473                                )),
2474                                Err(errors::Error::NonFatal(nonfatal)) => {
2475                                    error!(
2476                                        "non-fatal error fetching device stream for {:?}: {:?}",
2477                                        filepath, nonfatal
2478                                    );
2479                                    Ok(None)
2480                                }
2481                                Err(errors::Error::Fatal(fatal)) => Err(fatal),
2482                            }
2483                        }
2484                        fvfs_watcher::WatchEvent::IDLE | fvfs_watcher::WatchEvent::REMOVE_FILE => {
2485                            Ok(None)
2486                        }
2487                        event => Err(anyhow::anyhow!(
2488                            "unrecognized event {:?} for device filename {}",
2489                            event,
2490                            filename.display()
2491                        )),
2492                    }
2493                }
2494            })
2495            .fuse()
2496            .try_flatten_unordered(None);
2497        Ok(stream_of_streams)
2498    }
2499
2500    async fn handle_device_instance(
2501        &mut self,
2502        instance: devices::NetworkDeviceInstance,
2503        dns_watchers: &mut DnsServerWatchers<'_>,
2504    ) -> Result<(), anyhow::Error> {
2505        // Produce the identifier for the device to determine if it is already
2506        // known to Netcfg.
2507        let interface_naming_id =
2508            match self.get_interface_naming_identifier_for_instance(&instance).await {
2509                Ok(id) => id,
2510                Err(e) => {
2511                    error!("non-fatal error getting interface naming id {instance:?}: {e:?}");
2512
2513                    return Ok(());
2514                }
2515            };
2516
2517        // TODO(https://fxbug.dev/42086636): Add metrics/inspect data for devices
2518        // that encounter the situations below
2519        // Check if this device is known to Netcfg.
2520        // The device creation process removes an existing interface with
2521        // the same InterfaceNamingIdentifier. Therefore, two interfaces cannot
2522        // exist concurrently with the same InterfaceNamingIdentifier. This
2523        // should result in 0 or 1 interface to exist with the
2524        // provided identifier.
2525        match self.interface_states.iter().find_map(|(_id, state)| {
2526            // A device with the same InterfaceNamingId is considered to be
2527            // the same logical interface.
2528            if state.interface_naming_id() == Some(&interface_naming_id) {
2529                Some(state)
2530            } else {
2531                None
2532            }
2533        }) {
2534            // If Netcfg knows about the device already, then it's likely
2535            // flapped and we should uninstall the existing interface prior
2536            // to adding the new one.
2537            Some(interface_state @ InterfaceState { control, .. }) => {
2538                let interface_naming_id = interface_state.interface_naming_id();
2539                warn!(
2540                    "interface likely flapped. attempting to remove interface with \
2541                    interface naming id ({interface_naming_id:?}) prior to adding \
2542                    instance: {instance:?}"
2543                );
2544                match control.remove().await {
2545                    Ok(Ok(())) => {
2546                        // Successfully removed interface.
2547                        // It may have been that Netstack responded to the
2548                        // `remove()` request or Netstack noticed the flap
2549                        // and removed the interface on its own.
2550                        info!(
2551                            "interface removed due to reason: {:?}",
2552                            control.clone().wait_termination().await
2553                        );
2554                    }
2555                    Ok(Err(e)) => {
2556                        panic!("expected to be able to call remove on this interface: {e:?}")
2557                    }
2558                    Err(e) => {
2559                        // Do nothing. The interface was already removed.
2560                        info!("interface was already removed prior to calling `remove()`: {e:?}");
2561                    }
2562                }
2563            }
2564            None => {
2565                // Do nothing. The device is not known to Netcfg so
2566                // we should attempt to add the device.
2567            }
2568        }
2569
2570        match self.add_new_device(&instance, dns_watchers).await {
2571            Ok(()) => {
2572                return Ok(());
2573            }
2574            Err(devices::AddDeviceError::AlreadyExists(name)) => {
2575                // Either the interface with this name was not installed
2576                // by Netcfg or another device installed by Netcfg used
2577                // the same name already. We will reject interface
2578                // installation.
2579                error!(
2580                    "interface with name ({name}) is already present in the Netstack, \
2581                    and the device was either not installed by Netcfg or a different \
2582                    device installed by Netcfg used the same name. rejecting \
2583                    installation for instance: {instance:?}"
2584                );
2585                return Ok(());
2586            }
2587            Err(devices::AddDeviceError::Other(errors::Error::NonFatal(e))) => {
2588                error!("non-fatal error adding device {:?}: {:?}", instance, e);
2589
2590                return Ok(());
2591            }
2592            Err(devices::AddDeviceError::Other(errors::Error::Fatal(e))) => {
2593                return Err(e.context(format!("error adding new device {:?}", instance)));
2594            }
2595        }
2596    }
2597
2598    /// Add a blackhole interface to the netstack.
2599    async fn add_blackhole_interface(&mut self, name: &str) -> Result<(), devices::AddDeviceError> {
2600        let (control, control_server_end) =
2601            fidl_fuchsia_net_interfaces_ext::admin::Control::create_endpoints()
2602                .context("create Control proxy")
2603                .map_err(errors::Error::NonFatal)?;
2604        let Metric(metric) = self.interface_metrics.blackhole_metric;
2605        self.installer
2606            .install_blackhole_interface(
2607                control_server_end,
2608                fnet_interfaces_admin::Options {
2609                    name: Some(name.to_owned()),
2610                    metric: Some(metric),
2611                    netstack_managed_routes_designation: None,
2612                    __source_breaking: fidl::marker::SourceBreaking,
2613                },
2614            )
2615            .context("error installing blackhole interface")
2616            .map_err(errors::Error::NonFatal)?;
2617        let interface_id = control.get_id().await.map_err(|err| {
2618            let other = match err {
2619                fidl_fuchsia_net_interfaces_ext::admin::TerminalError::Fidl(err) => err.into(),
2620                fidl_fuchsia_net_interfaces_ext::admin::TerminalError::Terminal(terminal_error) => {
2621                    match terminal_error {
2622                        fidl_fuchsia_net_interfaces_admin::InterfaceRemovedReason::DuplicateName => {
2623                            return devices::AddDeviceError::AlreadyExists(name.to_owned());
2624                        }
2625                        reason => {
2626                            anyhow::anyhow!("received terminal event {:?}", reason)
2627                        }
2628                    }
2629                }
2630            };
2631            devices::AddDeviceError::Other(
2632                errors::Error::NonFatal(other).context("calling Control get_id"),
2633            )
2634        })?;
2635        let _did_enable: bool = control
2636            .enable()
2637            .await
2638            .map_err(map_control_error("error sending enable request"))
2639            .and_then(|res| {
2640                // ControlEnableError is an empty *flexible* enum, so we can't match on it, but
2641                // the operation is infallible at the time of writing.
2642                res.map_err(|e: fidl_fuchsia_net_interfaces_admin::ControlEnableError| {
2643                    errors::Error::Fatal(anyhow::anyhow!("enable interface: {:?}", e))
2644                })
2645            })?;
2646        let interface_state =
2647            InterfaceState::new_blackhole(control, interface::ProvisioningType::Local);
2648
2649        assert_matches!(
2650            self.interface_states
2651                .insert(InterfaceId::new(interface_id).expect("must be nonzero"), interface_state),
2652            None
2653        );
2654
2655        info!("installed blackhole interface (id={} name={})", interface_id, name);
2656
2657        Ok(())
2658    }
2659
2660    /// Add a device at `filepath` to the netstack.
2661    async fn add_new_device(
2662        &mut self,
2663        device_instance: &devices::NetworkDeviceInstance,
2664        dns_watchers: &mut DnsServerWatchers<'_>,
2665    ) -> Result<(), devices::AddDeviceError> {
2666        let device_info =
2667            device_instance.get_device_info().await.context("error getting device info and MAC")?;
2668
2669        let DeviceInfo { mac, port_class, topological_path } = &device_info;
2670
2671        let mac = mac.ok_or_else(|| {
2672            warn!("devices without mac address not supported yet");
2673            devices::AddDeviceError::Other(errors::Error::NonFatal(anyhow::anyhow!(
2674                "device without mac not supported"
2675            )))
2676        })?;
2677
2678        let device_class =
2679            DeviceClass::try_from(*port_class).map_err(|e: UnknownPortClassError| {
2680                devices::AddDeviceError::Other(errors::Error::NonFatal(anyhow::Error::new(e)))
2681            })?;
2682        let device_info =
2683            DeviceInfoRef { device_class, mac: &mac, topological_path: &topological_path };
2684
2685        let interface_type = device_info.interface_type();
2686        let metric = match interface_type {
2687            InterfaceType::WlanClient | InterfaceType::WlanAp => self.interface_metrics.wlan_metric,
2688            InterfaceType::Ethernet => self.interface_metrics.eth_metric,
2689            InterfaceType::Blackhole => self.interface_metrics.blackhole_metric,
2690        }
2691        .into();
2692        let (interface_name, interface_naming_id) = match self
2693            .interface_naming_config
2694            .generate_stable_name(&topological_path, &mac, device_class)
2695        {
2696            Ok((name, interface_naming_id)) => (name.to_string(), interface_naming_id),
2697            Err(interface::NameGenerationError::GenerationError(e)) => {
2698                return Err(devices::AddDeviceError::Other(errors::Error::Fatal(
2699                    e.context("error getting stable name"),
2700                )));
2701            }
2702        };
2703
2704        info!(
2705            "adding {device_instance:?} to stack with name = {interface_name} and \
2706        naming id = {interface_naming_id:?}"
2707        );
2708
2709        let provisioning_action = interface::find_provisioning_action_from_provisioning_rules(
2710            &self.interface_provisioning_policy,
2711            &device_info,
2712            &interface_name,
2713        );
2714        info!(
2715            "interface with name {:?} will have {:?} provisioning",
2716            &interface_name, provisioning_action
2717        );
2718
2719        let ProvisioningAction { provisioning, netstack_managed_routes_designation } =
2720            provisioning_action;
2721
2722        let (interface_id, control) = device_instance
2723            .add_to_stack(
2724                self,
2725                InterfaceConfig {
2726                    name: interface_name.clone(),
2727                    metric,
2728                    netstack_managed_routes_designation,
2729                },
2730            )
2731            .await
2732            .context("error adding to stack")?;
2733
2734        self.configure_eth_interface(
2735            interface_id.try_into().expect("interface ID should be nonzero"),
2736            control,
2737            interface_name,
2738            interface_naming_id,
2739            &device_info,
2740            dns_watchers,
2741            provisioning,
2742        )
2743        .await
2744        .context("error configuring ethernet interface")
2745        .map_err(devices::AddDeviceError::Other)
2746    }
2747
2748    /// Configure an ethernet interface.
2749    ///
2750    /// If the device is a WLAN AP, it will be configured as a WLAN AP (see
2751    /// `configure_wlan_ap_and_dhcp_server` for more details). Otherwise, it
2752    /// will be configured as a host (see `configure_host` for more details).
2753    async fn configure_eth_interface(
2754        &mut self,
2755        interface_id: InterfaceId,
2756        control: fidl_fuchsia_net_interfaces_ext::admin::Control,
2757        interface_name: String,
2758        interface_naming_id: interface::InterfaceNamingIdentifier,
2759        device_info: &DeviceInfoRef<'_>,
2760        dns_watchers: &mut DnsServerWatchers<'_>,
2761        provisioning_type: ProvisioningType,
2762    ) -> Result<(), errors::Error> {
2763        let ForwardedDeviceClasses { ipv4, ipv6 } = &self.forwarded_device_classes;
2764        let ipv4_forwarding = ipv4.contains(&device_info.device_class);
2765        let ipv6_forwarding = ipv6.contains(&device_info.device_class);
2766        let config: fnet_interfaces_admin::Configuration = control
2767            .set_configuration(&fnet_interfaces_admin::Configuration {
2768                ipv6: Some(fnet_interfaces_admin::Ipv6Configuration {
2769                    unicast_forwarding: Some(ipv6_forwarding),
2770                    multicast_forwarding: Some(ipv6_forwarding),
2771                    ..Default::default()
2772                }),
2773                ipv4: Some(fnet_interfaces_admin::Ipv4Configuration {
2774                    unicast_forwarding: Some(ipv4_forwarding),
2775                    multicast_forwarding: Some(ipv4_forwarding),
2776                    ..Default::default()
2777                }),
2778                ..Default::default()
2779            })
2780            .await
2781            .map_err(map_control_error("setting configuration"))
2782            .and_then(|res| {
2783                res.map_err(|e: fnet_interfaces_admin::ControlSetConfigurationError| {
2784                    errors::Error::Fatal(anyhow::anyhow!("{:?}", e))
2785                })
2786            })?;
2787        info!("installed configuration with result {:?}", config);
2788
2789        if device_info.is_wlan_ap() {
2790            if let Some(id) = self
2791                .interface_states
2792                .iter()
2793                .find_map(|(id, state)| if state.is_wlan_ap() { Some(id) } else { None })
2794            {
2795                return Err(errors::Error::NonFatal(anyhow::anyhow!(
2796                    "multiple WLAN AP interfaces are not supported, \
2797                        have WLAN AP interface with id = {}",
2798                    id
2799                )));
2800            }
2801            self.netpol_networks_service
2802                .update(PropertyUpdate::ChangeNetwork(
2803                    network::NetworkId::fuchsia(interface_id),
2804                    network::NetworkUpdate::Properties(network::NetworkPropertiesChange {
2805                        added: true,
2806                        marks: None,
2807                    }),
2808                ))
2809                .await;
2810            let InterfaceState { control, .. } = match self.interface_states.entry(interface_id) {
2811                Entry::Occupied(entry) => {
2812                    panic!(
2813                        "multiple interfaces with the same ID = {}; \
2814                                attempting to add state for a WLAN AP, existing state = {:?}",
2815                        entry.key(),
2816                        entry.get(),
2817                    );
2818                }
2819                Entry::Vacant(entry) => entry.insert(InterfaceState::new_wlan_ap(
2820                    interface_naming_id,
2821                    control,
2822                    device_info.device_class,
2823                    provisioning_type,
2824                )),
2825            };
2826
2827            info!("discovered WLAN AP (interface ID={})", interface_id);
2828
2829            if let Some(dhcp_server) = &self.dhcp_server {
2830                info!("configuring DHCP server for WLAN AP (interface ID={})", interface_id);
2831                Self::configure_wlan_ap_and_dhcp_server(
2832                    &mut self.filter_enabled_state,
2833                    &mut self.filter_control,
2834                    interface_id,
2835                    dhcp_server,
2836                    control,
2837                    interface_name,
2838                    device_info,
2839                )
2840                .await
2841                .context("error configuring wlan ap and dhcp server")?;
2842            } else {
2843                warn!(
2844                    "cannot configure DHCP server for WLAN AP (interface ID={}) \
2845                        since DHCP server service is not available",
2846                    interface_id
2847                );
2848            }
2849        } else {
2850            let InterfaceState { control, .. } = match self.interface_states.entry(interface_id) {
2851                Entry::Occupied(entry) => {
2852                    panic!(
2853                        "multiple interfaces with the same ID = {}; \
2854                            attempting to add state for a host, existing state = {:?}",
2855                        entry.key(),
2856                        entry.get()
2857                    );
2858                }
2859                Entry::Vacant(entry) => {
2860                    self.netpol_networks_service
2861                        .update(PropertyUpdate::ChangeNetwork(
2862                            network::NetworkId::fuchsia(interface_id),
2863                            network::NetworkUpdate::Properties(network::NetworkPropertiesChange {
2864                                added: true,
2865                                marks: None,
2866                            }),
2867                        ))
2868                        .await;
2869                    let dhcpv6_pd_config = if !self
2870                        .allowed_upstream_device_classes
2871                        .contains(&device_info.device_class)
2872                    {
2873                        None
2874                    } else {
2875                        self.dhcpv6_prefix_provider_handler.as_ref().map(
2876                            |dhcpv6::PrefixProviderHandler {
2877                                 preferred_prefix_len,
2878                                 prefix_control_request_stream: _,
2879                                 watch_prefix_responder: _,
2880                                 interface_config: _,
2881                                 current_prefix: _,
2882                             }| {
2883                                preferred_prefix_len.map_or(
2884                                    fnet_dhcpv6::PrefixDelegationConfig::Empty(fnet_dhcpv6::Empty),
2885                                    |preferred_prefix_len| {
2886                                        fnet_dhcpv6::PrefixDelegationConfig::PrefixLength(
2887                                            preferred_prefix_len,
2888                                        )
2889                                    },
2890                                )
2891                            },
2892                        )
2893                    };
2894                    entry.insert(
2895                        InterfaceState::new_host(
2896                            interface_naming_id,
2897                            control,
2898                            device_info.device_class,
2899                            dhcpv6_pd_config,
2900                            provisioning_type,
2901                        )
2902                        .await?,
2903                    )
2904                }
2905            };
2906
2907            info!("discovered host interface with id={}, configuring interface", interface_id);
2908
2909            Self::configure_host(
2910                &mut self.filter_enabled_state,
2911                &mut self.filter_control,
2912                &self.stack,
2913                interface_id,
2914                device_info,
2915                // Disable in-stack DHCPv4 when provisioning is ignored.
2916                self.dhcpv4_client_provider.is_none()
2917                    && provisioning_type == interface::ProvisioningType::Local,
2918            )
2919            .await
2920            .context("error configuring host")?;
2921
2922            if let Some(watcher_provider) = &self.route_advertisement_watcher_provider {
2923                dns::add_rdnss_watcher(&watcher_provider, interface_id, dns_watchers)
2924                    .await
2925                    .map_err(errors::Error::NonFatal)?;
2926            }
2927
2928            if self.socket_proxy_state.is_some()
2929                && provisioning_type == interface::ProvisioningType::Local
2930            {
2931                if self.locally_provisioned_network_rule_set.is_none() {
2932                    self.locally_provisioned_network_rule_set = futures::try_join!(
2933                        install_locally_provisioned_network_rule_set::<Ipv4>(),
2934                        install_locally_provisioned_network_rule_set::<Ipv6>(),
2935                    )
2936                    .inspect_err(|err| {
2937                        log::error!(
2938                            "failed to create route rules for locally provisioined networks: {:?}",
2939                            err
2940                        );
2941                    })
2942                    .ok();
2943                }
2944            }
2945
2946            let _did_enable: bool = control
2947                .enable()
2948                .await
2949                .map_err(map_control_error("error sending enable request"))
2950                .and_then(|res| {
2951                    // ControlEnableError is an empty *flexible* enum, so we can't match on it, but
2952                    // the operation is infallible at the time of writing.
2953                    res.map_err(|e: fidl_fuchsia_net_interfaces_admin::ControlEnableError| {
2954                        errors::Error::Fatal(anyhow::anyhow!("enable interface: {:?}", e))
2955                    })
2956                })?;
2957        }
2958
2959        Ok(())
2960    }
2961
2962    /// Configure host interface.
2963    async fn configure_host(
2964        filter_enabled_state: &mut FilterEnabledState,
2965        filter_control: &mut FilterControl,
2966        stack: &fnet_stack::StackProxy,
2967        interface_id: InterfaceId,
2968        device_info: &DeviceInfoRef<'_>,
2969        start_in_stack_dhcpv4: bool,
2970    ) -> Result<(), errors::Error> {
2971        // Handle on-demand interface enabling by using either implementation of Filter.
2972        filter_enabled_state
2973            .maybe_update(Some(device_info.interface_type()), interface_id, filter_control)
2974            .await
2975            .map_err(|e| {
2976                anyhow::anyhow!("failed to update filter on nic {interface_id} with error = {e:?}")
2977            })
2978            .map_err(errors::Error::NonFatal)?;
2979
2980        // Enable DHCP.
2981        if start_in_stack_dhcpv4 {
2982            stack
2983                .set_dhcp_client_enabled(interface_id.get(), true)
2984                .await
2985                .unwrap_or_else(|err| exit_with_fidl_error(err))
2986                .map_err(|e| anyhow!("failed to start dhcp client: {:?}", e))
2987                .map_err(errors::Error::NonFatal)?;
2988        }
2989
2990        Ok(())
2991    }
2992
2993    /// Configure the WLAN AP and the DHCP server to serve requests on its network.
2994    ///
2995    /// Note, this method will not start the DHCP server, it will only be configured
2996    /// with the parameters so it is ready to be started when an interface UP event
2997    /// is received for the WLAN AP.
2998    async fn configure_wlan_ap_and_dhcp_server(
2999        filter_enabled_state: &mut FilterEnabledState,
3000        filter_control: &mut FilterControl,
3001        interface_id: InterfaceId,
3002        dhcp_server: &fnet_dhcp::Server_Proxy,
3003        control: &fidl_fuchsia_net_interfaces_ext::admin::Control,
3004        name: String,
3005        device_info: &DeviceInfoRef<'_>,
3006    ) -> Result<(), errors::Error> {
3007        let (address_state_provider, server_end) = fidl::endpoints::create_proxy::<
3008            fidl_fuchsia_net_interfaces_admin::AddressStateProviderMarker,
3009        >();
3010
3011        // Handle on-demand interface enabling by using either implementation of Filter.
3012        filter_enabled_state
3013            .maybe_update(Some(device_info.interface_type()), interface_id, filter_control)
3014            .await
3015            .map_err(|e| {
3016                anyhow::anyhow!("failed to update filter on nic {interface_id} with error = {e:?}")
3017            })
3018            .map_err(errors::Error::NonFatal)?;
3019
3020        // Calculate and set the interface address based on the network address.
3021        // The interface address should be the first available address.
3022        let network_addr_as_u32 = u32::from_be_bytes(WLAN_AP_NETWORK_ADDR.addr);
3023        let interface_addr_as_u32 = network_addr_as_u32 + 1;
3024        let ipv4 = fnet::Ipv4Address { addr: interface_addr_as_u32.to_be_bytes() };
3025        let addr = fidl_fuchsia_net::Subnet {
3026            addr: fnet::IpAddress::Ipv4(ipv4.clone()),
3027            prefix_len: WLAN_AP_PREFIX_LEN.get(),
3028        };
3029
3030        control
3031            .add_address(
3032                &addr,
3033                &fidl_fuchsia_net_interfaces_admin::AddressParameters {
3034                    add_subnet_route: Some(true),
3035                    ..Default::default()
3036                },
3037                server_end,
3038            )
3039            .map_err(map_control_error("error sending add address request"))?;
3040
3041        // Allow the address to outlive this scope. At the time of writing its lifetime is
3042        // identical to the interface's lifetime and no updates to its properties are made. We may
3043        // wish to retain the handle in the future to allow external address removal (e.g. by a
3044        // user) to be observed so that an error can be emitted (as such removal would break a
3045        // critical user journey).
3046        address_state_provider
3047            .detach()
3048            .map_err(Into::into)
3049            .map_err(map_address_state_provider_error("error sending detach request"))?;
3050
3051        // Enable the interface to allow DAD to proceed.
3052        let _did_enable: bool = control
3053            .enable()
3054            .await
3055            .map_err(map_control_error("error sending enable request"))
3056            .and_then(|res| {
3057                // ControlEnableError is an empty *flexible* enum, so we can't match on it, but the
3058                // operation is infallible at the time of writing.
3059                res.map_err(|e: fidl_fuchsia_net_interfaces_admin::ControlEnableError| {
3060                    errors::Error::Fatal(anyhow::anyhow!("enable interface: {:?}", e))
3061                })
3062            })?;
3063
3064        let state_stream =
3065            fidl_fuchsia_net_interfaces_ext::admin::assignment_state_stream(address_state_provider);
3066        let mut state_stream = pin!(state_stream);
3067        fidl_fuchsia_net_interfaces_ext::admin::wait_assignment_state(
3068            &mut state_stream,
3069            fidl_fuchsia_net_interfaces::AddressAssignmentState::Assigned,
3070        )
3071        .await
3072        .map_err(map_address_state_provider_error("failed to add interface address for WLAN AP"))?;
3073
3074        // First we clear any leases that the server knows about since the server
3075        // will be used on a new interface. If leases exist, configuring the DHCP
3076        // server parameters may fail (AddressPool).
3077        debug!("clearing DHCP leases");
3078        dhcp_server
3079            .clear_leases()
3080            .await
3081            .context("error sending clear DHCP leases request")
3082            .map_err(errors::Error::NonFatal)?
3083            .map_err(zx::Status::from_raw)
3084            .context("error clearing DHCP leases request")
3085            .map_err(errors::Error::NonFatal)?;
3086
3087        // Configure the DHCP server.
3088        let v = vec![ipv4];
3089        debug!("setting DHCP IpAddrs parameter to {:?}", v);
3090        dhcp_server
3091            .set_parameter(&fnet_dhcp::Parameter::IpAddrs(v))
3092            .await
3093            .context("error sending set DHCP IpAddrs parameter request")
3094            .map_err(errors::Error::NonFatal)?
3095            .map_err(zx::Status::from_raw)
3096            .context("error setting DHCP IpAddrs parameter")
3097            .map_err(errors::Error::NonFatal)?;
3098
3099        let v = vec![name];
3100        debug!("setting DHCP BoundDeviceNames parameter to {:?}", v);
3101        dhcp_server
3102            .set_parameter(&fnet_dhcp::Parameter::BoundDeviceNames(v))
3103            .await
3104            .context("error sending set DHCP BoundDeviceName parameter request")
3105            .map_err(errors::Error::NonFatal)?
3106            .map_err(zx::Status::from_raw)
3107            .context("error setting DHCP BoundDeviceNames parameter")
3108            .map_err(errors::Error::NonFatal)?;
3109
3110        let v = fnet_dhcp::LeaseLength {
3111            default: Some(WLAN_AP_DHCP_LEASE_TIME_SECONDS),
3112            max: Some(WLAN_AP_DHCP_LEASE_TIME_SECONDS),
3113            ..Default::default()
3114        };
3115        debug!("setting DHCP LeaseLength parameter to {:?}", v);
3116        dhcp_server
3117            .set_parameter(&fnet_dhcp::Parameter::Lease(v))
3118            .await
3119            .context("error sending set DHCP LeaseLength parameter request")
3120            .map_err(errors::Error::NonFatal)?
3121            .map_err(zx::Status::from_raw)
3122            .context("error setting DHCP LeaseLength parameter")
3123            .map_err(errors::Error::NonFatal)?;
3124
3125        let host_mask = ::dhcpv4::configuration::SubnetMask::new(WLAN_AP_PREFIX_LEN);
3126        let broadcast_addr =
3127            host_mask.broadcast_of(&std::net::Ipv4Addr::from_fidl(WLAN_AP_NETWORK_ADDR));
3128        let broadcast_addr_as_u32: u32 = broadcast_addr.into();
3129        // The start address of the DHCP pool should be the first address after the WLAN AP
3130        // interface address.
3131        let dhcp_pool_start =
3132            fnet::Ipv4Address { addr: (interface_addr_as_u32 + 1).to_be_bytes() }.into();
3133        // The last address of the DHCP pool should be the last available address
3134        // in the interface's network. This is the address immediately before the
3135        // network's broadcast address.
3136        let dhcp_pool_end = fnet::Ipv4Address { addr: (broadcast_addr_as_u32 - 1).to_be_bytes() };
3137
3138        let v = fnet_dhcp::AddressPool {
3139            prefix_length: Some(WLAN_AP_PREFIX_LEN.get()),
3140            range_start: Some(dhcp_pool_start),
3141            range_stop: Some(dhcp_pool_end),
3142            ..Default::default()
3143        };
3144        debug!("setting DHCP AddressPool parameter to {:?}", v);
3145        dhcp_server
3146            .set_parameter(&fnet_dhcp::Parameter::AddressPool(v))
3147            .await
3148            .context("error sending set DHCP AddressPool parameter request")
3149            .map_err(errors::Error::NonFatal)?
3150            .map_err(zx::Status::from_raw)
3151            .context("error setting DHCP AddressPool parameter")
3152            .map_err(errors::Error::NonFatal)
3153    }
3154
3155    async fn handle_dhcpv6_acquire_prefix(
3156        &mut self,
3157        fnet_dhcpv6::AcquirePrefixConfig {
3158            interface_id,
3159            preferred_prefix_len,
3160            ..
3161        }: fnet_dhcpv6::AcquirePrefixConfig,
3162        prefix: fidl::endpoints::ServerEnd<fnet_dhcpv6::PrefixControlMarker>,
3163        dns_watchers: &mut DnsServerWatchers<'_>,
3164    ) -> Result<(), errors::Error> {
3165        let (prefix_control_request_stream, control_handle) =
3166            prefix.into_stream_and_control_handle();
3167
3168        let dhcpv6_client_provider = if let Some(s) = self.dhcpv6_client_provider.as_ref() {
3169            s
3170        } else {
3171            warn!(
3172                "Attempted to acquire prefix when DHCPv6 is not \
3173                supported; interface_id={:?}, preferred_prefix_len={:?}",
3174                interface_id, preferred_prefix_len,
3175            );
3176            return control_handle
3177                .send_on_exit(fnet_dhcpv6::PrefixControlExitReason::NotSupported)
3178                .context("failed to send NotSupported terminal event")
3179                .map_err(errors::Error::NonFatal);
3180        };
3181
3182        let interface_config = if let Some(interface_id) = interface_id {
3183            match self
3184                .interface_states
3185                .get(&interface_id.try_into().expect("interface ID should be nonzero"))
3186            {
3187                None => {
3188                    warn!(
3189                        "Attempted to acquire a prefix on unmanaged interface; \
3190                        id={:?}, preferred_prefix_len={:?}",
3191                        interface_id, preferred_prefix_len,
3192                    );
3193                    return control_handle
3194                        .send_on_exit(fnet_dhcpv6::PrefixControlExitReason::InvalidInterface)
3195                        .context("failed to send InvalidInterface terminal event")
3196                        .map_err(errors::Error::NonFatal);
3197                }
3198                Some(InterfaceState { config: InterfaceConfigState::WlanAp(_), .. }) => {
3199                    warn!(
3200                        "Attempted to acquire a prefix on AP interface; \
3201                        id={:?}, preferred_prefix_len={:?}",
3202                        interface_id, preferred_prefix_len,
3203                    );
3204                    return control_handle
3205                        .send_on_exit(fnet_dhcpv6::PrefixControlExitReason::InvalidInterface)
3206                        .context("failed to send InvalidInterface terminal event")
3207                        .map_err(errors::Error::NonFatal);
3208                }
3209                Some(InterfaceState { config: InterfaceConfigState::Blackhole(_), .. }) => {
3210                    warn!(
3211                        "Attempted to acquire a prefix on blackhole interface; \
3212                        id={:?}, preferred_prefix_len={:?}",
3213                        interface_id, preferred_prefix_len,
3214                    );
3215                    return control_handle
3216                        .send_on_exit(fnet_dhcpv6::PrefixControlExitReason::InvalidInterface)
3217                        .context("failed to send InvalidInterface terminal event")
3218                        .map_err(errors::Error::NonFatal);
3219                }
3220                Some(InterfaceState {
3221                    config:
3222                        InterfaceConfigState::Host(HostInterfaceState {
3223                            dhcpv4_client: _,
3224                            dhcpv6_client_state: _,
3225                            dhcpv6_pd_config: _,
3226                            interface_admin_auth: _,
3227                            interface_naming_id: _,
3228                        }),
3229                    ..
3230                }) => dhcpv6::AcquirePrefixInterfaceConfig::Id(interface_id),
3231            }
3232        } else {
3233            dhcpv6::AcquirePrefixInterfaceConfig::Upstreams
3234        };
3235        let pd_config = if let Some(preferred_prefix_len) = preferred_prefix_len {
3236            if preferred_prefix_len > net_types::ip::Ipv6Addr::BYTES * 8 {
3237                warn!(
3238                    "Preferred prefix length exceeds bits in IPv6 address; \
3239                    interface_id={:?}, preferred_prefix_len={:?}",
3240                    interface_id, preferred_prefix_len,
3241                );
3242                return control_handle
3243                    .send_on_exit(fnet_dhcpv6::PrefixControlExitReason::InvalidPrefixLength)
3244                    .context("failed to send InvalidPrefixLength terminal event")
3245                    .map_err(errors::Error::NonFatal);
3246            }
3247            fnet_dhcpv6::PrefixDelegationConfig::PrefixLength(preferred_prefix_len)
3248        } else {
3249            fnet_dhcpv6::PrefixDelegationConfig::Empty(fnet_dhcpv6::Empty)
3250        };
3251
3252        // TODO(https://fxbug.dev/142065403): Support multiple clients asking
3253        // for IPv6 prefixes.
3254        if self.dhcpv6_prefix_provider_handler.is_some() {
3255            warn!(
3256                "Attempted to acquire a prefix while a prefix is already being acquired for \
3257                another iface; interface_id={:?}, preferred_prefix_len={:?}",
3258                interface_id, preferred_prefix_len,
3259            );
3260            return control_handle
3261                .send_on_exit(fnet_dhcpv6::PrefixControlExitReason::AlreadyAcquiring)
3262                .context("failed to send AlreadyAcquiring terminal event")
3263                .map_err(errors::Error::NonFatal);
3264        }
3265
3266        let interface_state_iter = match interface_config {
3267            dhcpv6::AcquirePrefixInterfaceConfig::Id(want_id) => {
3268                let want_id = want_id.try_into().expect("interface ID should be nonzero");
3269                either::Either::Left(std::iter::once((
3270                    want_id,
3271                    self.interface_states
3272                        .get_mut(&want_id)
3273                        .unwrap_or_else(|| panic!("interface {} state not present", want_id)),
3274                )))
3275            }
3276            dhcpv6::AcquirePrefixInterfaceConfig::Upstreams => either::Either::Right(
3277                self.interface_states.iter_mut().filter_map(|(id, if_state)| {
3278                    self.allowed_upstream_device_classes
3279                        .contains(&if_state.device_class)
3280                        .then_some((*id, if_state))
3281                }),
3282            ),
3283        };
3284        // Look for all eligible interfaces and start/restart DHCPv6 client as needed.
3285        for (id, InterfaceState { config, .. }) in interface_state_iter {
3286            let HostInterfaceState {
3287                dhcpv4_client: _,
3288                dhcpv6_client_state,
3289                dhcpv6_pd_config,
3290                interface_admin_auth: _,
3291                interface_naming_id,
3292            } = match config {
3293                InterfaceConfigState::Host(state) => state,
3294                InterfaceConfigState::WlanAp(WlanApInterfaceState { interface_naming_id: _ })
3295                | InterfaceConfigState::Blackhole(_) => {
3296                    continue;
3297                }
3298            };
3299
3300            // Save the config so that future DHCPv6 clients are started with PD.
3301            *dhcpv6_pd_config = Some(pd_config.clone());
3302
3303            let fnet_interfaces_ext::PropertiesAndState { properties, state: _ } =
3304                if let Some(properties) = self.interface_properties.get(&id) {
3305                    properties
3306                } else {
3307                    // There is a delay between when netcfg installs and when said interface's
3308                    // properties are received via the interface watcher and ends up in
3309                    // `interface_properties`, so if the properties are not yet known, simply
3310                    // continue. The DHCPv6 client on such an interface will be started with PD
3311                    // configured per the usual process when handling interface watcher events.
3312                    continue;
3313                };
3314
3315            // TODO(https://fxbug.dev/42068818): Reload configuration in-place rather than
3316            // restarting the DHCPv6 client with different configuration.
3317            // Stop DHCPv6 client if it's running.
3318            if let Some::<dhcpv6::ClientState>(_) = dhcpv6_client_state.take() {
3319                dhcpv6::stop_client(
3320                    &self.lookup_admin,
3321                    &mut self.dns_servers,
3322                    &mut self.dns_server_watch_responders,
3323                    &mut self.netpol_networks_service,
3324                    id,
3325                    dns_watchers,
3326                    &mut self.dhcpv6_prefixes_streams,
3327                )
3328                .await;
3329            }
3330
3331            // Restart DHCPv6 client and configure it to perform PD.
3332            let sockaddr = match start_dhcpv6_client(
3333                properties,
3334                dhcpv6::duid(interface_naming_id.mac),
3335                &dhcpv6_client_provider,
3336                Some(pd_config.clone()),
3337                dns_watchers,
3338                &mut self.dhcpv6_prefixes_streams,
3339            )
3340            .context("starting DHCPv6 client with PD")
3341            {
3342                Ok(dhcpv6_client_addr) => dhcpv6_client_addr,
3343                Err(errors::Error::NonFatal(e)) => {
3344                    warn!("error restarting DHCPv6 client to perform PD: {:?}", e);
3345                    None
3346                }
3347                Err(errors::Error::Fatal(e)) => {
3348                    panic!("error restarting DHCPv6 client to perform PD: {:?}", e);
3349                }
3350            };
3351            *dhcpv6_client_state = sockaddr.map(dhcpv6::ClientState::new);
3352        }
3353        self.dhcpv6_prefix_provider_handler = Some(dhcpv6::PrefixProviderHandler {
3354            prefix_control_request_stream,
3355            watch_prefix_responder: None,
3356            current_prefix: None,
3357            interface_config,
3358            preferred_prefix_len,
3359        });
3360        Ok(())
3361    }
3362
3363    async fn handle_watch_prefix(
3364        &mut self,
3365        responder: fnet_dhcpv6::PrefixControlWatchPrefixResponder,
3366        dns_watchers: &mut DnsServerWatchers<'_>,
3367    ) -> Result<(), anyhow::Error> {
3368        let dhcpv6::PrefixProviderHandler {
3369            watch_prefix_responder,
3370            interface_config: _,
3371            prefix_control_request_stream: _,
3372            preferred_prefix_len: _,
3373            current_prefix: _,
3374        } = self
3375            .dhcpv6_prefix_provider_handler
3376            .as_mut()
3377            .expect("DHCPv6 prefix provider handler must be present to handle WatchPrefix");
3378        if let Some(responder) = watch_prefix_responder.take() {
3379            warn!("Attempted to call WatchPrefix twice on PrefixControl channel, closing channel");
3380            match responder
3381                .control_handle()
3382                .send_on_exit(fnet_dhcpv6::PrefixControlExitReason::DoubleWatch)
3383            {
3384                Err(e) => {
3385                    warn!(
3386                        "failed to send DoubleWatch terminal event on PrefixControl channel: {:?}",
3387                        e
3388                    );
3389                }
3390                Ok(()) => {}
3391            }
3392            self.on_dhcpv6_prefix_control_close(dns_watchers).await;
3393            Ok(())
3394        } else {
3395            *watch_prefix_responder = Some(responder);
3396
3397            dhcpv6::maybe_send_watch_prefix_response(
3398                &self.interface_states,
3399                &self.allowed_upstream_device_classes,
3400                self.dhcpv6_prefix_provider_handler.as_mut(),
3401            )
3402        }
3403    }
3404
3405    async fn on_dhcpv6_prefix_control_close(&mut self, dns_watchers: &mut DnsServerWatchers<'_>) {
3406        let _: dhcpv6::PrefixProviderHandler = self
3407            .dhcpv6_prefix_provider_handler
3408            .take()
3409            .expect("DHCPv6 prefix provider handler must be present");
3410        let dhcpv6_client_provider =
3411            self.dhcpv6_client_provider.as_ref().expect("DHCPv6 client provider must be present");
3412        for (id, InterfaceState { config, .. }) in self.interface_states.iter_mut() {
3413            let (dhcpv6_client_state, interface_naming_id) = match config {
3414                InterfaceConfigState::WlanAp(WlanApInterfaceState { interface_naming_id: _ })
3415                | InterfaceConfigState::Blackhole(_) => {
3416                    continue;
3417                }
3418                InterfaceConfigState::Host(HostInterfaceState {
3419                    dhcpv4_client: _,
3420                    dhcpv6_client_state,
3421                    dhcpv6_pd_config,
3422                    interface_admin_auth: _,
3423                    interface_naming_id,
3424                }) => {
3425                    if dhcpv6_pd_config.take().is_none() {
3426                        continue;
3427                    }
3428                    match dhcpv6_client_state.take() {
3429                        Some(_) => (dhcpv6_client_state, interface_naming_id),
3430                        None => continue,
3431                    }
3432                }
3433            };
3434
3435            // TODO(https://fxbug.dev/42068818): Reload configuration in-place rather than
3436            // restarting the DHCPv6 client with different configuration.
3437            // Stop DHCPv6 client if it's running.
3438            dhcpv6::stop_client(
3439                &self.lookup_admin,
3440                &mut self.dns_servers,
3441                &mut self.dns_server_watch_responders,
3442                &mut self.netpol_networks_service,
3443                *id,
3444                dns_watchers,
3445                &mut self.dhcpv6_prefixes_streams,
3446            )
3447            .await;
3448
3449            let fnet_interfaces_ext::PropertiesAndState { properties, state: _ } =
3450                self.interface_properties.get(id).unwrap_or_else(|| {
3451                    panic!("interface {} has DHCPv6 client but properties unknown", id)
3452                });
3453
3454            // Restart DHCPv6 client without PD.
3455            let sockaddr = match start_dhcpv6_client(
3456                properties,
3457                dhcpv6::duid(interface_naming_id.mac),
3458                &dhcpv6_client_provider,
3459                None,
3460                dns_watchers,
3461                &mut self.dhcpv6_prefixes_streams,
3462            )
3463            .context("starting DHCPv6 client with PD")
3464            {
3465                Ok(dhcpv6_client_addr) => dhcpv6_client_addr,
3466                Err(errors::Error::NonFatal(e)) => {
3467                    warn!("restarting DHCPv6 client to stop PD: {:?}", e);
3468                    None
3469                }
3470                Err(e @ errors::Error::Fatal(_)) => {
3471                    panic!("restarting DHCPv6 client to stop PD: {:?}", e);
3472                }
3473            };
3474            *dhcpv6_client_state = sockaddr.map(dhcpv6::ClientState::new);
3475        }
3476    }
3477
3478    async fn handle_dhcpv4_configuration(
3479        &mut self,
3480        interface_id: InterfaceId,
3481        res: Result<fnet_dhcp_ext::Configuration, fnet_dhcp_ext::Error>,
3482    ) -> Dhcpv4ConfigurationHandlerResult {
3483        let configuration = match res {
3484            Err(error) => {
3485                let allow_restart = match error {
3486                    fnet_dhcp_ext::Error::UnexpectedExit(reason) => match reason {
3487                        Some(reason) => match reason {
3488                            fnet_dhcp::ClientExitReason::AddressRemovedByUser => {
3489                                log::warn!(
3490                                    "DHCP client exited because its \
3491                                    bound address was removed (iface={interface_id})"
3492                                );
3493                                // The user intentionally removed the DHCP client's address, so we
3494                                // shouldn't automatically restart the client.
3495                                AllowClientRestart::No
3496                            }
3497                            reason @ (
3498                                fnet_dhcp::ClientExitReason::ClientAlreadyExistsOnInterface
3499                                | fnet_dhcp::ClientExitReason::WatchConfigurationAlreadyPending
3500                                | fnet_dhcp::ClientExitReason::InvalidInterface
3501                                | fnet_dhcp::ClientExitReason::InvalidParams
3502                                | fnet_dhcp::ClientExitReason::NetworkUnreachable
3503                                | fnet_dhcp::ClientExitReason::UnableToOpenSocket
3504                                | fnet_dhcp::ClientExitReason::GracefulShutdown
3505                                | fnet_dhcp::ClientExitReason::AddressStateProviderError
3506                            ) => {
3507                                log::error!("DHCP client unexpectedly exited \
3508                                                (iface={interface_id}, reason={reason:?})");
3509                                // The exit was unexpected, so we should restart the client.
3510                                AllowClientRestart::Yes
3511                            }
3512                        },
3513                        None => {
3514                            log::error!(
3515                                "DHCP client unexpectedly exited without \
3516                                giving a reason (iface={interface_id})"
3517                            );
3518                            AllowClientRestart::Yes
3519                        }
3520                    },
3521                    error @ (fnet_dhcp_ext::Error::ApiViolation(_)
3522                    | fnet_dhcp_ext::Error::RouteSet(_)
3523                    | fnet_dhcp_ext::Error::Fidl(_)
3524                    | fnet_dhcp_ext::Error::WrongExitReason(_)
3525                    | fnet_dhcp_ext::Error::MissingExitReason) => {
3526                        log::error!("DHCP client exited due to error \
3527                                        (iface={interface_id}, error={error:?})");
3528                                        AllowClientRestart::Yes
3529                    }
3530                };
3531                return Dhcpv4ConfigurationHandlerResult::ClientStopped(allow_restart);
3532            }
3533            Ok(configuration) => configuration,
3534        };
3535
3536        let (dhcpv4_client, control) = {
3537            let InterfaceState { config, control, .. } = self
3538                .interface_states
3539                .get_mut(&interface_id)
3540                .unwrap_or_else(|| panic!("interface {} not found", interface_id));
3541
3542            match config {
3543                InterfaceConfigState::Host(HostInterfaceState {
3544                    dhcpv4_client,
3545                    dhcpv6_client_state: _,
3546                    dhcpv6_pd_config: _,
3547                    interface_admin_auth: _,
3548                    interface_naming_id: _,
3549                }) => (
3550                    match dhcpv4_client {
3551                        Dhcpv4ClientState::Running(client) => client,
3552                        Dhcpv4ClientState::NotRunning | Dhcpv4ClientState::ScheduledRestart(_) => {
3553                            panic!(
3554                                "interface {} does not have a running DHCPv4 client",
3555                                interface_id
3556                            )
3557                        }
3558                    },
3559                    control,
3560                ),
3561                InterfaceConfigState::WlanAp(wlan_ap_state) => {
3562                    panic!(
3563                        "interface {} expected to be host but is WLAN AP with state {:?}",
3564                        interface_id, wlan_ap_state
3565                    );
3566                }
3567                InterfaceConfigState::Blackhole(state) => {
3568                    panic!(
3569                        "interface {} expected to be host but is blackhole with state {:?}",
3570                        interface_id, state
3571                    );
3572                }
3573            }
3574        };
3575
3576        dhcpv4::update_configuration(
3577            interface_id,
3578            dhcpv4_client,
3579            configuration,
3580            &mut self.dns_servers,
3581            &mut self.dns_server_watch_responders,
3582            &mut self.netpol_networks_service,
3583            control,
3584            &self.lookup_admin,
3585        )
3586        .await;
3587
3588        Dhcpv4ConfigurationHandlerResult::ContinueOperation
3589    }
3590
3591    async fn handle_dhcpv6_prefixes(
3592        &mut self,
3593        interface_id: InterfaceId,
3594        res: Result<Vec<fnet_dhcpv6::Prefix>, fidl::Error>,
3595        dns_watchers: &mut DnsServerWatchers<'_>,
3596    ) -> Result<(), anyhow::Error> {
3597        let new_prefixes = match res
3598            .context("DHCPv6 prefixes stream FIDL error")
3599            .and_then(|prefixes| dhcpv6::from_fidl_prefixes(&prefixes))
3600        {
3601            Err(e) => {
3602                warn!(
3603                    "DHCPv6 client on interface={} error on watch_prefixes: {:?}",
3604                    interface_id, e
3605                );
3606                dhcpv6::stop_client(
3607                    &self.lookup_admin,
3608                    &mut self.dns_servers,
3609                    &mut self.dns_server_watch_responders,
3610                    &mut self.netpol_networks_service,
3611                    interface_id,
3612                    dns_watchers,
3613                    &mut self.dhcpv6_prefixes_streams,
3614                )
3615                .await;
3616                HashMap::new()
3617            }
3618            Ok(prefixes_map) => prefixes_map,
3619        };
3620        let InterfaceState { config, .. } = self
3621            .interface_states
3622            .get_mut(&interface_id)
3623            .unwrap_or_else(|| panic!("interface {} not found", interface_id));
3624        let dhcpv6::ClientState { prefixes, sockaddr: _ } = match config {
3625            InterfaceConfigState::Host(HostInterfaceState {
3626                dhcpv4_client: _,
3627                dhcpv6_client_state,
3628                dhcpv6_pd_config: _,
3629                interface_admin_auth: _,
3630                interface_naming_id: _,
3631            }) => dhcpv6_client_state.as_mut().unwrap_or_else(|| {
3632                panic!("interface {} does not have a running DHCPv6 client", interface_id)
3633            }),
3634            InterfaceConfigState::WlanAp(wlan_ap_state) => {
3635                panic!(
3636                    "interface {} expected to be host but is WLAN AP with state {:?}",
3637                    interface_id, wlan_ap_state
3638                );
3639            }
3640            InterfaceConfigState::Blackhole(state) => {
3641                panic!(
3642                    "interface {} expected to be host but is blackhole with state {:?}",
3643                    interface_id, state
3644                );
3645            }
3646        };
3647        *prefixes = new_prefixes;
3648
3649        dhcpv6::maybe_send_watch_prefix_response(
3650            &self.interface_states,
3651            &self.allowed_upstream_device_classes,
3652            self.dhcpv6_prefix_provider_handler.as_mut(),
3653        )
3654    }
3655
3656    async fn get_interface_naming_identifier_for_instance(
3657        &self,
3658        device_instance: &devices::NetworkDeviceInstance,
3659    ) -> Result<interface::InterfaceNamingIdentifier, anyhow::Error> {
3660        let device_info = device_instance
3661            .get_device_info()
3662            .await
3663            .context("error getting device info and MAC")
3664            .map_err(|e| match e {
3665                errors::Error::NonFatal(e) | errors::Error::Fatal(e) => e,
3666            })?;
3667
3668        let DeviceInfo { mac, port_class: _, topological_path } = &device_info;
3669        let mac = mac.ok_or_else(|| anyhow!("devices without mac not supported"))?;
3670
3671        Ok(interface::generate_identifier(&mac, topological_path))
3672    }
3673}
3674
3675pub async fn run<M: Mode>() -> Result<(), anyhow::Error> {
3676    let opt: Opt = argh::from_env();
3677    let Opt { min_severity: LogLevel(min_severity), config_data } = &opt;
3678
3679    // Use the diagnostics_log library directly rather than e.g. the #[fuchsia::main] macro on
3680    // the main function, so that we can specify the logging severity level at runtime based on a
3681    // command line argument.
3682    diagnostics_log::initialize(
3683        diagnostics_log::PublishOptions::default().minimum_severity(*min_severity),
3684    )?;
3685
3686    info!("starting");
3687    debug!("starting with options = {:?}", opt);
3688
3689    let Config {
3690        dns_config: DnsConfig { servers },
3691        filter_config,
3692        filter_enabled_interface_types,
3693        interface_metrics,
3694        allowed_upstream_device_classes: AllowedDeviceClasses(allowed_upstream_device_classes),
3695        allowed_bridge_upstream_device_classes:
3696            AllowedDeviceClasses(allowed_bridge_upstream_device_classes),
3697        enable_dhcpv6,
3698        forwarded_device_classes,
3699        interface_naming_policy,
3700        interface_provisioning_policy,
3701        blackhole_interfaces,
3702        enable_socket_proxy,
3703    } = Config::load(config_data)?;
3704
3705    info!("using naming policy: {interface_naming_policy:?}");
3706    info!("using provisioning policy: {interface_provisioning_policy:?}");
3707
3708    let mut netcfg = NetCfg::new(
3709        filter_enabled_interface_types,
3710        interface_metrics,
3711        enable_dhcpv6,
3712        forwarded_device_classes,
3713        &allowed_upstream_device_classes,
3714        interface_naming_policy,
3715        interface_provisioning_policy,
3716        enable_socket_proxy,
3717    )
3718    .await
3719    .context("error creating new netcfg instance")?;
3720
3721    for name in &blackhole_interfaces {
3722        let result = netcfg.add_blackhole_interface(name).await;
3723
3724        match result {
3725            Ok(()) => {}
3726            Err(devices::AddDeviceError::AlreadyExists(name)) => {
3727                // Either the interface with this name was not installed
3728                // by Netcfg or another device installed by Netcfg used
3729                // the same name already. We will reject interface
3730                // installation.
3731                error!(
3732                    "interface with name ({name}) is already present in the Netstack, \
3733                    so we're rejecting installation of a blackhole interface with that name."
3734                );
3735            }
3736            Err(devices::AddDeviceError::Other(errors::Error::NonFatal(e))) => {
3737                error!("non-fatal error adding blackhole interface {:?}: {:?}", name, e);
3738            }
3739            Err(devices::AddDeviceError::Other(errors::Error::Fatal(e))) => {
3740                return Err(e.context(format!("error adding new blackhole interface {:?}", name)));
3741            }
3742        }
3743    }
3744
3745    // TODO(https://fxbug.dev/42080661): Once non-Fuchsia components can control filtering rules, disable
3746    // setting filters when interfaces are in Delegated provisioning mode.
3747    netcfg
3748        .filter_control
3749        .update_filters(filter_config)
3750        .await
3751        .context("update filters based on config")?;
3752
3753    // TODO(https://fxbug.dev/42080096): Once non-Fuchsia components can control DNS servers, disable
3754    // setting default DNS servers when interfaces are in Delegated provisioning mode.
3755    let servers = servers.into_iter().map(static_source_from_ip).collect();
3756    debug!("updating default servers to {:?}", servers);
3757    netcfg.update_dns_servers(DnsServersUpdateSource::Default, servers).await;
3758
3759    M::run(netcfg, allowed_bridge_upstream_device_classes)
3760        .map_err(|e| {
3761            let err_str = format!("fatal error running main: {:?}", e);
3762            error!("{}", err_str);
3763            anyhow!(err_str)
3764        })
3765        .await
3766}
3767
3768/// Allows callers of `netcfg::run` to configure at compile time which features
3769/// should be enabled.
3770///
3771/// This trait may be expanded to support combinations of features that can be
3772/// assembled together for specific netcfg builds.
3773#[async_trait(?Send)]
3774pub trait Mode {
3775    async fn run<'a>(
3776        netcfg: NetCfg<'a>,
3777        allowed_bridge_upstream_device_classes: HashSet<DeviceClass>,
3778    ) -> Result<(), anyhow::Error>;
3779}
3780
3781/// In this configuration, netcfg acts as the policy manager for netstack,
3782/// watching for device events and configuring the netstack with new interfaces
3783/// as needed on new device discovery. It does not implement any FIDL protocols.
3784pub enum BasicMode {}
3785
3786#[async_trait(?Send)]
3787impl Mode for BasicMode {
3788    async fn run<'a>(
3789        mut netcfg: NetCfg<'a>,
3790        _allowed_bridge_upstream_device_classes: HashSet<DeviceClass>,
3791    ) -> Result<(), anyhow::Error> {
3792        netcfg.run(virtualization::Stub).await.context("event loop")
3793    }
3794}
3795
3796/// In this configuration, netcfg implements the base functionality included in
3797/// `BasicMode`, and also serves the `fuchsia.net.virtualization/Control`
3798/// protocol, allowing clients to create virtual networks.
3799pub enum VirtualizationEnabled {}
3800
3801#[async_trait(?Send)]
3802impl Mode for VirtualizationEnabled {
3803    async fn run<'a>(
3804        mut netcfg: NetCfg<'a>,
3805        allowed_bridge_upstream_device_classes: HashSet<DeviceClass>,
3806    ) -> Result<(), anyhow::Error> {
3807        let handler = virtualization::Virtualization::new(
3808            netcfg.allowed_upstream_device_classes,
3809            allowed_bridge_upstream_device_classes,
3810            virtualization::BridgeHandlerImpl::new(netcfg.stack.clone()),
3811            netcfg.installer.clone(),
3812        );
3813        netcfg.run(handler).await.context("event loop")
3814    }
3815}
3816
3817fn map_control_error(
3818    ctx: &'static str,
3819) -> impl FnOnce(
3820    fnet_interfaces_ext::admin::TerminalError<fnet_interfaces_admin::InterfaceRemovedReason>,
3821) -> errors::Error {
3822    move |e| {
3823        let severity = match &e {
3824            fidl_fuchsia_net_interfaces_ext::admin::TerminalError::Fidl(e) => {
3825                if e.is_closed() {
3826                    // Control handle can close when interface is
3827                    // removed; not a fatal error.
3828                    errors::Error::NonFatal
3829                } else {
3830                    errors::Error::Fatal
3831                }
3832            }
3833            fidl_fuchsia_net_interfaces_ext::admin::TerminalError::Terminal(e) => match e {
3834                fidl_fuchsia_net_interfaces_admin::InterfaceRemovedReason::DuplicateName
3835                | fidl_fuchsia_net_interfaces_admin::InterfaceRemovedReason::PortAlreadyBound
3836                | fidl_fuchsia_net_interfaces_admin::InterfaceRemovedReason::BadPort => {
3837                    errors::Error::Fatal
3838                }
3839                fidl_fuchsia_net_interfaces_admin::InterfaceRemovedReason::PortClosed
3840                | fidl_fuchsia_net_interfaces_admin::InterfaceRemovedReason::User
3841                | _ => errors::Error::NonFatal,
3842            },
3843        };
3844        severity(anyhow::Error::new(e).context(ctx))
3845    }
3846}
3847
3848fn map_address_state_provider_error(
3849    ctx: &'static str,
3850) -> impl FnOnce(fnet_interfaces_ext::admin::AddressStateProviderError) -> errors::Error {
3851    move |e| {
3852        let severity = match &e {
3853            fnet_interfaces_ext::admin::AddressStateProviderError::Fidl(e) => {
3854                if e.is_closed() {
3855                    // TODO(https://fxbug.dev/42170615): Reconsider whether this
3856                    // should be a fatal error, as it can be caused by a
3857                    // netstack bug.
3858                    errors::Error::NonFatal
3859                } else {
3860                    errors::Error::Fatal
3861                }
3862            }
3863            fnet_interfaces_ext::admin::AddressStateProviderError::ChannelClosed => {
3864                // TODO(https://fxbug.dev/42170615): Reconsider whether this should
3865                // be a fatal error, as it can be caused by a netstack bug.
3866                errors::Error::NonFatal
3867            }
3868            fnet_interfaces_ext::admin::AddressStateProviderError::AddressRemoved(e) => match e {
3869                fidl_fuchsia_net_interfaces_admin::AddressRemovalReason::Invalid
3870                | fidl_fuchsia_net_interfaces_admin::AddressRemovalReason::InvalidProperties => {
3871                    errors::Error::Fatal
3872                }
3873                fidl_fuchsia_net_interfaces_admin::AddressRemovalReason::AlreadyAssigned
3874                | fidl_fuchsia_net_interfaces_admin::AddressRemovalReason::DadFailed
3875                | fidl_fuchsia_net_interfaces_admin::AddressRemovalReason::Forfeited
3876                | fidl_fuchsia_net_interfaces_admin::AddressRemovalReason::InterfaceRemoved
3877                | fidl_fuchsia_net_interfaces_admin::AddressRemovalReason::UserRemoved => {
3878                    errors::Error::NonFatal
3879                }
3880            },
3881        };
3882        severity(anyhow::Error::new(e).context(ctx))
3883    }
3884}
3885
3886/// If we can't reach netstack via fidl, log an error and exit.
3887//
3888// TODO(https://fxbug.dev/42070352): add a test that works as intended.
3889pub(crate) fn exit_with_fidl_error(cause: fidl::Error) -> ! {
3890    error!(cause:%; "exiting due to fidl error");
3891    std::process::exit(1);
3892}
3893
3894/// Installs the rule that directs all unmarked sockets to lookup the main table
3895/// where all the locally-provisioned network related routes live.
3896///
3897/// This needs to have a high priority so that it is not overridden later.
3898async fn install_locally_provisioned_network_rule_set<
3899    I: fnet_routes_ext::rules::FidlRuleIpExt
3900        + fnet_routes_ext::rules::FidlRuleAdminIpExt
3901        + fnet_routes_ext::FidlRouteIpExt
3902        + fnet_routes_ext::admin::FidlRouteAdminIpExt,
3903>() -> Result<<I::RuleSetMarker as ProtocolMarker>::Proxy, anyhow::Error> {
3904    let rule_table = fuchsia_component::client::connect_to_protocol::<I::RuleTableMarker>()?;
3905    // The `RouteTableV{4,6}` protocol provides access to the main table.
3906    let main_route_table = fuchsia_component::client::connect_to_protocol::<I::RouteTableMarker>()?;
3907
3908    let fidl_fuchsia_net_routes_admin::GrantForRouteTableAuthorization { table_id, token } =
3909        fnet_routes_ext::admin::get_authorization_for_route_table::<I>(&main_route_table)
3910            .await
3911            .context("failed to get authorization for the main table")?;
3912
3913    const LOCALLY_PROVISIONED_NETWORK_RULE_SET_PRIORITY: u32 = 0;
3914    let rule_set = fnet_routes_ext::rules::new_rule_set::<I>(
3915        &rule_table,
3916        fnet_routes_ext::rules::RuleSetPriority::from(
3917            LOCALLY_PROVISIONED_NETWORK_RULE_SET_PRIORITY,
3918        ),
3919    )
3920    .context("failed to create a new rule set")?;
3921
3922    fnet_routes_ext::rules::authenticate_for_route_table::<I>(&rule_set, table_id, token)
3923        .await
3924        .context("fidl error authenticating for route table")?
3925        .map_err(|err| anyhow::anyhow!("failed to authenticate for the route table: {err:?}"))?;
3926
3927    const LOCALLY_PROVISIONED_NETWORK_RULE_INDEX: u32 = 0;
3928    fnet_routes_ext::rules::add_rule::<I>(
3929        &rule_set,
3930        fnet_routes_ext::rules::RuleIndex::from(LOCALLY_PROVISIONED_NETWORK_RULE_INDEX),
3931        fnet_routes_ext::rules::RuleMatcher {
3932            mark_1: Some(fnet_matchers_ext::Mark::Unmarked),
3933            mark_2: Some(fnet_matchers_ext::Mark::Unmarked),
3934            ..Default::default()
3935        },
3936        fnet_routes_ext::rules::RuleAction::Lookup(fnet_routes_ext::TableId::new(table_id)),
3937    )
3938    .await
3939    .context("fidl error adding rule")?
3940    .map_err(|err| anyhow::anyhow!("failed to add the rule: {err:?}"))?;
3941
3942    Ok(rule_set)
3943}
3944
3945#[cfg(test)]
3946mod tests {
3947    use fidl_fuchsia_net_ext::FromExt as _;
3948    use {
3949        fidl_fuchsia_net_dhcpv6_ext as fnet_dhcpv6_ext, fidl_fuchsia_net_routes as fnet_routes,
3950        fidl_fuchsia_net_routes_admin as fnet_routes_admin,
3951        fidl_fuchsia_net_routes_ext as fnet_routes_ext,
3952    };
3953
3954    use assert_matches::assert_matches;
3955    use futures::future::{self, FutureExt as _};
3956    use futures::stream::{FusedStream as _, TryStreamExt as _};
3957    use net_declare::{
3958        fidl_ip, fidl_ip_v4_with_prefix, fidl_ip_v6, fidl_ip_v6_with_prefix, fidl_mac, fidl_subnet,
3959    };
3960    use pretty_assertions::assert_eq;
3961    use test_case::test_case;
3962
3963    use super::*;
3964    use crate::interface::ProvisioningType::{Delegated, Local};
3965    use crate::socketproxy::socketproxy_utils::respond_to_socketproxy;
3966
3967    impl Config {
3968        pub fn load_str(s: &str) -> Result<Self, anyhow::Error> {
3969            let config = serde_json5::from_str(s)
3970                .with_context(|| format!("could not deserialize the config data {}", s))?;
3971            Ok(config)
3972        }
3973    }
3974
3975    struct ServerEnds {
3976        lookup_admin: fnet_name::LookupAdminRequestStream,
3977        dhcpv4_client_provider: fnet_dhcp::ClientProviderRequestStream,
3978        dhcpv6_client_provider: fnet_dhcpv6::ClientProviderRequestStream,
3979        route_set_v4_provider: fidl::endpoints::ServerEnd<fnet_routes_admin::RouteTableV4Marker>,
3980        dhcpv4_server: fidl::endpoints::ServerEnd<fnet_dhcp::Server_Marker>,
3981        fuchsia_networks: fidl::endpoints::ServerEnd<fnp_socketproxy::FuchsiaNetworksMarker>,
3982    }
3983
3984    impl Into<anyhow::Error> for errors::Error {
3985        fn into(self) -> anyhow::Error {
3986            match self {
3987                errors::Error::NonFatal(e) => e,
3988                errors::Error::Fatal(e) => e,
3989            }
3990        }
3991    }
3992
3993    impl Into<fidl_fuchsia_hardware_network::PortClass> for DeviceClass {
3994        fn into(self) -> fidl_fuchsia_hardware_network::PortClass {
3995            match self {
3996                Self::Virtual => fidl_fuchsia_hardware_network::PortClass::Virtual,
3997                Self::Ethernet => fidl_fuchsia_hardware_network::PortClass::Ethernet,
3998                Self::WlanClient => fidl_fuchsia_hardware_network::PortClass::WlanClient,
3999                Self::Ppp => fidl_fuchsia_hardware_network::PortClass::Ppp,
4000                Self::Bridge => fidl_fuchsia_hardware_network::PortClass::Bridge,
4001                Self::WlanAp => fidl_fuchsia_hardware_network::PortClass::WlanAp,
4002                Self::Lowpan => fidl_fuchsia_hardware_network::PortClass::Lowpan,
4003                Self::Blackhole => fidl_fuchsia_hardware_network::PortClass::Virtual,
4004            }
4005        }
4006    }
4007
4008    static DEFAULT_ALLOWED_UPSTREAM_DEVICE_CLASSES: std::sync::LazyLock<HashSet<DeviceClass>> =
4009        std::sync::LazyLock::new(HashSet::new);
4010
4011    struct NetcfgTestArgs {
4012        with_dhcpv4_client_provider: bool,
4013        with_fuchsia_networks: bool,
4014    }
4015
4016    fn test_netcfg(args: NetcfgTestArgs) -> Result<(NetCfg<'static>, ServerEnds), anyhow::Error> {
4017        let (stack, _stack_server) = fidl::endpoints::create_proxy::<fnet_stack::StackMarker>();
4018        let (lookup_admin, lookup_admin_server) =
4019            fidl::endpoints::create_proxy::<fnet_name::LookupAdminMarker>();
4020        let (filter, _filter_server) =
4021            fidl::endpoints::create_proxy::<fnet_filter_deprecated::FilterMarker>();
4022        let filter_control = FilterControl::Deprecated(filter);
4023        let (interface_state, _interface_state_server) =
4024            fidl::endpoints::create_proxy::<fnet_interfaces::StateMarker>();
4025        let (dhcp_server, dhcp_server_server_end) =
4026            fidl::endpoints::create_proxy::<fnet_dhcp::Server_Marker>();
4027        let (dhcpv4_client_provider, dhcpv4_client_provider_server) =
4028            fidl::endpoints::create_proxy::<fnet_dhcp::ClientProviderMarker>();
4029        let (dhcpv6_client_provider, dhcpv6_client_provider_server) =
4030            fidl::endpoints::create_proxy::<fnet_dhcpv6::ClientProviderMarker>();
4031        let (installer, _installer_server) =
4032            fidl::endpoints::create_proxy::<fidl_fuchsia_net_interfaces_admin::InstallerMarker>();
4033        let (route_set_v4_provider, route_set_v4_provider_server) =
4034            fidl::endpoints::create_proxy::<fnet_routes_admin::RouteTableV4Marker>();
4035        let (fuchsia_networks, fuchsia_networks_server) =
4036            fidl::endpoints::create_proxy::<fnp_socketproxy::FuchsiaNetworksMarker>();
4037        Ok((
4038            NetCfg {
4039                stack,
4040                lookup_admin,
4041                filter_control,
4042                interface_state,
4043                installer,
4044                dhcp_server: Some(dhcp_server),
4045                dhcpv4_client_provider: args
4046                    .with_dhcpv4_client_provider
4047                    .then_some(dhcpv4_client_provider),
4048                dhcpv6_client_provider: Some(dhcpv6_client_provider),
4049                route_set_v4_provider,
4050                socket_proxy_state: args
4051                    .with_fuchsia_networks
4052                    .then_some(SocketProxyState::new(fuchsia_networks)),
4053                locally_provisioned_network_rule_set: None,
4054                filter_enabled_state: Default::default(),
4055                interface_properties: Default::default(),
4056                interface_states: Default::default(),
4057                interface_metrics: Default::default(),
4058                dns_servers: Default::default(),
4059                dns_server_watch_responders: Default::default(),
4060                route_advertisement_watcher_provider: Default::default(),
4061                forwarded_device_classes: Default::default(),
4062                dhcpv6_prefix_provider_handler: Default::default(),
4063                allowed_upstream_device_classes: &DEFAULT_ALLOWED_UPSTREAM_DEVICE_CLASSES,
4064                dhcpv4_configuration_streams: dhcpv4::ConfigurationStreamMap::empty(),
4065                dhcpv6_prefixes_streams: dhcpv6::PrefixesStreamMap::empty(),
4066                interface_naming_config: interface::InterfaceNamingConfig::from_naming_rules(
4067                    vec![],
4068                ),
4069                interface_provisioning_policy: Default::default(),
4070                netpol_networks_service: Default::default(),
4071                enable_socket_proxy: false,
4072            },
4073            ServerEnds {
4074                lookup_admin: lookup_admin_server.into_stream(),
4075                dhcpv4_client_provider: dhcpv4_client_provider_server.into_stream(),
4076                dhcpv6_client_provider: dhcpv6_client_provider_server.into_stream(),
4077                route_set_v4_provider: route_set_v4_provider_server,
4078                dhcpv4_server: dhcp_server_server_end,
4079                fuchsia_networks: fuchsia_networks_server,
4080            },
4081        ))
4082    }
4083
4084    const INTERFACE_ID: InterfaceId = InterfaceId::new(1).unwrap();
4085    const DHCPV6_DNS_SOURCE: DnsServersUpdateSource =
4086        DnsServersUpdateSource::Dhcpv6 { interface_id: INTERFACE_ID.get() };
4087    const LINK_LOCAL_SOCKADDR1: fnet::Ipv6SocketAddress = fnet::Ipv6SocketAddress {
4088        address: fidl_ip_v6!("fe80::1"),
4089        port: fnet_dhcpv6::DEFAULT_CLIENT_PORT,
4090        zone_index: INTERFACE_ID.get(),
4091    };
4092    const LINK_LOCAL_SOCKADDR2: fnet::Ipv6SocketAddress = fnet::Ipv6SocketAddress {
4093        address: fidl_ip_v6!("fe80::2"),
4094        port: fnet_dhcpv6::DEFAULT_CLIENT_PORT,
4095        zone_index: INTERFACE_ID.get(),
4096    };
4097    const GLOBAL_ADDR: fnet::Subnet = fnet::Subnet { addr: fidl_ip!("2000::1"), prefix_len: 64 };
4098    const DNS_SERVER1: fnet::SocketAddress = fnet::SocketAddress::Ipv6(fnet::Ipv6SocketAddress {
4099        address: fidl_ip_v6!("2001::1"),
4100        port: fnet_dhcpv6::DEFAULT_CLIENT_PORT,
4101        zone_index: 0,
4102    });
4103    const DNS_SERVER2: fnet::SocketAddress = fnet::SocketAddress::Ipv6(fnet::Ipv6SocketAddress {
4104        address: fidl_ip_v6!("2001::2"),
4105        port: fnet_dhcpv6::DEFAULT_CLIENT_PORT,
4106        zone_index: 0,
4107    });
4108
4109    fn test_addr(addr: fnet::Subnet) -> fnet_interfaces::Address {
4110        fnet_interfaces_ext::Address::<fnet_interfaces_ext::DefaultInterest> {
4111            addr,
4112            valid_until: fnet_interfaces_ext::NoInterest,
4113            preferred_lifetime_info: fnet_interfaces_ext::NoInterest,
4114            assignment_state: fnet_interfaces::AddressAssignmentState::Assigned,
4115        }
4116        .into()
4117    }
4118
4119    fn ipv6addrs(a: Option<fnet::Ipv6SocketAddress>) -> Vec<fnet_interfaces::Address> {
4120        // The DHCPv6 client will only use a link-local address but we include a global address
4121        // and expect it to not be used.
4122        std::iter::once(test_addr(GLOBAL_ADDR))
4123            .chain(a.map(|fnet::Ipv6SocketAddress { address, port: _, zone_index: _ }| {
4124                test_addr(fnet::Subnet { addr: fnet::IpAddress::Ipv6(address), prefix_len: 64 })
4125            }))
4126            .collect()
4127    }
4128
4129    /// Handle receiving a netstack interface changed event.
4130    async fn handle_interface_changed_event(
4131        netcfg: &mut NetCfg<'_>,
4132        dns_watchers: &mut DnsServerWatchers<'_>,
4133        online: Option<bool>,
4134        addresses: Option<Vec<fnet_interfaces::Address>>,
4135    ) -> Result<(), anyhow::Error> {
4136        let event = fnet_interfaces::Event::Changed(fnet_interfaces::Properties {
4137            id: Some(INTERFACE_ID.get()),
4138            online,
4139            addresses,
4140            ..Default::default()
4141        });
4142        netcfg
4143            .handle_interface_watcher_event(event.into(), dns_watchers, &mut virtualization::Stub)
4144            .await
4145    }
4146
4147    /// Make sure that a new DHCPv6 client was requested, and verify its parameters.
4148    async fn check_new_dhcpv6_client(
4149        server: &mut fnet_dhcpv6::ClientProviderRequestStream,
4150        id: InterfaceId,
4151        sockaddr: fnet::Ipv6SocketAddress,
4152        prefix_delegation_config: Option<fnet_dhcpv6::PrefixDelegationConfig>,
4153        dns_watchers: &mut DnsServerWatchers<'_>,
4154    ) -> Result<fnet_dhcpv6::ClientRequestStream, anyhow::Error> {
4155        let evt =
4156            server.try_next().await.context("error getting next dhcpv6 client provider event")?;
4157        let mut client_server =
4158            match evt.ok_or_else(|| anyhow::anyhow!("expected dhcpv6 client provider request"))? {
4159                fnet_dhcpv6::ClientProviderRequest::NewClient {
4160                    params,
4161                    request,
4162                    control_handle: _,
4163                } => {
4164                    let stateful = prefix_delegation_config.is_some();
4165                    let params: fnet_dhcpv6_ext::NewClientParams = params.try_into()?;
4166                    assert_eq!(
4167                        params,
4168                        fnet_dhcpv6_ext::NewClientParams {
4169                            interface_id: id.get(),
4170                            address: sockaddr,
4171                            config: fnet_dhcpv6_ext::ClientConfig {
4172                                information_config: fnet_dhcpv6_ext::InformationConfig {
4173                                    dns_servers: true,
4174                                },
4175                                prefix_delegation_config,
4176                                non_temporary_address_config: fnet_dhcpv6_ext::AddressConfig {
4177                                    address_count: 0,
4178                                    preferred_addresses: None,
4179                                }
4180                            },
4181                            duid: stateful.then_some(fnet_dhcpv6::Duid::LinkLayerAddress(
4182                                fnet_dhcpv6::LinkLayerAddress::Ethernet(TEST_MAC)
4183                            )),
4184                        }
4185                    );
4186
4187                    request.into_stream()
4188                }
4189            };
4190        assert!(dns_watchers.contains_key(&DHCPV6_DNS_SOURCE), "should have a watcher");
4191        // NetCfg always keeps the server hydrated with a pending hanging-get.
4192        expect_watch_prefixes(&mut client_server).await;
4193        Ok(client_server)
4194    }
4195
4196    fn expect_watch_dhcpv4_configuration(
4197        stream: &mut fnet_dhcp::ClientRequestStream,
4198    ) -> fnet_dhcp::ClientWatchConfigurationResponder {
4199        assert_matches::assert_matches!(
4200            stream.try_next().now_or_never(),
4201            Some(Ok(Some(fnet_dhcp::ClientRequest::WatchConfiguration { responder }))) => {
4202                responder
4203            },
4204            "expect a watch_configuration call immediately"
4205        )
4206    }
4207
4208    const TEST_MAC: fidl_fuchsia_net::MacAddress = fidl_mac!("00:01:02:03:04:05");
4209    const TEST_TOPOLOGICAL_PATH: &'static str = "/dev/foo/bar/network-device";
4210
4211    fn test_interface_naming_id() -> interface::InterfaceNamingIdentifier {
4212        interface::generate_identifier(&TEST_MAC.into(), TEST_TOPOLOGICAL_PATH)
4213    }
4214
4215    async fn expect_get_interface_auth(control: &mut fnet_interfaces_admin::ControlRequestStream) {
4216        let responder = control
4217            .by_ref()
4218            .try_next()
4219            .await
4220            .expect("get next interface control request")
4221            .expect("interface control request")
4222            .into_get_authorization_for_interface()
4223            .expect("should be interface authorization request");
4224        responder
4225            .send(fnet_resources::GrantForInterfaceAuthorization {
4226                interface_id: INTERFACE_ID.get(),
4227                token: zx::Event::create(),
4228            })
4229            .expect("respond should succeed");
4230    }
4231
4232    #[fuchsia::test]
4233    async fn test_stopping_dhcpv6_with_down_lookup_admin() {
4234        let (
4235            mut netcfg,
4236            ServerEnds {
4237                lookup_admin,
4238                dhcpv4_client_provider: _,
4239                mut dhcpv6_client_provider,
4240                route_set_v4_provider: _,
4241                dhcpv4_server: _,
4242                fuchsia_networks: _,
4243            },
4244        ) = test_netcfg(NetcfgTestArgs {
4245            with_dhcpv4_client_provider: false,
4246            with_fuchsia_networks: false,
4247        })
4248        .expect("error creating test netcfg");
4249        let mut dns_watchers = DnsServerWatchers::empty();
4250
4251        // Mock a new interface being discovered by NetCfg (we only need to make NetCfg aware of a
4252        // NIC with ID `INTERFACE_ID` to test DHCPv6).
4253        let (control, control_server_end) =
4254            fidl_fuchsia_net_interfaces_ext::admin::Control::create_endpoints()
4255                .expect("create endpoints");
4256
4257        let mut control_request_stream = control_server_end.into_stream();
4258
4259        let port_class = fidl_fuchsia_hardware_network::PortClass::Virtual;
4260        let (new_host_result, ()) = futures::join!(
4261            InterfaceState::new_host(
4262                test_interface_naming_id(),
4263                control,
4264                port_class.try_into().unwrap(),
4265                None,
4266                interface::ProvisioningType::Local,
4267            ),
4268            expect_get_interface_auth(&mut control_request_stream)
4269        );
4270
4271        assert_matches::assert_matches!(
4272            netcfg
4273                .interface_states
4274                .insert(INTERFACE_ID, new_host_result.expect("new_host should succeed")),
4275            None
4276        );
4277
4278        // Should start the DHCPv6 client when we get an interface changed event that shows the
4279        // interface as up with an link-local address.
4280        netcfg
4281            .handle_interface_watcher_event(
4282                fnet_interfaces::Event::Added(fnet_interfaces::Properties {
4283                    id: Some(INTERFACE_ID.get()),
4284                    name: Some("testif01".to_string()),
4285                    port_class: Some(fnet_interfaces::PortClass::Device(port_class)),
4286                    online: Some(true),
4287                    addresses: Some(ipv6addrs(Some(LINK_LOCAL_SOCKADDR1))),
4288                    has_default_ipv4_route: Some(false),
4289                    has_default_ipv6_route: Some(false),
4290                    ..Default::default()
4291                })
4292                .into(),
4293                &mut dns_watchers,
4294                &mut virtualization::Stub,
4295            )
4296            .await
4297            .expect("error handling interface added event with interface up and sockaddr1");
4298        let _: fnet_dhcpv6::ClientRequestStream = check_new_dhcpv6_client(
4299            &mut dhcpv6_client_provider,
4300            INTERFACE_ID,
4301            LINK_LOCAL_SOCKADDR1,
4302            None,
4303            &mut dns_watchers,
4304        )
4305        .await
4306        .expect("error checking for new client with sockaddr1");
4307
4308        // Drop the server-end of the lookup admin to simulate a down lookup admin service.
4309        std::mem::drop(lookup_admin);
4310
4311        // Not having any more link local IPv6 addresses should terminate the client.
4312        handle_interface_changed_event(&mut netcfg, &mut dns_watchers, None, Some(ipv6addrs(None)))
4313            .await
4314            .expect("error handling interface changed event with sockaddr1 removed");
4315
4316        // Another update without any link-local IPv6 addresses should do nothing
4317        // since the DHCPv6 client was already stopped.
4318        handle_interface_changed_event(&mut netcfg, &mut dns_watchers, None, Some(Vec::new()))
4319            .await
4320            .expect("error handling interface changed event with sockaddr1 removed");
4321
4322        // Update interface with a link-local address to create a new DHCPv6 client.
4323        handle_interface_changed_event(
4324            &mut netcfg,
4325            &mut dns_watchers,
4326            None,
4327            Some(ipv6addrs(Some(LINK_LOCAL_SOCKADDR1))),
4328        )
4329        .await
4330        .expect("error handling interface changed event with sockaddr1 removed");
4331        let _: fnet_dhcpv6::ClientRequestStream = check_new_dhcpv6_client(
4332            &mut dhcpv6_client_provider,
4333            INTERFACE_ID,
4334            LINK_LOCAL_SOCKADDR1,
4335            None,
4336            &mut dns_watchers,
4337        )
4338        .await
4339        .expect("error checking for new client with sockaddr1");
4340
4341        // Update offline status to down to stop DHCPv6 client.
4342        handle_interface_changed_event(&mut netcfg, &mut dns_watchers, Some(false), None)
4343            .await
4344            .expect("error handling interface changed event with sockaddr1 removed");
4345
4346        // Update interface with new addresses but leave offline status as down.
4347        handle_interface_changed_event(&mut netcfg, &mut dns_watchers, None, Some(ipv6addrs(None)))
4348            .await
4349            .expect("error handling interface changed event with sockaddr1 removed")
4350    }
4351
4352    async fn expect_watch_prefixes(client_server: &mut fnet_dhcpv6::ClientRequestStream) {
4353        assert_matches::assert_matches!(
4354            client_server.try_next().now_or_never(),
4355            Some(Ok(Some(fnet_dhcpv6::ClientRequest::WatchPrefixes { responder }))) => {
4356                responder.drop_without_shutdown();
4357            },
4358            "expect a watch_prefixes call immediately"
4359        )
4360    }
4361
4362    async fn handle_update(
4363        netcfg: &mut NetCfg<'_>,
4364        online: Option<bool>,
4365        addresses: Option<Vec<fnet_interfaces::Address>>,
4366        dns_watchers: &mut DnsServerWatchers<'_>,
4367    ) {
4368        netcfg
4369            .handle_interface_watcher_event(
4370                fnet_interfaces::Event::Changed(fnet_interfaces::Properties {
4371                    id: Some(INTERFACE_ID.get()),
4372                    online,
4373                    addresses,
4374                    ..Default::default()
4375                })
4376                .into(),
4377                dns_watchers,
4378                &mut virtualization::Stub,
4379            )
4380            .await
4381            .expect("error handling interface change event with interface online")
4382    }
4383    const DHCP_ADDRESS: fnet::Ipv4AddressWithPrefix = fidl_ip_v4_with_prefix!("192.0.2.254/24");
4384
4385    fn dhcp_address_parameters() -> fnet_interfaces_admin::AddressParameters {
4386        fnet_interfaces_admin::AddressParameters {
4387            initial_properties: Some(fnet_interfaces_admin::AddressProperties {
4388                preferred_lifetime_info: None,
4389                valid_lifetime_end: Some(zx::MonotonicInstant::INFINITE.into_nanos()),
4390                ..Default::default()
4391            }),
4392            temporary: Some(true),
4393            add_subnet_route: Some(false),
4394            ..Default::default()
4395        }
4396    }
4397
4398    // Verify that the DHCPv4 server for a WlanAP is started when the interface
4399    // is observed to be online, regardless of whether that happens in an
4400    // `Added` or `Changed` event from the Netstack's interfaces watcher.
4401    #[test_case(true; "added online")]
4402    #[test_case(false; "added offline")]
4403    #[fuchsia::test]
4404    async fn test_dhcpv4_server_started(added_online: bool) {
4405        let (mut netcfg, ServerEnds { dhcpv4_server, .. }) = test_netcfg(NetcfgTestArgs {
4406            with_dhcpv4_client_provider: false,
4407            with_fuchsia_networks: false,
4408        })
4409        .expect("error creating test netcfg");
4410
4411        // A future representing the DHCPv4 server. Must be polled while feeding
4412        // updates to Netcfg.
4413        let mut dhcpv4_server_req_stream = dhcpv4_server.into_stream();
4414        let start_serving_fut = dhcpv4_server_req_stream.next().map(|req| {
4415            match req.expect("dhcpv4 request stream ended").expect("dhcpv4 request") {
4416                fnet_dhcp::Server_Request::StartServing { responder } => {
4417                    responder.send(Ok(())).expect("failed to respond");
4418                }
4419                _ => panic!("unexpected DHCPv4 server request"),
4420            }
4421        });
4422
4423        // Mock a new WlanAP interface being discovered by NetCfg.
4424        let port_class = fidl_fuchsia_hardware_network::PortClass::WlanAp;
4425        let (control_client, _control_server_end) =
4426            fidl_fuchsia_net_interfaces_ext::admin::Control::create_endpoints()
4427                .expect("create endpoints");
4428        let wlan_ap = InterfaceState::new_wlan_ap(
4429            test_interface_naming_id(),
4430            control_client,
4431            port_class.try_into().unwrap(),
4432            interface::ProvisioningType::Local,
4433        );
4434        assert_matches::assert_matches!(
4435            netcfg.interface_states.insert(INTERFACE_ID, wlan_ap),
4436            None
4437        );
4438
4439        // Have Netcfg observe the new interface being added.
4440        let mut dns_watchers = DnsServerWatchers::empty();
4441        let mut virt_stub = virtualization::Stub;
4442        let start_serving_fut = {
4443            let netcfg_fut = netcfg
4444                .handle_interface_watcher_event(
4445                    fnet_interfaces::Event::Added(fnet_interfaces::Properties {
4446                        id: Some(INTERFACE_ID.get()),
4447                        name: Some("testif01".to_string()),
4448                        port_class: Some(fnet_interfaces::PortClass::Device(port_class)),
4449                        online: Some(added_online),
4450                        addresses: Some(Vec::new()),
4451                        has_default_ipv4_route: Some(false),
4452                        has_default_ipv6_route: Some(false),
4453                        ..Default::default()
4454                    })
4455                    .into(),
4456                    &mut dns_watchers,
4457                    &mut virt_stub,
4458                )
4459                .map(|result| result.expect("handling interfaces watcher event"))
4460                .fuse();
4461            let netcfg_fut = pin!(netcfg_fut);
4462            if added_online {
4463                // Serving should be started. Expect both futures to terminate.
4464                let ((), ()) = futures::join!(netcfg_fut, start_serving_fut);
4465                None
4466            } else {
4467                match futures::future::select(netcfg_fut, start_serving_fut).await {
4468                    futures::future::Either::Left(((), start_serving_fut)) => {
4469                        Some(start_serving_fut)
4470                    }
4471                    futures::future::Either::Right(((), _netcfg_fut)) => {
4472                        panic!("serving unexpectedly started")
4473                    }
4474                }
4475            }
4476        };
4477        if let Some(start_serving_fut) = start_serving_fut {
4478            let netcfg_fut = handle_update(
4479                &mut netcfg,
4480                Some(true), /* online */
4481                None,       /* addresses */
4482                &mut dns_watchers,
4483            )
4484            .fuse();
4485            let netcfg_fut = pin!(netcfg_fut);
4486            // Serving should be started. Expect both futures to terminate.
4487            let ((), ()) = futures::join!(netcfg_fut, start_serving_fut);
4488        }
4489    }
4490
4491    #[test_case(true, true; "added online and removed interface")]
4492    #[test_case(false, true; "added offline and removed interface")]
4493    #[test_case(true, false; "added online and disabled interface")]
4494    #[test_case(false, false; "added offline and disabled interface")]
4495    #[fuchsia::test]
4496    async fn test_dhcpv4(added_online: bool, remove_interface: bool) {
4497        let (
4498            mut netcfg,
4499            ServerEnds {
4500                mut lookup_admin,
4501                mut dhcpv4_client_provider,
4502                dhcpv6_client_provider: _,
4503                route_set_v4_provider,
4504                dhcpv4_server: _,
4505                fuchsia_networks: _,
4506            },
4507        ) = test_netcfg(NetcfgTestArgs {
4508            with_dhcpv4_client_provider: true,
4509            with_fuchsia_networks: false,
4510        })
4511        .expect("error creating test netcfg");
4512        let mut dns_watchers = DnsServerWatchers::empty();
4513
4514        let mut route_set_request_stream =
4515            fnet_routes_ext::testutil::admin::serve_one_route_set::<Ipv4>(route_set_v4_provider);
4516
4517        // Mock a new interface being discovered by NetCfg (we only need to make NetCfg aware of a
4518        // NIC with ID `INTERFACE_ID` to test DHCPv4).
4519        let (control_client, control_server_end) =
4520            fidl_fuchsia_net_interfaces_ext::admin::Control::create_endpoints()
4521                .expect("create endpoints");
4522        let port_class = fidl_fuchsia_hardware_network::PortClass::Virtual;
4523        let mut control = control_server_end.into_stream();
4524
4525        let (new_host_result, ()) = futures::join!(
4526            InterfaceState::new_host(
4527                test_interface_naming_id(),
4528                control_client,
4529                port_class.try_into().unwrap(),
4530                None,
4531                interface::ProvisioningType::Local,
4532            ),
4533            expect_get_interface_auth(&mut control)
4534        );
4535
4536        assert_matches::assert_matches!(
4537            netcfg
4538                .interface_states
4539                .insert(INTERFACE_ID, new_host_result.expect("new_host should succeed")),
4540            None
4541        );
4542
4543        let handle_route_set_fut = async {
4544            let (_proof, responder) = route_set_request_stream
4545                .try_next()
4546                .await
4547                .expect("get next route set request")
4548                .expect("route set request stream should not have ended")
4549                .into_authenticate_for_interface()
4550                .expect("should be AuthenticateForInterface request");
4551            responder.send(Ok(())).expect("responding should succeed");
4552        };
4553
4554        let handle_interface_watcher_event_fut = async {
4555            netcfg
4556                .handle_interface_watcher_event(
4557                    fnet_interfaces::Event::Added(fnet_interfaces::Properties {
4558                        id: Some(INTERFACE_ID.get()),
4559                        name: Some("testif01".to_string()),
4560                        port_class: Some(fnet_interfaces::PortClass::Device(
4561                            fidl_fuchsia_hardware_network::PortClass::Virtual,
4562                        )),
4563                        online: Some(added_online),
4564                        addresses: Some(Vec::new()),
4565                        has_default_ipv4_route: Some(false),
4566                        has_default_ipv6_route: Some(false),
4567                        ..Default::default()
4568                    })
4569                    .into(),
4570                    &mut dns_watchers,
4571                    &mut virtualization::Stub,
4572                )
4573                .await
4574                .expect("error handling interface added event");
4575
4576            if !added_online {
4577                assert_matches::assert_matches!(
4578                    dhcpv4_client_provider.try_next().now_or_never(),
4579                    None
4580                );
4581                handle_update(
4582                    &mut netcfg,
4583                    Some(true), /* online */
4584                    None,       /* addresses */
4585                    &mut dns_watchers,
4586                )
4587                .await;
4588            }
4589
4590            let mut client_req_stream = match dhcpv4_client_provider
4591                .try_next()
4592                .await
4593                .expect("get next dhcpv4 client provider event")
4594                .expect("dhcpv4 client provider request")
4595            {
4596                fnet_dhcp::ClientProviderRequest::NewClient {
4597                    interface_id,
4598                    params,
4599                    request,
4600                    control_handle: _,
4601                } => {
4602                    assert_eq!(interface_id, INTERFACE_ID.get());
4603                    assert_eq!(params, dhcpv4::new_client_params());
4604                    request.into_stream()
4605                }
4606                fnet_dhcp::ClientProviderRequest::CheckPresence { responder: _ } => {
4607                    unreachable!("only called at startup")
4608                }
4609            };
4610
4611            let responder = expect_watch_dhcpv4_configuration(&mut client_req_stream);
4612
4613            (client_req_stream, responder)
4614        };
4615
4616        // Make sure the DHCPv4 client is created on interface up.
4617        let ((mut client_stream, responder), ()) =
4618            futures::join!(handle_interface_watcher_event_fut, handle_route_set_fut);
4619
4620        let check_route = |fnet_routes::RouteV4 { destination, action, properties },
4621                           routers: &mut HashSet<_>| {
4622            assert_eq!(destination, fnet_dhcp_ext::DEFAULT_ADDR_PREFIX);
4623            let (outbound_interface, next_hop) = assert_matches!(action, fnet_routes::RouteActionV4::Forward(
4624                fnet_routes::RouteTargetV4 {
4625                    outbound_interface,
4626                    next_hop
4627                }
4628            ) => (outbound_interface, next_hop));
4629            assert_eq!(outbound_interface, INTERFACE_ID.get());
4630
4631            assert!(routers.insert(*next_hop.expect("specified next hop")));
4632
4633            assert_matches!(
4634                properties,
4635                fnet_routes::RoutePropertiesV4 {
4636                    specified_properties: Some(fnet_routes::SpecifiedRouteProperties {
4637                        metric: Some(fnet_routes::SpecifiedMetric::InheritedFromInterface(
4638                            fnet_routes::Empty
4639                        )),
4640                        ..
4641                    }),
4642                    ..
4643                }
4644            );
4645        };
4646
4647        let (expected_routers, route_set_request_stream) = {
4648            let dns_servers = vec![fidl_ip_v4!("192.0.2.1"), fidl_ip_v4!("192.0.2.2")];
4649            let routers = vec![fidl_ip_v4!("192.0.2.3"), fidl_ip_v4!("192.0.2.4")];
4650
4651            let (_asp_client, asp_server) = fidl::endpoints::create_proxy();
4652
4653            responder
4654                .send(fnet_dhcp::ClientWatchConfigurationResponse {
4655                    address: Some(fnet_dhcp::Address {
4656                        address: Some(DHCP_ADDRESS),
4657                        address_parameters: Some(dhcp_address_parameters()),
4658                        address_state_provider: Some(asp_server),
4659                        ..Default::default()
4660                    }),
4661                    dns_servers: Some(dns_servers.clone()),
4662                    routers: Some(routers.clone()),
4663                    ..Default::default()
4664                })
4665                .expect("send configuration update");
4666
4667            let (got_interface_id, got_response) = netcfg
4668                .dhcpv4_configuration_streams
4669                .next()
4670                .await
4671                .expect("DHCPv4 configuration streams should never be exhausted");
4672            assert_eq!(got_interface_id, INTERFACE_ID);
4673
4674            let dns_servers = dns_servers
4675                .into_iter()
4676                .map(|address| {
4677                    fnet::SocketAddress::Ipv4(fnet::Ipv4SocketAddress {
4678                        address,
4679                        port: DEFAULT_DNS_PORT,
4680                    })
4681                })
4682                .collect::<Vec<_>>();
4683
4684            let expect_add_default_routers = futures::stream::repeat(()).take(routers.len()).fold(
4685                (HashSet::new(), route_set_request_stream),
4686                |(mut routers, mut route_set), ()| async move {
4687                    let (route, responder) = assert_matches!(
4688                        route_set.next().await.expect("route set request stream should not be exhausted"),
4689                        Ok(fnet_routes_admin::RouteSetV4Request::AddRoute { route, responder}) => {
4690                            (route, responder)
4691                        }
4692                    );
4693                    check_route(route, &mut routers);
4694                    responder.send(Ok(true)).expect("send add route response");
4695                    (routers, route_set)
4696                },
4697            );
4698
4699            let expect_add_address_called = async {
4700                assert_matches!(
4701                    control.next().await.expect("control request stream should not be exhausted"),
4702                    Ok(fnet_interfaces_admin::ControlRequest::AddAddress {
4703                        address,
4704                        parameters,
4705                        address_state_provider: _,
4706                        control_handle: _,
4707                    })  => {
4708                        assert_eq!(address, fnet::Subnet::from_ext(DHCP_ADDRESS));
4709                        assert_eq!(parameters, dhcp_address_parameters());
4710                    }
4711                );
4712            };
4713
4714            let (dhcpv4_result, (), (added_routers, route_set_request_stream), ()) = future::join4(
4715                netcfg.handle_dhcpv4_configuration(got_interface_id, got_response),
4716                run_lookup_admin_once(&mut lookup_admin, &dns_servers),
4717                expect_add_default_routers,
4718                expect_add_address_called,
4719            )
4720            .await;
4721            assert_eq!(netcfg.dns_servers.consolidated(), dns_servers);
4722            assert_matches!(dhcpv4_result, Dhcpv4ConfigurationHandlerResult::ContinueOperation);
4723            let expected_routers = routers.iter().cloned().collect::<HashSet<_>>();
4724            assert_eq!(added_routers, expected_routers);
4725            (expected_routers, route_set_request_stream)
4726        };
4727
4728        // Netcfg always keeps the server hydrated with a WatchConfiguration
4729        // request.
4730        let _responder: fnet_dhcp::ClientWatchConfigurationResponder =
4731            expect_watch_dhcpv4_configuration(&mut client_stream);
4732
4733        let expect_delete_default_routers = futures::stream::repeat(())
4734            .take(expected_routers.len())
4735            .fold((HashSet::new(), route_set_request_stream), |(mut routers, mut route_set), ()| async move {
4736                let (route, responder) = assert_matches!(
4737                    route_set.next().await.expect("route set request stream should not be exhausted"),
4738                    Ok(fnet_routes_admin::RouteSetV4Request::RemoveRoute { route, responder }) => {
4739                        (route, responder)
4740                    }
4741                );
4742                check_route(route, &mut routers);
4743                responder.send(Ok(true)).expect("send del fwd entry response");
4744                (routers, route_set)
4745            });
4746
4747        // Make sure the DHCPv4 client is shutdown on interface disable/removal.
4748        let ((), (), (), (deleted_routers, mut route_set_request_stream)) = future::join4(
4749            async {
4750                if remove_interface {
4751                    netcfg
4752                        .handle_interface_watcher_event(
4753                            fnet_interfaces::Event::Removed(INTERFACE_ID.get()).into(),
4754                            &mut dns_watchers,
4755                            &mut virtualization::Stub,
4756                        )
4757                        .await
4758                        .expect("error handling interface removed event")
4759                } else {
4760                    handle_update(
4761                        &mut netcfg,
4762                        Some(false), /* online */
4763                        None,        /* addresses */
4764                        &mut dns_watchers,
4765                    )
4766                    .await
4767                }
4768            },
4769            async {
4770                match client_stream
4771                    .try_next()
4772                    .await
4773                    .expect("wait for next shutdown request")
4774                    .expect("netcfg should send shutdown request before closing client")
4775                {
4776                    req @ fnet_dhcp::ClientRequest::WatchConfiguration { responder: _ } => {
4777                        panic!("unexpected request = {:?}", req);
4778                    }
4779                    fnet_dhcp::ClientRequest::Shutdown { control_handle } => control_handle
4780                        .send_on_exit(fnet_dhcp::ClientExitReason::GracefulShutdown)
4781                        .expect("send client exit reason"),
4782                }
4783            },
4784            run_lookup_admin_once(&mut lookup_admin, &Vec::new()),
4785            expect_delete_default_routers,
4786        )
4787        .await;
4788        assert_eq!(netcfg.dns_servers.consolidated(), []);
4789        assert_eq!(deleted_routers, expected_routers);
4790
4791        // No more requests sent by NetCfg.
4792        assert_matches!(lookup_admin.next().now_or_never(), None);
4793        assert_matches!(route_set_request_stream.next().now_or_never(), None);
4794        let control_next = control.next().now_or_never();
4795        if remove_interface {
4796            assert_matches!(control_next, Some(None));
4797        } else {
4798            assert_matches!(control_next, None);
4799        }
4800    }
4801
4802    #[fuchsia::test]
4803    async fn test_dhcpv4_ignores_address_change() {
4804        let (
4805            mut netcfg,
4806            ServerEnds {
4807                lookup_admin: _,
4808                mut dhcpv4_client_provider,
4809                dhcpv6_client_provider: _,
4810                route_set_v4_provider,
4811                dhcpv4_server: _,
4812                fuchsia_networks: _,
4813            },
4814        ) = test_netcfg(NetcfgTestArgs {
4815            with_dhcpv4_client_provider: true,
4816            with_fuchsia_networks: false,
4817        })
4818        .expect("error creating test netcfg");
4819        let mut dns_watchers = DnsServerWatchers::empty();
4820
4821        let _noop_route_sets_task = fasync::Task::local(
4822            fnet_routes_ext::testutil::admin::serve_noop_route_sets::<Ipv4>(route_set_v4_provider),
4823        );
4824
4825        // Mock a new interface being discovered by NetCfg (we only need to make NetCfg aware of a
4826        // NIC with ID `INTERFACE_ID` to test DHCPv4).
4827        let (control, control_server_end) =
4828            fidl_fuchsia_net_interfaces_ext::admin::Control::create_endpoints()
4829                .expect("create endpoints");
4830        let port_class = fidl_fuchsia_hardware_network::PortClass::Virtual;
4831
4832        let new_host_fut = InterfaceState::new_host(
4833            test_interface_naming_id(),
4834            control,
4835            port_class.try_into().unwrap(),
4836            None,
4837            interface::ProvisioningType::Local,
4838        );
4839        let mut control = control_server_end.into_stream();
4840        let (new_host_result, ()) =
4841            futures::join!(new_host_fut, expect_get_interface_auth(&mut control));
4842
4843        assert_matches::assert_matches!(
4844            netcfg
4845                .interface_states
4846                .insert(INTERFACE_ID, new_host_result.expect("new_host should succeed")),
4847            None
4848        );
4849
4850        // Make sure the DHCPv4 client is created on interface up.
4851        netcfg
4852            .handle_interface_watcher_event(
4853                fnet_interfaces::Event::Added(fnet_interfaces::Properties {
4854                    id: Some(INTERFACE_ID.get()),
4855                    name: Some("testif01".to_string()),
4856                    port_class: Some(fnet_interfaces::PortClass::Device(
4857                        fidl_fuchsia_hardware_network::PortClass::Virtual,
4858                    )),
4859                    online: Some(true),
4860                    addresses: Some(Vec::new()),
4861                    has_default_ipv4_route: Some(false),
4862                    has_default_ipv6_route: Some(false),
4863                    ..Default::default()
4864                })
4865                .into(),
4866                &mut dns_watchers,
4867                &mut virtualization::Stub,
4868            )
4869            .await
4870            .expect("error handling interface added event");
4871
4872        let mut client_req_stream = match dhcpv4_client_provider
4873            .try_next()
4874            .await
4875            .expect("get next dhcpv4 client provider event")
4876            .expect("dhcpv4 client provider request")
4877        {
4878            fnet_dhcp::ClientProviderRequest::NewClient {
4879                interface_id,
4880                params,
4881                request,
4882                control_handle: _,
4883            } => {
4884                assert_eq!(interface_id, INTERFACE_ID.get());
4885                assert_eq!(params, dhcpv4::new_client_params());
4886                request.into_stream()
4887            }
4888            fnet_dhcp::ClientProviderRequest::CheckPresence { responder: _ } => {
4889                unreachable!("only called at startup")
4890            }
4891        };
4892
4893        let responder = expect_watch_dhcpv4_configuration(&mut client_req_stream);
4894        let (_asp_client, asp_server) = fidl::endpoints::create_proxy();
4895        responder
4896            .send(fnet_dhcp::ClientWatchConfigurationResponse {
4897                address: Some(fnet_dhcp::Address {
4898                    address: Some(DHCP_ADDRESS),
4899                    address_parameters: Some(dhcp_address_parameters()),
4900                    address_state_provider: Some(asp_server),
4901                    ..Default::default()
4902                }),
4903                ..Default::default()
4904            })
4905            .expect("send configuration update");
4906
4907        // A DHCP client should only be started or stopped when the interface is
4908        // enabled or disabled. If we change the addresses seen on the
4909        // interface, we shouldn't see requests to create a new DHCP client, and
4910        // the existing one should remain alive (netcfg shouldn't have sent a
4911        // shutdown request for it).
4912        for addresses in [
4913            vec![fidl_subnet!("192.2.2.2/28")],
4914            vec![],
4915            vec![fidl_subnet!("192.2.2.2/28"), fidl_subnet!("fe80::1234/64")],
4916        ] {
4917            handle_update(
4918                &mut netcfg,
4919                None,
4920                Some(addresses.into_iter().map(test_addr).collect()),
4921                &mut dns_watchers,
4922            )
4923            .await;
4924        }
4925        assert_matches!(dhcpv4_client_provider.next().now_or_never(), None);
4926        assert_matches!(client_req_stream.next().now_or_never(), None);
4927    }
4928
4929    /// Waits for a `SetDnsServers` request with the specified servers.
4930    async fn run_lookup_admin_once(
4931        server: &mut fnet_name::LookupAdminRequestStream,
4932        expected_servers: &Vec<fnet::SocketAddress>,
4933    ) {
4934        let req = server
4935            .try_next()
4936            .await
4937            .expect("get next lookup admin request")
4938            .expect("lookup admin request stream should not be exhausted");
4939        let (servers, responder) = assert_matches!(
4940            req,
4941            fnet_name::LookupAdminRequest::SetDnsServers { servers, responder } => {
4942                (servers, responder)
4943            }
4944        );
4945
4946        assert_eq!(expected_servers, &servers);
4947        responder.send(Ok(())).expect("send set dns servers response");
4948    }
4949
4950    #[test_case(Some(fnet_dhcp::ClientExitReason::UnableToOpenSocket), AllowClientRestart::Yes)]
4951    #[test_case(Some(fnet_dhcp::ClientExitReason::NetworkUnreachable), AllowClientRestart::Yes)]
4952    #[test_case(None, AllowClientRestart::Yes)]
4953    #[test_case(Some(fnet_dhcp::ClientExitReason::AddressRemovedByUser), AllowClientRestart::No)]
4954    #[fuchsia::test]
4955    async fn dhcpv4_handles_client_exit(
4956        exit_reason: Option<fnet_dhcp::ClientExitReason>,
4957        expected_allow_restart: AllowClientRestart,
4958    ) {
4959        let (
4960            mut netcfg,
4961            ServerEnds {
4962                lookup_admin: _,
4963                mut dhcpv4_client_provider,
4964                dhcpv6_client_provider: _,
4965                route_set_v4_provider,
4966                dhcpv4_server: _,
4967                fuchsia_networks: _,
4968            },
4969        ) = test_netcfg(NetcfgTestArgs {
4970            with_dhcpv4_client_provider: true,
4971            with_fuchsia_networks: false,
4972        })
4973        .expect("error creating test netcfg");
4974        let mut dns_watchers = DnsServerWatchers::empty();
4975
4976        let _noop_route_sets_task = fasync::Task::local(
4977            fnet_routes_ext::testutil::admin::serve_noop_route_sets::<Ipv4>(route_set_v4_provider),
4978        );
4979
4980        // Mock a new interface being discovered by NetCfg (we only need to make NetCfg aware of a
4981        // NIC with ID `INTERFACE_ID` to test DHCPv4).
4982        let (control, control_server_end) =
4983            fidl_fuchsia_net_interfaces_ext::admin::Control::create_endpoints()
4984                .expect("create endpoints");
4985        let port_class = fidl_fuchsia_hardware_network::PortClass::Virtual;
4986        let new_host_fut = InterfaceState::new_host(
4987            test_interface_naming_id(),
4988            control,
4989            port_class.try_into().unwrap(),
4990            None,
4991            interface::ProvisioningType::Local,
4992        );
4993        let mut control = control_server_end.into_stream();
4994        let (new_host_result, ()) =
4995            futures::join!(new_host_fut, expect_get_interface_auth(&mut control));
4996        assert_matches::assert_matches!(
4997            netcfg
4998                .interface_states
4999                .insert(INTERFACE_ID, new_host_result.expect("new_host should succeed")),
5000            None
5001        );
5002
5003        // Make sure the DHCPv4 client is created on interface up.
5004        let (client_stream, control_handle, responder) = {
5005            netcfg
5006                .handle_interface_watcher_event(
5007                    fnet_interfaces::Event::Added(fnet_interfaces::Properties {
5008                        id: Some(INTERFACE_ID.get()),
5009                        name: Some("testif01".to_string()),
5010                        port_class: Some(fnet_interfaces::PortClass::Device(
5011                            fidl_fuchsia_hardware_network::PortClass::Virtual,
5012                        )),
5013                        online: Some(true),
5014                        addresses: Some(Vec::new()),
5015                        has_default_ipv4_route: Some(false),
5016                        has_default_ipv6_route: Some(false),
5017                        ..Default::default()
5018                    })
5019                    .into(),
5020                    &mut dns_watchers,
5021                    &mut virtualization::Stub,
5022                )
5023                .await
5024                .expect("error handling interface added event");
5025
5026            let (mut client_req_stream, control_handle) = match dhcpv4_client_provider
5027                .try_next()
5028                .await
5029                .expect("get next dhcpv4 client provider event")
5030                .expect("dhcpv4 client provider request")
5031            {
5032                fnet_dhcp::ClientProviderRequest::NewClient {
5033                    interface_id,
5034                    params,
5035                    request,
5036                    control_handle: _,
5037                } => {
5038                    assert_eq!(interface_id, INTERFACE_ID.get());
5039                    assert_eq!(params, dhcpv4::new_client_params());
5040                    request.into_stream_and_control_handle()
5041                }
5042                fnet_dhcp::ClientProviderRequest::CheckPresence { responder: _ } => {
5043                    unreachable!("only called at startup")
5044                }
5045            };
5046
5047            let responder = expect_watch_dhcpv4_configuration(&mut client_req_stream);
5048            (client_req_stream, control_handle, responder)
5049        };
5050
5051        // Simulate the client exiting with an error.
5052        match exit_reason {
5053            Some(reason) => {
5054                control_handle.send_on_exit(reason).expect("sending OnExit should succeed");
5055            }
5056            None => {}
5057        }
5058
5059        drop((client_stream, control_handle, responder));
5060
5061        let (got_interface_id, got_response) = netcfg
5062            .dhcpv4_configuration_streams
5063            .next()
5064            .await
5065            .expect("DHCPv4 configuration streams should never be exhausted");
5066        assert_eq!(got_interface_id, INTERFACE_ID);
5067
5068        let dhcpv4_result =
5069            netcfg.handle_dhcpv4_configuration(got_interface_id, got_response).await;
5070        assert_eq!(
5071            dhcpv4_result,
5072            Dhcpv4ConfigurationHandlerResult::ClientStopped(expected_allow_restart)
5073        );
5074    }
5075
5076    #[fuchsia::test]
5077    async fn test_dhcpv6() {
5078        let (mut netcfg, mut servers) = test_netcfg(NetcfgTestArgs {
5079            with_dhcpv4_client_provider: false,
5080            with_fuchsia_networks: false,
5081        })
5082        .expect("error creating test netcfg");
5083        let mut dns_watchers = DnsServerWatchers::empty();
5084
5085        // Mock a fake DNS update from the DHCPv6 client.
5086        async fn update_dns(netcfg: &mut NetCfg<'static>, servers: &mut ServerEnds) {
5087            let ((), ()) = future::join(
5088                netcfg.update_dns_servers(
5089                    DHCPV6_DNS_SOURCE,
5090                    vec![fnet_name::DnsServer_ {
5091                        address: Some(DNS_SERVER2),
5092                        source: Some(fnet_name::DnsServerSource::Dhcpv6(
5093                            fnet_name::Dhcpv6DnsServerSource {
5094                                source_interface: Some(INTERFACE_ID.get()),
5095                                ..Default::default()
5096                            },
5097                        )),
5098                        ..Default::default()
5099                    }],
5100                ),
5101                run_lookup_admin_once(&mut servers.lookup_admin, &vec![DNS_SERVER2, DNS_SERVER1]),
5102            )
5103            .await;
5104        }
5105
5106        // Mock a fake DNS update from the netstack.
5107        let netstack_servers = vec![DNS_SERVER1];
5108        let ((), ()) = future::join(
5109            netcfg.update_dns_servers(
5110                DnsServersUpdateSource::Netstack,
5111                vec![fnet_name::DnsServer_ {
5112                    address: Some(DNS_SERVER1),
5113                    source: Some(fnet_name::DnsServerSource::StaticSource(
5114                        fnet_name::StaticDnsServerSource::default(),
5115                    )),
5116                    ..Default::default()
5117                }],
5118            ),
5119            run_lookup_admin_once(&mut servers.lookup_admin, &netstack_servers),
5120        )
5121        .await;
5122
5123        // Mock a new interface being discovered by NetCfg (we only need to make NetCfg aware of a
5124        // NIC with ID `INTERFACE_ID` to test DHCPv6).
5125        let (control, control_server_end) =
5126            fidl_fuchsia_net_interfaces_ext::admin::Control::create_endpoints()
5127                .expect("create endpoints");
5128        let mut control_request_stream = control_server_end.into_stream();
5129        let port_class = fidl_fuchsia_hardware_network::PortClass::Virtual;
5130        let (new_host_result, ()) = futures::join!(
5131            InterfaceState::new_host(
5132                test_interface_naming_id(),
5133                control,
5134                port_class.try_into().unwrap(),
5135                None,
5136                interface::ProvisioningType::Local,
5137            ),
5138            expect_get_interface_auth(&mut control_request_stream)
5139        );
5140        assert_matches::assert_matches!(
5141            netcfg
5142                .interface_states
5143                .insert(INTERFACE_ID, new_host_result.expect("new_host should succeed")),
5144            None
5145        );
5146
5147        // Should start the DHCPv6 client when we get an interface changed event that shows the
5148        // interface as up with an link-local address.
5149        netcfg
5150            .handle_interface_watcher_event(
5151                fnet_interfaces::Event::Added(fnet_interfaces::Properties {
5152                    id: Some(INTERFACE_ID.get()),
5153                    name: Some("testif01".to_string()),
5154                    port_class: Some(fnet_interfaces::PortClass::Device(port_class)),
5155                    online: Some(true),
5156                    addresses: Some(ipv6addrs(Some(LINK_LOCAL_SOCKADDR1))),
5157                    has_default_ipv4_route: Some(false),
5158                    has_default_ipv6_route: Some(false),
5159                    ..Default::default()
5160                })
5161                .into(),
5162                &mut dns_watchers,
5163                &mut virtualization::Stub,
5164            )
5165            .await
5166            .expect("error handling interface added event with interface up and sockaddr1");
5167        let mut client_server = check_new_dhcpv6_client(
5168            &mut servers.dhcpv6_client_provider,
5169            INTERFACE_ID,
5170            LINK_LOCAL_SOCKADDR1,
5171            None,
5172            &mut dns_watchers,
5173        )
5174        .await
5175        .expect("error checking for new client with sockaddr1");
5176        update_dns(&mut netcfg, &mut servers).await;
5177
5178        // Not having any more link local IPv6 addresses should terminate the client.
5179        let ((), ()) = future::join(
5180            handle_interface_changed_event(
5181                &mut netcfg,
5182                &mut dns_watchers,
5183                None,
5184                Some(ipv6addrs(None)),
5185            )
5186            .map(|r| r.expect("error handling interface changed event with sockaddr1 removed")),
5187            run_lookup_admin_once(&mut servers.lookup_admin, &netstack_servers),
5188        )
5189        .await;
5190        assert!(!dns_watchers.contains_key(&DHCPV6_DNS_SOURCE), "should not have a watcher");
5191        assert_matches::assert_matches!(client_server.try_next().await, Ok(None));
5192
5193        // Should start a new DHCPv6 client when we get an interface changed event that shows the
5194        // interface as up with an link-local address.
5195        handle_interface_changed_event(
5196            &mut netcfg,
5197            &mut dns_watchers,
5198            None,
5199            Some(ipv6addrs(Some(LINK_LOCAL_SOCKADDR2))),
5200        )
5201        .await
5202        .expect("error handling netstack event with sockaddr2 added");
5203        let mut client_server = check_new_dhcpv6_client(
5204            &mut servers.dhcpv6_client_provider,
5205            INTERFACE_ID,
5206            LINK_LOCAL_SOCKADDR2,
5207            None,
5208            &mut dns_watchers,
5209        )
5210        .await
5211        .expect("error checking for new client with sockaddr2");
5212        update_dns(&mut netcfg, &mut servers).await;
5213
5214        // Interface being down should terminate the client.
5215        let ((), ()) = future::join(
5216            handle_interface_changed_event(
5217                &mut netcfg,
5218                &mut dns_watchers,
5219                Some(false), /* down */
5220                None,
5221            )
5222            .map(|r| r.expect("error handling interface changed event with interface down")),
5223            run_lookup_admin_once(&mut servers.lookup_admin, &netstack_servers),
5224        )
5225        .await;
5226        assert!(!dns_watchers.contains_key(&DHCPV6_DNS_SOURCE), "should not have a watcher");
5227        assert_matches::assert_matches!(client_server.try_next().await, Ok(None));
5228
5229        // Should start a new DHCPv6 client when we get an interface changed event that shows the
5230        // interface as up with an link-local address.
5231        handle_interface_changed_event(
5232            &mut netcfg,
5233            &mut dns_watchers,
5234            Some(true), /* up */
5235            None,
5236        )
5237        .await
5238        .expect("error handling interface up event");
5239        let mut client_server = check_new_dhcpv6_client(
5240            &mut servers.dhcpv6_client_provider,
5241            INTERFACE_ID,
5242            LINK_LOCAL_SOCKADDR2,
5243            None,
5244            &mut dns_watchers,
5245        )
5246        .await
5247        .expect("error checking for new client with sockaddr2 after interface up again");
5248        update_dns(&mut netcfg, &mut servers).await;
5249
5250        // Should start a new DHCPv6 client when we get an interface changed event that shows the
5251        // interface as up with a new link-local address.
5252        let ((), ()) = future::join(
5253            handle_interface_changed_event(
5254                &mut netcfg,
5255                &mut dns_watchers,
5256                None,
5257                Some(ipv6addrs(Some(LINK_LOCAL_SOCKADDR1))),
5258            )
5259            .map(|r| {
5260                r.expect("error handling interface change event with sockaddr1 replacing sockaddr2")
5261            }),
5262            run_lookup_admin_once(&mut servers.lookup_admin, &netstack_servers),
5263        )
5264        .await;
5265        assert_matches::assert_matches!(client_server.try_next().await, Ok(None));
5266        let _client_server: fnet_dhcpv6::ClientRequestStream = check_new_dhcpv6_client(
5267            &mut servers.dhcpv6_client_provider,
5268            INTERFACE_ID,
5269            LINK_LOCAL_SOCKADDR1,
5270            None,
5271            &mut dns_watchers,
5272        )
5273        .await
5274        .expect("error checking for new client with sockaddr1 after address change");
5275        update_dns(&mut netcfg, &mut servers).await;
5276
5277        // Complete the DNS server watcher then start a new one.
5278        let ((), ()) = future::join(
5279            netcfg
5280                .handle_dns_server_watcher_done(DHCPV6_DNS_SOURCE, &mut dns_watchers)
5281                .map(|r| r.expect("error handling completion of dns server watcher")),
5282            run_lookup_admin_once(&mut servers.lookup_admin, &netstack_servers),
5283        )
5284        .await;
5285        assert!(!dns_watchers.contains_key(&DHCPV6_DNS_SOURCE), "should not have a watcher");
5286        handle_interface_changed_event(
5287            &mut netcfg,
5288            &mut dns_watchers,
5289            None,
5290            Some(ipv6addrs(Some(LINK_LOCAL_SOCKADDR2))),
5291        )
5292        .await
5293        .expect("error handling interface change event with sockaddr2 replacing sockaddr1");
5294        let mut client_server = check_new_dhcpv6_client(
5295            &mut servers.dhcpv6_client_provider,
5296            INTERFACE_ID,
5297            LINK_LOCAL_SOCKADDR2,
5298            None,
5299            &mut dns_watchers,
5300        )
5301        .await
5302        .expect("error checking for new client with sockaddr2 after completing dns watcher");
5303        update_dns(&mut netcfg, &mut servers).await;
5304
5305        // An event that indicates the interface is removed should stop the client.
5306        let ((), ()) = future::join(
5307            netcfg
5308                .handle_interface_watcher_event(
5309                    fnet_interfaces::Event::Removed(INTERFACE_ID.get()).into(),
5310                    &mut dns_watchers,
5311                    &mut virtualization::Stub,
5312                )
5313                .map(|r| r.expect("error handling interface removed event")),
5314            run_lookup_admin_once(&mut servers.lookup_admin, &netstack_servers),
5315        )
5316        .await;
5317        assert!(!dns_watchers.contains_key(&DHCPV6_DNS_SOURCE), "should not have a watcher");
5318        assert_matches::assert_matches!(client_server.try_next().await, Ok(None));
5319        assert!(!netcfg.interface_states.contains_key(&INTERFACE_ID));
5320    }
5321
5322    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
5323    enum InterfaceKind {
5324        Unowned,
5325        NonHost,
5326        Host { upstream: bool },
5327    }
5328
5329    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
5330    struct InterfaceConfig {
5331        id: InterfaceId,
5332        kind: InterfaceKind,
5333    }
5334
5335    const UPSTREAM_INTERFACE_CONFIG: InterfaceConfig = InterfaceConfig {
5336        id: InterfaceId::new(1).unwrap(),
5337        kind: InterfaceKind::Host { upstream: true },
5338    };
5339
5340    const ALLOWED_UPSTREAM_DEVICE_CLASS: DeviceClass = DeviceClass::Ethernet;
5341    const DISALLOWED_UPSTREAM_DEVICE_CLASS: DeviceClass = DeviceClass::Virtual;
5342
5343    fn dhcpv6_sockaddr(interface_id: InterfaceId) -> fnet::Ipv6SocketAddress {
5344        let mut address = fidl_ip_v6!("fe80::");
5345        let interface_id: u8 =
5346            interface_id.get().try_into().expect("interface ID should fit into u8");
5347        *address.addr.last_mut().expect("IPv6 address is empty") = interface_id;
5348        fnet::Ipv6SocketAddress {
5349            address: address,
5350            port: fnet_dhcpv6::DEFAULT_CLIENT_PORT,
5351            zone_index: interface_id.into(),
5352        }
5353    }
5354
5355    #[test_case(
5356        Some(UPSTREAM_INTERFACE_CONFIG.id),
5357        None; "specific_interface_no_preferred_prefix_len")]
5358    #[test_case(
5359        None,
5360        None; "all_upstreams_no_preferred_prefix_len")]
5361    #[test_case(
5362        Some(UPSTREAM_INTERFACE_CONFIG.id),
5363        Some(2); "specific_interface_preferred_prefix_len")]
5364    #[test_case(
5365        None,
5366        Some(1); "all_upstreams_preferred_prefix_len")]
5367    #[fuchsia::test]
5368    async fn test_dhcpv6_acquire_prefix(
5369        interface_id: Option<InterfaceId>,
5370        preferred_prefix_len: Option<u8>,
5371    ) {
5372        const INTERFACE_CONFIGS: [InterfaceConfig; 5] = [
5373            UPSTREAM_INTERFACE_CONFIG,
5374            InterfaceConfig {
5375                id: InterfaceId::new(2).unwrap(),
5376                kind: InterfaceKind::Host { upstream: true },
5377            },
5378            InterfaceConfig {
5379                id: InterfaceId::new(3).unwrap(),
5380                kind: InterfaceKind::Host { upstream: false },
5381            },
5382            InterfaceConfig { id: InterfaceId::new(4).unwrap(), kind: InterfaceKind::Unowned },
5383            InterfaceConfig { id: InterfaceId::new(5).unwrap(), kind: InterfaceKind::NonHost },
5384        ];
5385
5386        let (
5387            mut netcfg,
5388            ServerEnds {
5389                lookup_admin: lookup_admin_request_stream,
5390                dhcpv4_client_provider: _,
5391                dhcpv6_client_provider: mut dhcpv6_client_provider_request_stream,
5392                route_set_v4_provider: _,
5393                dhcpv4_server: _,
5394                fuchsia_networks: _,
5395            },
5396        ) = test_netcfg(NetcfgTestArgs {
5397            with_dhcpv4_client_provider: false,
5398            with_fuchsia_networks: false,
5399        })
5400        .expect("error creating test netcfg");
5401        let allowed_upstream_device_classes = HashSet::from([ALLOWED_UPSTREAM_DEVICE_CLASS]);
5402        netcfg.allowed_upstream_device_classes = &allowed_upstream_device_classes;
5403        let mut dns_watchers = DnsServerWatchers::empty();
5404
5405        struct TestInterfaceState {
5406            _control_server_end:
5407                Option<fidl::endpoints::ServerEnd<fnet_interfaces_admin::ControlMarker>>,
5408            dhcpv6_client_request_stream: Option<fnet_dhcpv6::ClientRequestStream>,
5409            kind: InterfaceKind,
5410        }
5411        let mut interface_states = HashMap::new();
5412        for InterfaceConfig { id, kind } in INTERFACE_CONFIGS.into_iter() {
5413            // Mock new interfaces being discovered by NetCfg as needed.
5414            let (device_class, control_server_end) = match kind {
5415                InterfaceKind::Unowned => (DISALLOWED_UPSTREAM_DEVICE_CLASS, None),
5416                InterfaceKind::NonHost => {
5417                    let (control, control_server_end) =
5418                        fidl_fuchsia_net_interfaces_ext::admin::Control::create_endpoints()
5419                            .expect("create endpoints");
5420                    let device_class = DeviceClass::WlanAp;
5421                    assert_matches::assert_matches!(
5422                        netcfg.interface_states.insert(
5423                            id.try_into().expect("interface ID should be nonzero"),
5424                            InterfaceState::new_wlan_ap(
5425                                test_interface_naming_id(),
5426                                control,
5427                                device_class,
5428                                interface::ProvisioningType::Local
5429                            )
5430                        ),
5431                        None
5432                    );
5433                    (device_class, Some(control_server_end))
5434                }
5435                InterfaceKind::Host { upstream } => {
5436                    let (control, control_server_end) =
5437                        fidl_fuchsia_net_interfaces_ext::admin::Control::create_endpoints()
5438                            .expect("create endpoints");
5439                    let device_class = if upstream {
5440                        ALLOWED_UPSTREAM_DEVICE_CLASS
5441                    } else {
5442                        DISALLOWED_UPSTREAM_DEVICE_CLASS
5443                    };
5444
5445                    let mut control_request_stream = control_server_end.into_stream();
5446
5447                    let (new_host_result, ()) = futures::join!(
5448                        InterfaceState::new_host(
5449                            test_interface_naming_id(),
5450                            control,
5451                            device_class,
5452                            None,
5453                            interface::ProvisioningType::Local,
5454                        ),
5455                        expect_get_interface_auth(&mut control_request_stream)
5456                    );
5457
5458                    assert_matches::assert_matches!(
5459                        netcfg.interface_states.insert(
5460                            id.try_into().expect("interface ID should be nonzero"),
5461                            new_host_result.expect("new_host should succeed")
5462                        ),
5463                        None
5464                    );
5465
5466                    let (control_inner, _is_terminated) = control_request_stream.into_inner();
5467                    let control_inner = std::sync::Arc::try_unwrap(control_inner)
5468                        .expect("recover original server end");
5469
5470                    (
5471                        device_class,
5472                        Some(
5473                            fidl::endpoints::ServerEnd
5474                                ::<fnet_interfaces_admin::ControlMarker>
5475                                ::from(control_inner.into_channel().into_zx_channel()),
5476                        ),
5477                    )
5478                }
5479            };
5480
5481            let sockaddr = dhcpv6_sockaddr(id);
5482
5483            // Fake an interface added event.
5484            netcfg
5485                .handle_interface_watcher_event(
5486                    fnet_interfaces::Event::Added(fnet_interfaces::Properties {
5487                        id: Some(id.get()),
5488                        name: Some(format!("testif{}", id)),
5489                        port_class: Some(fnet_interfaces::PortClass::Device(
5490                            device_class.into(),
5491                        )),
5492                        online: Some(true),
5493                        addresses: Some(ipv6addrs(Some(sockaddr))),
5494                        has_default_ipv4_route: Some(false),
5495                        has_default_ipv6_route: Some(false),
5496                        ..Default::default()
5497                    }).into(),
5498                    &mut dns_watchers,
5499                    &mut virtualization::Stub,
5500                )
5501                .await
5502                .unwrap_or_else(|e| panic!("error handling interface added event for {} with interface up and link-local addr: {}", id, e));
5503
5504            // Expect DHCPv6 client to have started on host interfaces.
5505            let dhcpv6_client_request_stream = match kind {
5506                InterfaceKind::Unowned | InterfaceKind::NonHost => None,
5507                InterfaceKind::Host { upstream: _ } => {
5508                    let request_stream = check_new_dhcpv6_client(
5509                        &mut dhcpv6_client_provider_request_stream,
5510                        id,
5511                        sockaddr,
5512                        None,
5513                        &mut dns_watchers,
5514                    )
5515                    .await
5516                    .unwrap_or_else(|e| {
5517                        panic!("error checking for new DHCPv6 client on interface {}: {}", id, e)
5518                    });
5519                    Some(request_stream)
5520                }
5521            };
5522            assert!(
5523                interface_states
5524                    .insert(
5525                        id,
5526                        TestInterfaceState {
5527                            _control_server_end: control_server_end,
5528                            dhcpv6_client_request_stream,
5529                            kind,
5530                        }
5531                    )
5532                    .is_none()
5533            );
5534        }
5535
5536        async fn assert_dhcpv6_clients_stopped(
5537            interface_states: &mut HashMap<InterfaceId, TestInterfaceState>,
5538            interface_id: Option<InterfaceId>,
5539        ) -> HashSet<InterfaceId> {
5540            futures::stream::iter(
5541                interface_states.iter_mut(),
5542            ).filter_map(|(
5543                id,
5544                TestInterfaceState { _control_server_end, dhcpv6_client_request_stream, kind },
5545            )| async move {
5546                // Expect DHCPv6 to be restarted iff the interface matches the
5547                // interface used to acquire prefix on, or is an upstream
5548                // capable host if no interface was specified to acquire
5549                // prefixes from.
5550                let expect_restart = interface_id.map_or_else(
5551                    || *kind == InterfaceKind::Host { upstream: true },
5552                    |want_id| want_id == *id,
5553                );
5554                if expect_restart {
5555                    let res = dhcpv6_client_request_stream
5556                        .take()
5557                        .unwrap_or_else(|| panic!("interface {} DHCPv6 client provider request stream missing when expecting restart", id))
5558                        .try_next().await;
5559                    assert_matches!(res, Ok(None));
5560                    Some(*id)
5561                } else {
5562                    if let Some(req_stream) = dhcpv6_client_request_stream.as_mut() {
5563                        // We do not expect DHCPv6 to restart and want to make
5564                        // sure that the stream has not ended. We can't `.await`
5565                        // because that will result in us blocking forever on
5566                        // the next event (since the DHCPv6 client did not close
5567                        // so the stream is still open and blocked).
5568                        assert_matches!(req_stream.try_next().now_or_never(), None, "interface_id={:?}, kind={:?}, id={:?}", interface_id, kind, id);
5569                        assert!(!req_stream.is_terminated());
5570                    }
5571                    None
5572                }
5573            })
5574            .collect().await
5575        }
5576
5577        async fn assert_dhcpv6_clients_started(
5578            count: usize,
5579            dhcpv6_client_provider_request_stream: &mut fnet_dhcpv6::ClientProviderRequestStream,
5580            interface_states: &mut HashMap<InterfaceId, TestInterfaceState>,
5581            want_pd_config: Option<fnet_dhcpv6::PrefixDelegationConfig>,
5582        ) -> HashSet<InterfaceId> {
5583            dhcpv6_client_provider_request_stream
5584                .map(|res| res.expect("DHCPv6 ClientProvider request stream error"))
5585                .take(count)
5586                .then(
5587                    |fnet_dhcpv6::ClientProviderRequest::NewClient {
5588                         params:
5589                             fnet_dhcpv6::NewClientParams { interface_id, address: _, config, .. },
5590                         request,
5591                         control_handle: _,
5592                     }| async move {
5593                        let mut new_stream = request.into_stream();
5594                        // NetCfg always keeps the server hydrated with a pending hanging-get.
5595                        expect_watch_prefixes(&mut new_stream).await;
5596                        (interface_id, config, new_stream)
5597                    },
5598                )
5599                .map(|(interface_id, config, new_stream)| {
5600                    let interface_id = interface_id
5601                        .expect("interface ID missing in new DHCPv6 client request")
5602                        .try_into()
5603                        .expect("interface ID should be nonzero");
5604                    let TestInterfaceState {
5605                        _control_server_end,
5606                        dhcpv6_client_request_stream,
5607                        kind: _,
5608                    } = interface_states.get_mut(&interface_id).unwrap_or_else(|| {
5609                        panic!("interface {} must be present in map", interface_id)
5610                    });
5611                    assert!(
5612                        std::mem::replace(dhcpv6_client_request_stream, Some(new_stream)).is_none()
5613                    );
5614
5615                    let config = fnet_dhcpv6_ext::ClientConfig::try_from(
5616                        config.expect("ClientConfig must be present"),
5617                    )
5618                    .expect("ClientConfig should pass FIDL table validation");
5619                    assert_eq!(
5620                        config,
5621                        fnet_dhcpv6_ext::ClientConfig {
5622                            information_config: fnet_dhcpv6_ext::InformationConfig {
5623                                dns_servers: true,
5624                            },
5625                            non_temporary_address_config: Default::default(),
5626                            prefix_delegation_config: want_pd_config.clone(),
5627                        }
5628                    );
5629                    interface_id
5630                })
5631                .collect()
5632                .await
5633        }
5634
5635        async fn handle_watch_prefix_with_fake(
5636            netcfg: &mut NetCfg<'_>,
5637            dns_watchers: &mut DnsServerWatchers<'_>,
5638            interface_id: InterfaceId,
5639            fake_prefixes: Vec<fnet_dhcpv6::Prefix>,
5640            // Used to test handling PrefixControl.WatchPrefix & Client.WatchPrefixes
5641            // events in different orders.
5642            handle_dhcpv6_prefixes_before_watch_prefix: bool,
5643        ) {
5644            let responder = netcfg
5645                .dhcpv6_prefix_provider_handler
5646                .as_mut()
5647                .map(dhcpv6::PrefixProviderHandler::try_next_prefix_control_request)
5648                .expect("DHCPv6 prefix provider handler must be present")
5649                .await
5650                .expect("prefix provider request")
5651                .expect("PrefixProvider request stream exhausted")
5652                .into_watch_prefix()
5653                .expect("request not WatchPrefix");
5654
5655            async fn handle_dhcpv6_prefixes(
5656                netcfg: &mut NetCfg<'_>,
5657                dns_watchers: &mut DnsServerWatchers<'_>,
5658                interface_id: InterfaceId,
5659                fake_prefixes: Vec<fnet_dhcpv6::Prefix>,
5660            ) {
5661                netcfg
5662                    .handle_dhcpv6_prefixes(interface_id, Ok(fake_prefixes), dns_watchers)
5663                    .await
5664                    .expect("handle DHCPv6 prefixes")
5665            }
5666
5667            async fn handle_watch_prefix(
5668                netcfg: &mut NetCfg<'_>,
5669                dns_watchers: &mut DnsServerWatchers<'_>,
5670                responder: fnet_dhcpv6::PrefixControlWatchPrefixResponder,
5671            ) {
5672                netcfg
5673                    .handle_watch_prefix(responder, dns_watchers)
5674                    .await
5675                    .expect("handle watch prefix")
5676            }
5677
5678            if handle_dhcpv6_prefixes_before_watch_prefix {
5679                handle_dhcpv6_prefixes(netcfg, dns_watchers, interface_id, fake_prefixes).await;
5680                handle_watch_prefix(netcfg, dns_watchers, responder).await;
5681            } else {
5682                handle_watch_prefix(netcfg, dns_watchers, responder).await;
5683                handle_dhcpv6_prefixes(netcfg, dns_watchers, interface_id, fake_prefixes).await;
5684            }
5685        }
5686
5687        // Making an AcquirePrefix call should trigger restarting DHCPv6 w/ PD.
5688        let (prefix_control, server_end) =
5689            fidl::endpoints::create_proxy::<fnet_dhcpv6::PrefixControlMarker>();
5690        let mut lookup_admin_fut = lookup_admin_request_stream
5691            .try_for_each(|req| {
5692                let (_, responder): (Vec<fnet::SocketAddress>, _) =
5693                    req.into_set_dns_servers().expect("request must be SetDnsServers");
5694                responder.send(Ok(())).expect("send SetDnsServers response");
5695                futures::future::ok(())
5696            })
5697            .fuse();
5698
5699        {
5700            let acquire_prefix_fut = netcfg.handle_dhcpv6_acquire_prefix(
5701                fnet_dhcpv6::AcquirePrefixConfig {
5702                    interface_id: interface_id.map(InterfaceId::get),
5703                    preferred_prefix_len,
5704                    ..Default::default()
5705                },
5706                server_end,
5707                &mut dns_watchers,
5708            );
5709            futures::select! {
5710                res = acquire_prefix_fut.fuse() => {
5711                    res.expect("acquire DHCPv6 prefix")
5712                }
5713                res = lookup_admin_fut => {
5714                    panic!("fuchsia.net.name/LookupAdmin request stream exhausted unexpectedly: {:?}", res)
5715                },
5716            };
5717            // Expect DHCPv6 client to have been restarted on the appropriate
5718            // interfaces with PD configured.
5719            let stopped = assert_dhcpv6_clients_stopped(&mut interface_states, interface_id).await;
5720            let started = assert_dhcpv6_clients_started(
5721                stopped.len(),
5722                dhcpv6_client_provider_request_stream.by_ref(),
5723                &mut interface_states,
5724                Some(preferred_prefix_len.map_or(
5725                    fnet_dhcpv6::PrefixDelegationConfig::Empty(fnet_dhcpv6::Empty),
5726                    fnet_dhcpv6::PrefixDelegationConfig::PrefixLength,
5727                )),
5728            )
5729            .await;
5730            assert_eq!(started, stopped);
5731
5732            // Yield a prefix to the PrefixControl.
5733            let prefix = fnet_dhcpv6::Prefix {
5734                prefix: fidl_ip_v6_with_prefix!("abcd::/64"),
5735                lifetimes: fnet_dhcpv6::Lifetimes { valid_until: 123, preferred_until: 456 },
5736            };
5737            let any_eligible_interface = *started.iter().next().expect(
5738                "must have configured DHCPv6 client to perform PD on at least one interface",
5739            );
5740            let (watch_prefix_res, ()) = futures::future::join(
5741                prefix_control.watch_prefix(),
5742                handle_watch_prefix_with_fake(
5743                    &mut netcfg,
5744                    &mut dns_watchers,
5745                    any_eligible_interface,
5746                    vec![prefix.clone()],
5747                    true, /* handle_dhcpv6_prefixes_before_watch_prefix */
5748                ),
5749            )
5750            .await;
5751            assert_matches!(watch_prefix_res, Ok(fnet_dhcpv6::PrefixEvent::Assigned(got_prefix)) => {
5752                assert_eq!(got_prefix, prefix);
5753            });
5754
5755            // Yield a different prefix from what was yielded before.
5756            let renewed_prefix = fnet_dhcpv6::Prefix {
5757                lifetimes: fnet_dhcpv6::Lifetimes {
5758                    valid_until: 123_123,
5759                    preferred_until: 456_456,
5760                },
5761                ..prefix
5762            };
5763            let (watch_prefix_res, ()) = futures::future::join(
5764                prefix_control.watch_prefix(),
5765                handle_watch_prefix_with_fake(
5766                    &mut netcfg,
5767                    &mut dns_watchers,
5768                    any_eligible_interface,
5769                    vec![renewed_prefix.clone()],
5770                    false, /* handle_dhcpv6_prefixes_before_watch_prefix */
5771                ),
5772            )
5773            .await;
5774            assert_matches!(watch_prefix_res, Ok(fnet_dhcpv6::PrefixEvent::Assigned(
5775                got_prefix,
5776            )) => {
5777                assert_eq!(got_prefix, renewed_prefix);
5778            });
5779            let (watch_prefix_res, ()) = futures::future::join(
5780                prefix_control.watch_prefix(),
5781                handle_watch_prefix_with_fake(
5782                    &mut netcfg,
5783                    &mut dns_watchers,
5784                    any_eligible_interface,
5785                    vec![],
5786                    true, /* handle_dhcpv6_prefixes_before_watch_prefix */
5787                ),
5788            )
5789            .await;
5790            assert_matches!(
5791                watch_prefix_res,
5792                Ok(fnet_dhcpv6::PrefixEvent::Unassigned(fnet_dhcpv6::Empty))
5793            );
5794        }
5795
5796        // Closing the PrefixControl should trigger clients running DHCPv6-PD to
5797        // be restarted.
5798        {
5799            futures::select! {
5800                () = netcfg.on_dhcpv6_prefix_control_close(&mut dns_watchers).fuse() => {},
5801                res = lookup_admin_fut => {
5802                    panic!(
5803                        "fuchsia.net.name/LookupAdmin request stream exhausted unexpectedly: {:?}",
5804                        res,
5805                    )
5806                },
5807            }
5808            let stopped = assert_dhcpv6_clients_stopped(&mut interface_states, interface_id).await;
5809            let started = assert_dhcpv6_clients_started(
5810                stopped.len(),
5811                dhcpv6_client_provider_request_stream.by_ref(),
5812                &mut interface_states,
5813                None,
5814            )
5815            .await;
5816            assert_eq!(started, stopped);
5817        }
5818    }
5819
5820    // Tests that DHCPv6 clients are configured to perform PD on eligible
5821    // upstream-providing interfaces while a `PrefixControl` channel is open.
5822    #[fuchsia::test]
5823    async fn test_dhcpv6_pd_on_added_upstream() {
5824        let (
5825            mut netcfg,
5826            ServerEnds {
5827                lookup_admin: _,
5828                dhcpv4_client_provider: _,
5829                dhcpv6_client_provider: mut dhcpv6_client_provider_request_stream,
5830                route_set_v4_provider: _,
5831                dhcpv4_server: _,
5832                fuchsia_networks: _,
5833            },
5834        ) = test_netcfg(NetcfgTestArgs {
5835            with_dhcpv4_client_provider: false,
5836            with_fuchsia_networks: false,
5837        })
5838        .expect("error creating test netcfg");
5839        let allowed_upstream_device_classes = HashSet::from([ALLOWED_UPSTREAM_DEVICE_CLASS]);
5840        netcfg.allowed_upstream_device_classes = &allowed_upstream_device_classes;
5841        let mut dns_watchers = DnsServerWatchers::empty();
5842
5843        let (_prefix_control, server_end) =
5844            fidl::endpoints::create_proxy::<fnet_dhcpv6::PrefixControlMarker>();
5845        netcfg
5846            .handle_dhcpv6_acquire_prefix(
5847                fnet_dhcpv6::AcquirePrefixConfig::default(),
5848                server_end,
5849                &mut dns_watchers,
5850            )
5851            .await
5852            .expect("handle DHCPv6 acquire prefix");
5853
5854        for (id, upstream) in
5855            [(InterfaceId::new(1).unwrap(), true), (InterfaceId::new(2).unwrap(), false)]
5856        {
5857            // Mock interface being discovered by NetCfg.
5858            let (control, control_server_end) =
5859                fidl_fuchsia_net_interfaces_ext::admin::Control::create_endpoints()
5860                    .expect("create endpoints");
5861            let device_class = if upstream {
5862                ALLOWED_UPSTREAM_DEVICE_CLASS
5863            } else {
5864                DISALLOWED_UPSTREAM_DEVICE_CLASS
5865            };
5866            let mut control_request_stream = control_server_end.into_stream();
5867
5868            let (new_host_result, ()) = futures::join!(
5869                InterfaceState::new_host(
5870                    test_interface_naming_id(),
5871                    control,
5872                    device_class,
5873                    upstream
5874                        .then_some(fnet_dhcpv6::PrefixDelegationConfig::Empty(fnet_dhcpv6::Empty)),
5875                    interface::ProvisioningType::Local,
5876                ),
5877                expect_get_interface_auth(&mut control_request_stream)
5878            );
5879
5880            assert_matches::assert_matches!(
5881                netcfg
5882                    .interface_states
5883                    .insert(id, new_host_result.expect("new_host should succeed")),
5884                None
5885            );
5886
5887            let sockaddr = dhcpv6_sockaddr(id);
5888
5889            // Fake an interface added event.
5890            netcfg
5891                .handle_interface_watcher_event(
5892                    fnet_interfaces::Event::Added(fnet_interfaces::Properties {
5893                        id: Some(id.get()),
5894                        name: Some(format!("testif{}", id)),
5895                        port_class: Some(fnet_interfaces::PortClass::Device(
5896                            device_class.into(),
5897                        )),
5898                        online: Some(true),
5899                        addresses: Some(ipv6addrs(Some(sockaddr))),
5900                        has_default_ipv4_route: Some(false),
5901                        has_default_ipv6_route: Some(false),
5902                        ..Default::default()
5903                    }).into(),
5904                    &mut dns_watchers,
5905                    &mut virtualization::Stub,
5906                )
5907                .await
5908                .unwrap_or_else(|e| panic!("error handling interface added event for {} with interface up and link-local addr: {:?}", id, e));
5909
5910            // Expect DHCPv6 client to have started with PD configuration.
5911            let _: fnet_dhcpv6::ClientRequestStream = check_new_dhcpv6_client(
5912                &mut dhcpv6_client_provider_request_stream,
5913                id,
5914                sockaddr,
5915                upstream.then_some(fnet_dhcpv6::PrefixDelegationConfig::Empty(fnet_dhcpv6::Empty)),
5916                &mut dns_watchers,
5917            )
5918            .await
5919            .unwrap_or_else(|e| {
5920                panic!("error checking for new DHCPv6 client on interface {}: {:?}", id, e)
5921            });
5922        }
5923    }
5924
5925    struct InterfacePropertiesHelper {
5926        id: InterfaceId,
5927        online: Option<bool>,
5928        has_default_ipv4_route: Option<bool>,
5929        has_default_ipv6_route: Option<bool>,
5930        addresses: Option<Vec<fnet_interfaces::Address>>,
5931    }
5932
5933    // Create a `fnet_interfaces::Properties` object using
5934    // a small subset of fields that influence whether an
5935    // interface is shared with the socketproxy.
5936    fn create_properties(
5937        InterfacePropertiesHelper {
5938            id,
5939            online,
5940            has_default_ipv4_route,
5941            has_default_ipv6_route,
5942            addresses,
5943        }: InterfacePropertiesHelper,
5944    ) -> fnet_interfaces::Properties {
5945        fnet_interfaces::Properties {
5946            id: Some(id.get()),
5947            name: Some(format!("testif{}", id)),
5948            port_class: Some(fnet_interfaces::PortClass::Device(
5949                ALLOWED_UPSTREAM_DEVICE_CLASS.into(),
5950            )),
5951            online,
5952            has_default_ipv4_route,
5953            has_default_ipv6_route,
5954            addresses,
5955            ..Default::default()
5956        }
5957    }
5958
5959    // Given a list of interface ids and provisioning actions,
5960    // get the nth interface with `Local` provisioning.
5961    //
5962    // Returns None if there is no nth matching interface.
5963    fn get_nth_interface_id_locally_provisioned(
5964        interfaces: &[(InterfaceId, interface::ProvisioningType)],
5965        nth: usize,
5966    ) -> Option<InterfaceId> {
5967        interfaces.into_iter().filter_map(|(id, action)| (*action == Local).then_some(*id)).nth(nth)
5968    }
5969
5970    const INTERFACE_ID2: InterfaceId = InterfaceId::new(2).unwrap();
5971    const INTERFACE_ID3: InterfaceId = InterfaceId::new(3).unwrap();
5972
5973    async fn fuchsia_networks_fallback_helper(
5974        interfaces: &[(InterfaceId, interface::ProvisioningType)],
5975        final_event: fnet_interfaces::Event,
5976    ) {
5977        // The provided interface id list must be non-empty.
5978        assert!(interfaces.len() > 0);
5979
5980        let (mut netcfg, ServerEnds { fuchsia_networks, .. }) = test_netcfg(NetcfgTestArgs {
5981            with_dhcpv4_client_provider: false,
5982            with_fuchsia_networks: true,
5983        })
5984        .expect("error creating test netcfg");
5985        let mut fuchsia_networks_stream = fuchsia_networks.into_stream();
5986        let mut dns_watchers = DnsServerWatchers::empty();
5987
5988        assert_eq!(netcfg.socket_proxy_state.as_ref().expect("should be set").default_id(), None);
5989
5990        // The initial default interface is the first one that is
5991        // locally provisioned. There must be at least one interface
5992        // that has ProvisioningAction::Local set.
5993        let initial_default_interface =
5994            get_nth_interface_id_locally_provisioned(interfaces, 0).unwrap();
5995
5996        // Mock interfaces being discovered by NetCfg.
5997        for (id, provisioning_action) in interfaces.iter() {
5998            let (control, control_server_end) =
5999                fidl_fuchsia_net_interfaces_ext::admin::Control::create_endpoints()
6000                    .expect("create endpoints");
6001            let mut control_request_stream = control_server_end.into_stream();
6002
6003            let (new_host_result, ()) = futures::join!(
6004                InterfaceState::new_host(
6005                    test_interface_naming_id(),
6006                    control,
6007                    ALLOWED_UPSTREAM_DEVICE_CLASS,
6008                    None,
6009                    *provisioning_action,
6010                ),
6011                expect_get_interface_auth(&mut control_request_stream)
6012            );
6013
6014            assert_matches::assert_matches!(
6015                netcfg
6016                    .interface_states
6017                    .insert(*id, new_host_result.expect("new_host should succeed")),
6018                None
6019            );
6020
6021            // Fake an interface added event. As `has_default_ipv4_route` and
6022            // `has_default_ipv6_route` are true, the network should be added to
6023            // the socketproxy state and made as default.
6024            let (watcher_result, ()) = futures::future::join(
6025                netcfg.handle_interface_watcher_event(
6026                    fnet_interfaces::Event::Added(create_properties(InterfacePropertiesHelper {
6027                        id: *id,
6028                        online: Some(true),
6029                        has_default_ipv4_route: Some(true),
6030                        has_default_ipv6_route: Some(true),
6031                        addresses: Some(ipv6addrs(None)),
6032                    }))
6033                    .into(),
6034                    &mut dns_watchers,
6035                    &mut virtualization::Stub,
6036                ),
6037                async {
6038                    // The call to `add`. Only locally provisioned networks
6039                    // are added in FuchsiaNetworks.
6040                    if *provisioning_action == Local {
6041                        respond_to_socketproxy(&mut fuchsia_networks_stream, Ok(())).await;
6042                    }
6043
6044                    // The call to `set_default`. Only the first viable network
6045                    // will be set to default.
6046                    if *id == initial_default_interface {
6047                        respond_to_socketproxy(&mut fuchsia_networks_stream, Ok(())).await;
6048                    }
6049                },
6050            )
6051            .await;
6052            assert_matches::assert_matches!(watcher_result, Ok(()));
6053        }
6054
6055        assert_eq!(
6056            netcfg.socket_proxy_state.as_ref().expect("should be set").default_id(),
6057            Some(initial_default_interface)
6058        );
6059
6060        // Send an interface changed event with `has_default_ipv4_route`
6061        // becoming false. This results in no calls against FuchsiaNetworks
6062        // due to one of the default routes still being present.
6063        assert_matches::assert_matches!(
6064            netcfg
6065                .handle_interface_watcher_event(
6066                    fnet_interfaces::Event::Changed(create_properties(InterfacePropertiesHelper {
6067                        id: initial_default_interface,
6068                        online: None,
6069                        has_default_ipv4_route: Some(false),
6070                        has_default_ipv6_route: None,
6071                        addresses: None,
6072                    }))
6073                    .into(),
6074                    &mut dns_watchers,
6075                    &mut virtualization::Stub,
6076                )
6077                .await,
6078            Ok(())
6079        );
6080
6081        // Send an interface changed event with an address update
6082        // that does not impact the network being a valid candidate.
6083        // This results in no calls against FuchsiaNetworks.
6084        assert_matches::assert_matches!(
6085            netcfg
6086                .handle_interface_watcher_event(
6087                    fnet_interfaces::Event::Changed(create_properties(InterfacePropertiesHelper {
6088                        id: initial_default_interface,
6089                        online: None,
6090                        has_default_ipv4_route: None,
6091                        has_default_ipv6_route: None,
6092                        addresses: Some(vec![fnet_interfaces::Address {
6093                            addr: Some(fidl_subnet!("192.0.2.0/24")),
6094                            assignment_state: Some(
6095                                fnet_interfaces::AddressAssignmentState::Assigned
6096                            ),
6097                            ..Default::default()
6098                        }]),
6099                    }))
6100                    .into(),
6101                    &mut dns_watchers,
6102                    &mut virtualization::Stub,
6103                )
6104                .await,
6105            Ok(())
6106        );
6107
6108        // Send an interface changed event that should cause the network
6109        // to be removed from the socketproxy.
6110        let (watcher_result, ()) = futures::future::join(
6111            netcfg.handle_interface_watcher_event(
6112                final_event.into(),
6113                &mut dns_watchers,
6114                &mut virtualization::Stub,
6115            ),
6116            async {
6117                // The call to `set_default`.
6118                respond_to_socketproxy(&mut fuchsia_networks_stream, Ok(())).await;
6119
6120                // The call to `remove`.
6121                respond_to_socketproxy(&mut fuchsia_networks_stream, Ok(())).await;
6122            },
6123        )
6124        .await;
6125        assert_matches::assert_matches!(watcher_result, Ok(()));
6126
6127        // The fallback default interface is the second one that is locally
6128        // provisioned (or None if one does not exist).
6129        assert_eq!(
6130            netcfg.socket_proxy_state.as_ref().expect("should be set").default_id(),
6131            get_nth_interface_id_locally_provisioned(interfaces, 1)
6132        );
6133    }
6134
6135    // Test the default network functionality of the FuchsiaNetworks
6136    // integration. Determine the fallback behavior when there is another
6137    // valid network and when no alternative network exists.
6138    #[test_case(
6139        &[INTERFACE_ID],
6140            fnet_interfaces::Event::Changed(create_properties(InterfacePropertiesHelper {
6141                id: INTERFACE_ID,
6142                online: None,
6143                has_default_ipv4_route: None,
6144                has_default_ipv6_route: Some(false),
6145                addresses: None,
6146        }))
6147    ; "one_iface_update_lost_candidacy_v6")]
6148    #[test_case(
6149        &[INTERFACE_ID],
6150            fnet_interfaces::Event::Changed(create_properties(InterfacePropertiesHelper {
6151                id: INTERFACE_ID,
6152                online: Some(false),
6153                has_default_ipv4_route: None,
6154                has_default_ipv6_route: None,
6155                addresses: None,
6156        }))
6157    ; "one_iface_update_lost_candidacy_offline")]
6158    #[test_case(
6159        &[INTERFACE_ID],
6160        fnet_interfaces::Event::Removed(INTERFACE_ID.get())
6161    ; "one_iface_remove_default")]
6162    #[test_case(
6163        &[INTERFACE_ID, INTERFACE_ID2],
6164            fnet_interfaces::Event::Changed(create_properties(InterfacePropertiesHelper {
6165                id: INTERFACE_ID,
6166                online: None,
6167                has_default_ipv4_route: None,
6168                has_default_ipv6_route: Some(false),
6169                addresses: None,
6170        }))
6171    ; "two_iface_update_lost_candidacy")]
6172    #[test_case(
6173        &[INTERFACE_ID, INTERFACE_ID2],
6174        fnet_interfaces::Event::Removed(INTERFACE_ID.get())
6175    ; "two_iface_remove_default")]
6176    #[test_case(
6177        &[INTERFACE_ID, INTERFACE_ID2, INTERFACE_ID3],
6178            fnet_interfaces::Event::Changed(create_properties(InterfacePropertiesHelper {
6179                id: INTERFACE_ID,
6180                online: None,
6181                has_default_ipv4_route: None,
6182                has_default_ipv6_route: Some(false),
6183                addresses: None,
6184        }))
6185    ; "three_iface_update_lost_candidacy")]
6186    #[test_case(
6187        &[INTERFACE_ID, INTERFACE_ID2, INTERFACE_ID3],
6188        fnet_interfaces::Event::Removed(INTERFACE_ID.get())
6189    ; "three_iface_remove_default")]
6190    #[fuchsia::test]
6191    async fn test_fuchsia_networks_fallback_all_local(
6192        interface_ids: &[InterfaceId],
6193        final_event: fnet_interfaces::Event,
6194    ) {
6195        let interfaces: Vec<(InterfaceId, interface::ProvisioningType)> =
6196            interface_ids.into_iter().map(|id| (*id, Local)).collect();
6197        fuchsia_networks_fallback_helper(&interfaces, final_event).await
6198    }
6199
6200    // Test the default network functionality of the FuchsiaNetworks
6201    // integration. Determine the fallback behavior when there are delegated
6202    // interfaces present.
6203    #[test_case(
6204        &[(INTERFACE_ID, Local), (INTERFACE_ID2, Delegated)],
6205        fnet_interfaces::Event::Removed(INTERFACE_ID.get())
6206    ; "one_local_iface_first_iface")]
6207    #[test_case(
6208        &[(INTERFACE_ID, Delegated), (INTERFACE_ID2, Delegated), (INTERFACE_ID3, Local)],
6209        fnet_interfaces::Event::Removed(INTERFACE_ID3.get())
6210    ; "one_local_iface_not_first_iface")]
6211    #[test_case(
6212        &[(INTERFACE_ID, Local), (INTERFACE_ID2, Delegated), (INTERFACE_ID3, Local)],
6213        fnet_interfaces::Event::Removed(INTERFACE_ID.get())
6214    ; "two_local_iface_first_iface")]
6215    #[test_case(
6216        &[(INTERFACE_ID, Delegated), (INTERFACE_ID2, Local), (INTERFACE_ID3, Local)],
6217        fnet_interfaces::Event::Removed(INTERFACE_ID2.get())
6218    ; "two_local_iface_not_first_iface")]
6219    #[fuchsia::test]
6220    async fn test_fuchsia_networks_fallback_mixed_provisioning(
6221        interfaces: &[(InterfaceId, interface::ProvisioningType)],
6222        final_event: fnet_interfaces::Event,
6223    ) {
6224        fuchsia_networks_fallback_helper(&interfaces, final_event).await
6225    }
6226
6227    #[test]
6228    fn test_config() {
6229        let config_str = r#"
6230{
6231  "dns_config": {
6232    "servers": ["8.8.8.8"]
6233  },
6234  "filter_config": {
6235    "rules": [],
6236    "nat_rules": [],
6237    "rdr_rules": []
6238  },
6239  "filter_enabled_interface_types": ["wlanclient", "wlanap"],
6240  "interface_metrics": {
6241    "wlan_metric": 100,
6242    "eth_metric": 10,
6243    "blackhole_metric": 123
6244  },
6245  "allowed_upstream_device_classes": ["ethernet", "wlanclient"],
6246  "allowed_bridge_upstream_device_classes": ["ethernet"],
6247  "enable_dhcpv6": true,
6248  "forwarded_device_classes": { "ipv4": [ "ethernet" ], "ipv6": [ "wlanclient" ] },
6249  "interface_naming_policy": [ { "matchers": [
6250        {"bus_types": ["usb", "pci", "sdio"]},
6251        {"device_classes": ["ethernet", "wlanclient", "wlanap"]},
6252        {"topological_path": "abcde"},
6253        {"any": true}
6254    ], "naming_scheme": [
6255        { "type": "dynamic", "rule": "device_class" },
6256        { "type": "static", "value": "x" },
6257        { "type": "dynamic", "rule": "normalized_mac" },
6258        { "type": "dynamic", "rule": "bus_type" },
6259        { "type": "dynamic", "rule": "bus_path" },
6260        { "type": "default" }
6261    ] } ],
6262    "interface_provisioning_policy": [ {
6263        "matchers": [ {"any": false } ],
6264        "provisioning": "delegated",
6265        "netstack_managed_routes_designation": "interface_local"
6266    }, {
6267        "matchers": [ {"interface_name": "xyz" } ],
6268        "provisioning": "local"
6269    } ],
6270   "blackhole_interfaces": [ "ifb0" ],
6271   "enable_socket_proxy": true
6272}
6273"#;
6274
6275        let Config {
6276            dns_config: DnsConfig { servers },
6277            filter_config,
6278            filter_enabled_interface_types,
6279            interface_metrics,
6280            allowed_upstream_device_classes,
6281            allowed_bridge_upstream_device_classes,
6282            enable_dhcpv6,
6283            forwarded_device_classes,
6284            interface_naming_policy,
6285            interface_provisioning_policy,
6286            blackhole_interfaces,
6287            enable_socket_proxy,
6288        } = Config::load_str(config_str).unwrap();
6289
6290        assert_eq!(vec!["8.8.8.8".parse::<std::net::IpAddr>().unwrap()], servers);
6291        let FilterConfig { rules, nat_rules, rdr_rules } = filter_config;
6292        assert_eq!(Vec::<String>::new(), rules);
6293        assert_eq!(Vec::<String>::new(), nat_rules);
6294        assert_eq!(Vec::<String>::new(), rdr_rules);
6295
6296        assert_eq!(
6297            HashSet::from([InterfaceType::WlanClient, InterfaceType::WlanAp]),
6298            filter_enabled_interface_types
6299        );
6300
6301        let expected_metrics = InterfaceMetrics {
6302            wlan_metric: Metric(100),
6303            eth_metric: Metric(10),
6304            blackhole_metric: Metric(123),
6305        };
6306        assert_eq!(interface_metrics, expected_metrics);
6307
6308        assert_eq!(
6309            AllowedDeviceClasses(HashSet::from([DeviceClass::Ethernet, DeviceClass::WlanClient])),
6310            allowed_upstream_device_classes
6311        );
6312        assert_eq!(
6313            AllowedDeviceClasses(HashSet::from([DeviceClass::Ethernet])),
6314            allowed_bridge_upstream_device_classes
6315        );
6316
6317        assert_eq!(enable_dhcpv6, true);
6318
6319        let expected_classes = ForwardedDeviceClasses {
6320            ipv4: HashSet::from([DeviceClass::Ethernet]),
6321            ipv6: HashSet::from([DeviceClass::WlanClient]),
6322        };
6323        assert_eq!(forwarded_device_classes, expected_classes);
6324
6325        let expected_naming_policy = Vec::from([interface::NamingRule {
6326            matchers: HashSet::from([
6327                interface::MatchingRule::BusTypes(vec![
6328                    interface::BusType::USB,
6329                    interface::BusType::PCI,
6330                    interface::BusType::SDIO,
6331                ]),
6332                interface::MatchingRule::DeviceClasses(vec![
6333                    DeviceClass::Ethernet,
6334                    DeviceClass::WlanClient,
6335                    DeviceClass::WlanAp,
6336                ]),
6337                interface::MatchingRule::TopologicalPath(glob::Pattern::new("abcde").unwrap()),
6338                interface::MatchingRule::Any(true),
6339            ]),
6340            naming_scheme: vec![
6341                interface::NameCompositionRule::Dynamic {
6342                    rule: interface::DynamicNameCompositionRule::DeviceClass,
6343                },
6344                interface::NameCompositionRule::Static { value: "x".to_owned() },
6345                interface::NameCompositionRule::Dynamic {
6346                    rule: interface::DynamicNameCompositionRule::NormalizedMac,
6347                },
6348                interface::NameCompositionRule::Dynamic {
6349                    rule: interface::DynamicNameCompositionRule::BusType,
6350                },
6351                interface::NameCompositionRule::Dynamic {
6352                    rule: interface::DynamicNameCompositionRule::BusPath,
6353                },
6354                interface::NameCompositionRule::Default,
6355            ],
6356        }]);
6357        assert_eq!(interface_naming_policy, expected_naming_policy);
6358
6359        let expected_provisioning_policy = Vec::from([
6360            interface::ProvisioningRule {
6361                matchers: HashSet::from([interface::ProvisioningMatchingRule::Common(
6362                    interface::MatchingRule::Any(false),
6363                )]),
6364                action: interface::ProvisioningAction {
6365                    provisioning: interface::ProvisioningType::Delegated,
6366                    netstack_managed_routes_designation: Some(
6367                        interface::NetstackManagedRoutesDesignation::InterfaceLocal,
6368                    ),
6369                },
6370            },
6371            interface::ProvisioningRule {
6372                matchers: HashSet::from([interface::ProvisioningMatchingRule::InterfaceName {
6373                    pattern: glob::Pattern::new("xyz").unwrap(),
6374                }]),
6375                action: interface::ProvisioningAction {
6376                    provisioning: interface::ProvisioningType::Local,
6377                    netstack_managed_routes_designation: None,
6378                },
6379            },
6380        ]);
6381        assert_eq!(interface_provisioning_policy, expected_provisioning_policy);
6382
6383        assert_eq!(blackhole_interfaces, vec!["ifb0".to_string()]);
6384
6385        assert_eq!(enable_socket_proxy, true)
6386    }
6387
6388    #[test]
6389    fn test_config_defaults() {
6390        let config_str = r#"
6391{
6392  "dns_config": { "servers": [] },
6393  "filter_config": {
6394    "rules": [],
6395    "nat_rules": [],
6396    "rdr_rules": []
6397  },
6398  "filter_enabled_interface_types": []
6399}
6400"#;
6401
6402        let Config {
6403            dns_config: _,
6404            filter_config: _,
6405            filter_enabled_interface_types: _,
6406            allowed_upstream_device_classes,
6407            allowed_bridge_upstream_device_classes,
6408            interface_metrics,
6409            enable_dhcpv6,
6410            forwarded_device_classes: _,
6411            interface_naming_policy,
6412            interface_provisioning_policy,
6413            blackhole_interfaces,
6414            enable_socket_proxy,
6415        } = Config::load_str(config_str).unwrap();
6416
6417        assert_eq!(allowed_upstream_device_classes, Default::default());
6418        assert_eq!(allowed_bridge_upstream_device_classes, Default::default());
6419        assert_eq!(interface_metrics, Default::default());
6420        assert_eq!(enable_dhcpv6, true);
6421        assert_eq!(interface_naming_policy.len(), 0);
6422        assert_eq!(interface_provisioning_policy.len(), 0);
6423        assert_eq!(blackhole_interfaces, Vec::<String>::new());
6424        assert_eq!(enable_socket_proxy, false);
6425    }
6426
6427    #[test_case(
6428        "eth_metric", Default::default(), Metric(1), Metric(1);
6429        "wlan assumes default metric when unspecified")]
6430    #[test_case("wlan_metric", Metric(1), Default::default(), Metric(1);
6431        "eth assumes default metric when unspecified")]
6432    fn test_config_metric_individual_defaults(
6433        metric_name: &'static str,
6434        wlan_metric: Metric,
6435        eth_metric: Metric,
6436        expect_metric: Metric,
6437    ) {
6438        let config_str = format!(
6439            r#"
6440{{
6441  "dns_config": {{ "servers": [] }},
6442  "filter_config": {{
6443    "rules": [],
6444    "nat_rules": [],
6445    "rdr_rules": []
6446  }},
6447  "filter_enabled_interface_types": [],
6448  "interface_metrics": {{ "{}": {} }}
6449}}
6450"#,
6451            metric_name, expect_metric
6452        );
6453
6454        let Config {
6455            dns_config: _,
6456            filter_config: _,
6457            filter_enabled_interface_types: _,
6458            allowed_upstream_device_classes: _,
6459            allowed_bridge_upstream_device_classes: _,
6460            enable_dhcpv6: _,
6461            interface_metrics,
6462            forwarded_device_classes: _,
6463            interface_naming_policy: _,
6464            interface_provisioning_policy: _,
6465            blackhole_interfaces: _,
6466            enable_socket_proxy: _,
6467        } = Config::load_str(&config_str).unwrap();
6468
6469        let expected_metrics =
6470            InterfaceMetrics { wlan_metric, eth_metric, blackhole_metric: Default::default() };
6471        assert_eq!(interface_metrics, expected_metrics);
6472    }
6473
6474    #[test]
6475    fn test_config_denies_unknown_fields() {
6476        let config_str = r#"{
6477            "filter_enabled_interface_types": ["wlanclient"],
6478            "foobar": "baz"
6479        }"#;
6480
6481        let err = Config::load_str(config_str).expect_err("config shouldn't accept unknown fields");
6482        let err = err.downcast::<serde_json5::Error>().expect("downcast error");
6483        assert_matches!(
6484            err,
6485            serde_json5::Error::Message {
6486                location: Some(serde_json5::Location { line: 3, .. }),
6487                ..
6488            }
6489        );
6490        // Ensure the error is complaining about unknown field.
6491        assert!(format!("{:?}", err).contains("foobar"));
6492    }
6493
6494    #[test]
6495    fn test_config_denies_unknown_fields_nested() {
6496        let bad_configs = vec![
6497            r#"
6498{
6499  "dns_config": { "speling": [] },
6500  "filter_config": {
6501    "rules": [],
6502    "nat_rules": [],
6503    "rdr_rules": []
6504  },
6505  "filter_enabled_interface_types": []
6506}
6507"#,
6508            r#"
6509{
6510  "dns_config": { "servers": [] },
6511  "filter_config": {
6512    "speling": [],
6513    "nat_rules": [],
6514    "rdr_rules": []
6515  },
6516  "filter_enabled_interface_types": []
6517}
6518"#,
6519            r#"
6520{
6521  "dns_config": { "servers": [] },
6522  "filter_config": {
6523    "rules": [],
6524    "nat_rules": [],
6525    "rdr_rules": []
6526  },
6527  "filter_enabled_interface_types": ["speling"]
6528}
6529"#,
6530            r#"
6531{
6532  "dns_config": { "servers": [] },
6533  "filter_config": {
6534    "rules": [],
6535    "nat_rules": [],
6536    "rdr_rules": []
6537  },
6538  "interface_metrics": {
6539    "eth_metric": 1,
6540    "wlan_metric": 2,
6541    "speling": 3,
6542  },
6543  "filter_enabled_interface_types": []
6544}
6545"#,
6546            r#"
6547{
6548  "dns_config": { "servers": [] },
6549  "filter_config": {
6550    "rules": [],
6551    "nat_rules": [],
6552    "rdr_rules": []
6553  },
6554  "filter_enabled_interface_types": ["speling"]
6555}
6556"#,
6557            r#"
6558{
6559  "dns_config": { "servers": [] },
6560  "filter_config": {
6561    "rules": [],
6562    "nat_rules": [],
6563    "rdr_rules": []
6564  },
6565  "filter_enabled_interface_types": [],
6566  "allowed_upstream_device_classes": ["speling"]
6567}
6568"#,
6569            r#"
6570{
6571  "dns_config": { "servers": [] },
6572  "filter_config": {
6573    "rules": [],
6574    "nat_rules": [],
6575    "rdr_rules": []
6576  },
6577  "filter_enabled_interface_types": [],
6578  "allowed_bridge_upstream_device_classes": ["speling"]
6579}
6580"#,
6581            r#"
6582{
6583  "dns_config": { "servers": [] },
6584  "filter_config": {
6585    "rules": [],
6586    "nat_rules": [],
6587    "rdr_rules": []
6588  },
6589  "filter_enabled_interface_types": [],
6590  "allowed_upstream_device_classes": [],
6591  "forwarded_device_classes": { "ipv4": [], "ipv6": [], "speling": [] }
6592}
6593"#,
6594            r#"
6595{
6596  "dns_config": { "servers": [] },
6597  "filter_config": {
6598    "rules": [],
6599    "nat_rules": [],
6600    "rdr_rules": []
6601  },
6602  "filter_enabled_interface_types": [],
6603  "allowed_upstream_device_classes": [],
6604  "forwarded_device_classes": { "ipv4": [], "ipv6": [] },
6605  "interface_naming_policy": [{
6606    "matchers": [],
6607    "naming_scheme": [],
6608    "speling": []
6609  }]
6610}
6611"#,
6612            r#"
6613{
6614  "dns_config": { "servers": [] },
6615  "filter_config": {
6616    "rules": [],
6617    "nat_rules": [],
6618    "rdr_rules": []
6619  },
6620  "filter_enabled_interface_types": [],
6621  "allowed_upstream_device_classes": [],
6622  "forwarded_device_classes": { "ipv4": [], "ipv6": [] },
6623  "interface_naming_policy": [{
6624    "matchers": [ { "speling": [] } ],
6625    "naming_scheme": []
6626  }]
6627}
6628"#,
6629            r#"
6630{
6631  "dns_config": { "servers": [] },
6632  "filter_config": {
6633    "rules": [],
6634    "nat_rules": [],
6635    "rdr_rules": []
6636  },
6637  "filter_enabled_interface_types": [],
6638  "allowed_upstream_device_classes": [],
6639  "forwarded_device_classes": { "ipv4": [], "ipv6": [] },
6640  "interface_naming_policy": [{
6641    "matchers": [ { "bus_types": ["speling"] } ],
6642    "naming_scheme": []
6643  }]
6644}
6645"#,
6646            r#"
6647{
6648  "dns_config": { "servers": [] },
6649  "filter_config": {
6650    "rules": [],
6651    "nat_rules": [],
6652    "rdr_rules": []
6653  },
6654  "filter_enabled_interface_types": [],
6655  "allowed_upstream_device_classes": [],
6656  "forwarded_device_classes": { "ipv4": [], "ipv6": [] },
6657  "interface_naming_policy": [{
6658    "matchers": [ { "device_classes": ["speling"] } ],
6659    "naming_scheme": []
6660  }]
6661}
6662"#,
6663            r#"
6664{
6665  "dns_config": { "servers": [] },
6666  "filter_config": {
6667    "rules": [],
6668    "nat_rules": [],
6669    "rdr_rules": []
6670  },
6671  "filter_enabled_interface_types": [],
6672  "allowed_upstream_device_classes": [],
6673  "forwarded_device_classes": { "ipv4": [], "ipv6": [] },
6674  "interface_naming_policy": [{
6675    "matchers": [ { "any": "speling" } ],
6676    "naming_scheme": []
6677  }]
6678}
6679"#,
6680            r#"
6681{
6682  "dns_config": { "servers": [] },
6683  "filter_config": {
6684    "rules": [],
6685    "nat_rules": [],
6686    "rdr_rules": []
6687  },
6688  "filter_enabled_interface_types": [],
6689  "allowed_upstream_device_classes": [],
6690  "forwarded_device_classes": { "ipv4": [], "ipv6": [] },
6691  "interface_naming_policy": [{
6692    "matchers": [ { "any": true } ],
6693    "naming_scheme": [ { "type": "speling" } ]
6694  }]
6695}
6696"#,
6697            r#"
6698{
6699  "dns_config": { "servers": [] },
6700  "filter_config": {
6701    "rules": [],
6702    "nat_rules": [],
6703    "rdr_rules": []
6704  },
6705  "filter_enabled_interface_types": [],
6706  "allowed_upstream_device_classes": [],
6707  "forwarded_device_classes": { "ipv4": [], "ipv6": [] },
6708  "interface_naming_policy": [{
6709    "matchers": [ { "any": true } ],
6710    "naming_scheme": [ { "type": "dynamic", "rule": "speling" } ]
6711  }]
6712}
6713"#,
6714            r#"
6715{
6716  "dns_config": { "servers": [] },
6717  "filter_config": {
6718    "rules": [],
6719    "nat_rules": [],
6720    "rdr_rules": []
6721  },
6722  "filter_enabled_interface_types": [],
6723  "allowed_upstream_device_classes": [],
6724  "forwarded_device_classes": { "ipv4": [], "ipv6": [] },
6725  "interface_provisioning_policy": [{
6726    "matchers": [ { "any": true } ],
6727    "speling": ""
6728  }]
6729}
6730"#,
6731            r#"
6732{
6733  "dns_config": { "servers": [] },
6734  "filter_config": {
6735    "rules": [],
6736    "nat_rules": [],
6737    "rdr_rules": []
6738  },
6739  "filter_enabled_interface_types": [],
6740  "allowed_upstream_device_classes": [],
6741  "forwarded_device_classes": { "ipv4": [], "ipv6": [] },
6742  "interface_provisioning_policy": [{
6743    "matchers": [ { "any": true } ],
6744    "provisioning": "speling"
6745  }]
6746}
6747"#,
6748            r#"
6749{
6750  "dns_config": { "servers": [] },
6751  "filter_config": {
6752    "rules": [],
6753    "nat_rules": [],
6754    "rdr_rules": []
6755  },
6756  "filter_enabled_interface_types": [],
6757  "allowed_upstream_device_classes": [],
6758  "forwarded_device_classes": { "ipv4": [], "ipv6": [] },
6759  "interface_provisioning_policy": [{
6760    "matchers": [ { "any": true } ],
6761    "provisioning": "delegated",
6762    "netstack_managed_routes_designation": "speling"
6763  }]
6764}
6765"#,
6766        ];
6767
6768        for config_str in bad_configs {
6769            let err =
6770                Config::load_str(config_str).expect_err("config shouldn't accept unknown fields");
6771            let err = err.downcast::<serde_json5::Error>().expect("downcast error");
6772            let err_str = format!("{:?}", err);
6773            // Ensure the error is complaining about unknown field, or complaining
6774            // about the missing field.
6775            assert!(err_str.contains("speling") || err_str.contains("missing field"));
6776        }
6777    }
6778
6779    #[test_case(
6780        r#"
6781{
6782  "dns_config": { "servers": [] },
6783  "filter_config": {
6784    "rules": [],
6785    "nat_rules": [],
6786    "rdr_rules": []
6787  },
6788  "filter_enabled_interface_types": [],
6789  "allowed_upstream_device_classes": [],
6790  "forwarded_device_classes": { "ipv4": [], "ipv6": [] },
6791  "interface_naming_policy": [{
6792    "matchers": [ { "topological_path": "[speling" } ],
6793    "naming_scheme": []
6794  }]
6795}
6796"#,
6797        "invalid range";
6798        "topological_path"
6799    )]
6800    #[test_case(
6801        r#"
6802{
6803  "dns_config": { "servers": [] },
6804  "filter_config": {
6805    "rules": [],
6806    "nat_rules": [],
6807    "rdr_rules": []
6808  },
6809  "filter_enabled_interface_types": [],
6810  "allowed_upstream_device_classes": [],
6811  "forwarded_device_classes": { "ipv4": [], "ipv6": [] },
6812  "interface_provisioning_policy": [{
6813    "matchers": [ { "interface_name": "[speling" } ],
6814    "provisioning": "delegated"
6815  }]
6816}
6817"#,
6818        "did not match any variant";
6819        "interface_name"
6820    )]
6821    fn test_config_denies_invalid_glob(bad_config: &'static str, err_text: &'static str) {
6822        // Should fail on improper glob: square braces not closed.
6823        let err =
6824            Config::load_str(bad_config).expect_err("config shouldn't accept invalid pattern");
6825        let err = err.downcast::<serde_json5::Error>().expect("downcast error");
6826        // Ensure the error is complaining about invalid glob.
6827        assert!(format!("{:?}", err).contains(err_text));
6828    }
6829
6830    #[test]
6831    fn test_config_legacy_wlan_name() {
6832        let config_str = r#"
6833{
6834  "dns_config": { "servers": [] },
6835  "filter_config": {
6836    "rules": [],
6837    "nat_rules": [],
6838    "rdr_rules": []
6839  },
6840  "filter_enabled_interface_types": ["wlan", "ap"],
6841  "allowed_upstream_device_classes": ["wlan"]
6842}
6843"#;
6844        let Config { filter_enabled_interface_types, allowed_upstream_device_classes, .. } =
6845            Config::load_str(config_str).unwrap();
6846        assert_eq!(
6847            HashSet::from([InterfaceType::WlanClient, InterfaceType::WlanAp]),
6848            filter_enabled_interface_types
6849        );
6850        assert_eq!(
6851            AllowedDeviceClasses(HashSet::from([DeviceClass::WlanClient])),
6852            allowed_upstream_device_classes
6853        );
6854    }
6855
6856    #[test]
6857    fn test_config_supports_json5() {
6858        let config_str = r#"{
6859            "dns_config": { "servers": [] },
6860            // A comment on the config
6861            "filter_config": {
6862                'rules': [], // Single quoted string
6863                "nat_rules": [],
6864                "rdr_rules": [], // Trailing comma
6865            },
6866            "filter_enabled_interface_types": [],
6867            "allowed_upstream_device_classes": []
6868        }"#;
6869        let _ = Config::load_str(config_str).unwrap();
6870    }
6871}