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