Skip to main content

netcfg/
lib.rs

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