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