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