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