netcfg/
lib.rs

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