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