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