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