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