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