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