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