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