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