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