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