Skip to main content

netcfg/
lib.rs

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