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