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