blob: c0a7adcd54ae573180ad4b8944fe48da2217d0f7 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use core::ops::{Deref as _, DerefMut as _};
use super::{
devices::{CommonInfo, DeviceSpecificInfo, Devices, EthernetInfo},
ethernet_worker,
util::{IntoFidl, TryFromFidlWithContext as _, TryIntoCore as _, TryIntoFidlWithContext as _},
DeviceStatusNotifier, InterfaceControl as _, InterfaceEventProducerFactory, Lockable,
LockableContext, MutableDeviceState as _,
};
use fidl_fuchsia_hardware_ethernet as fhardware_ethernet;
use fidl_fuchsia_hardware_network as fhardware_network;
use fidl_fuchsia_net as fidl_net;
use fidl_fuchsia_net_interfaces as finterfaces;
use fidl_fuchsia_net_stack::{
self as fidl_net_stack, ForwardingEntry, StackRequest, StackRequestStream,
};
use futures::{TryFutureExt as _, TryStreamExt as _};
use log::{debug, error};
use net_types::{ethernet::Mac, SpecifiedAddr, UnicastAddr};
use netstack3_core::{
add_ip_addr_subnet, add_route, del_ip_addr, del_route, get_all_routes, AddableEntryEither, Ctx,
};
pub(crate) struct StackFidlWorker<C> {
ctx: C,
}
struct LockedFidlWorker<'a, C: LockableContext> {
ctx: <C as Lockable<'a, Ctx<C::NonSyncCtx>>>::Guard,
worker: &'a StackFidlWorker<C>,
}
impl<C: LockableContext> StackFidlWorker<C> {
async fn lock_worker(&self) -> LockedFidlWorker<'_, C> {
let ctx = self.ctx.lock().await;
LockedFidlWorker { ctx, worker: self }
}
}
impl<C> StackFidlWorker<C>
where
C: ethernet_worker::EthernetWorkerContext + InterfaceEventProducerFactory,
C: Clone,
{
pub(crate) async fn serve(ctx: C, stream: StackRequestStream) -> Result<(), fidl::Error> {
stream
.try_fold(Self { ctx }, |worker, req| async {
match req {
StackRequest::AddEthernetInterface { topological_path, device, responder } => {
responder_send!(
responder,
&mut worker
.lock_worker()
.await
.fidl_add_ethernet_interface(topological_path, device)
.await
);
}
StackRequest::DelEthernetInterface { id, responder } => {
responder_send!(
responder,
&mut worker.lock_worker().await.fidl_del_ethernet_interface(id)
);
}
StackRequest::EnableInterfaceDeprecated { id, responder } => {
responder_send!(
responder,
&mut worker.lock_worker().await.fidl_enable_interface(id)
);
}
StackRequest::DisableInterfaceDeprecated { id, responder } => {
responder_send!(
responder,
&mut worker.lock_worker().await.fidl_disable_interface(id)
);
}
StackRequest::AddInterfaceAddressDeprecated { id, addr, responder } => {
responder_send!(
responder,
&mut worker.lock_worker().await.fidl_add_interface_address(id, addr)
);
}
StackRequest::DelInterfaceAddressDeprecated { id, addr, responder } => {
responder_send!(
responder,
&mut worker.lock_worker().await.fidl_del_interface_address(id, addr)
);
}
StackRequest::GetForwardingTable { responder } => {
responder_send!(
responder,
&mut worker.lock_worker().await.fidl_get_forwarding_table().iter_mut()
);
}
StackRequest::AddForwardingEntry { entry, responder } => {
responder_send!(
responder,
&mut worker.lock_worker().await.fidl_add_forwarding_entry(entry)
);
}
StackRequest::DelForwardingEntry {
entry:
fidl_net_stack::ForwardingEntry {
subnet,
device_id: _,
next_hop: _,
metric: _,
},
responder,
} => {
responder_send!(
responder,
&mut worker.lock_worker().await.fidl_del_forwarding_entry(subnet)
);
}
StackRequest::SetInterfaceIpForwardingDeprecated {
id: _,
ip_version: _,
enabled: _,
responder,
} => {
// TODO(https://fxbug.dev/76987): Support configuring per-NIC forwarding.
responder_send!(responder, &mut Err(fidl_net_stack::Error::NotSupported));
}
StackRequest::GetDnsServerWatcher { watcher, control_handle: _ } => {
let () = watcher
.close_with_epitaph(fuchsia_zircon::Status::NOT_SUPPORTED)
.unwrap_or_else(|e| {
debug!("failed to close DNS server watcher {:?}", e)
});
}
}
Ok(worker)
})
.map_ok(|Self { ctx: _ }| ())
.await
}
}
impl<'a, C> LockedFidlWorker<'a, C>
where
C: ethernet_worker::EthernetWorkerContext + InterfaceEventProducerFactory,
C: Clone,
{
async fn fidl_add_ethernet_interface(
self,
_topological_path: String,
device: fidl::endpoints::ClientEnd<fidl_fuchsia_hardware_ethernet::DeviceMarker>,
) -> Result<u64, fidl_net_stack::Error> {
let Self { mut ctx, worker } = self;
let (
client,
fidl_fuchsia_hardware_ethernet_ext::EthernetInfo {
mtu,
features,
mac: fidl_fuchsia_hardware_ethernet_ext::MacAddress { octets: mac_octets },
},
) = ethernet_worker::setup_ethernet(
device.into_proxy().map_err(|_| fidl_net_stack::Error::InvalidArgs)?,
)
.await
.map_err(|_| fidl_net_stack::Error::Internal)?;
let client_stream = client.get_stream();
let online = client
.get_status()
.await
.map(|s| s.contains(ethernet_worker::DeviceStatus::ONLINE))
.unwrap_or(false);
let mac_addr =
UnicastAddr::new(Mac::new(mac_octets)).ok_or(fidl_net_stack::Error::NotSupported)?;
let id = {
let Ctx { sync_ctx, non_sync_ctx } = &mut *ctx;
let eth_id = netstack3_core::add_ethernet_device(sync_ctx, non_sync_ctx, mac_addr, mtu);
let devices: &mut Devices = non_sync_ctx.as_mut();
devices
.add_device(eth_id, |id| {
let device_class = if features.contains(fhardware_ethernet::Features::LOOPBACK)
{
finterfaces::DeviceClass::Loopback(finterfaces::Empty)
} else if features.contains(fhardware_ethernet::Features::SYNTHETIC) {
finterfaces::DeviceClass::Device(fhardware_network::DeviceClass::Virtual)
} else if features.contains(fhardware_ethernet::Features::WLAN_AP) {
finterfaces::DeviceClass::Device(fhardware_network::DeviceClass::WlanAp)
} else if features.contains(fhardware_ethernet::Features::WLAN) {
finterfaces::DeviceClass::Device(fhardware_network::DeviceClass::Wlan)
} else {
finterfaces::DeviceClass::Device(fhardware_network::DeviceClass::Ethernet)
};
let name = format!("eth{}", id);
// We do not support updating the device's mac-address, mtu, and
// features during it's lifetime, their cached states are hence
// not updated once initialized.
DeviceSpecificInfo::Ethernet(EthernetInfo {
common_info: CommonInfo {
mtu,
admin_enabled: true,
events: worker.ctx.create_interface_event_producer(
id,
super::InterfaceProperties { name: name.clone(), device_class },
),
name,
},
client,
mac: mac_addr,
features,
phy_up: online,
})
})
.unwrap_or_else(|| {
panic!("failed to store device with {:?} on devices map", eth_id)
})
};
if online {
ctx.enable_interface(id)?;
}
ethernet_worker::EthernetWorker::new(id, self.worker.ctx.clone()).spawn(client_stream);
Ok(id)
}
}
impl<'a, C> LockedFidlWorker<'a, C>
where
C: LockableContext,
C::NonSyncCtx: AsMut<Devices>,
{
fn fidl_del_ethernet_interface(mut self, id: u64) -> Result<(), fidl_net_stack::Error> {
match self.ctx.non_sync_ctx.as_mut().remove_device(id) {
Some(_info) => {
// TODO(rheacock): ensure that the core client deletes all data
Ok(())
}
None => {
// Invalid device ID
Err(fidl_net_stack::Error::NotFound)
}
}
}
}
impl<'a, C> LockedFidlWorker<'a, C>
where
C: LockableContext,
C::NonSyncCtx: AsRef<Devices>,
{
fn fidl_add_interface_address(
mut self,
id: u64,
addr: fidl_net::Subnet,
) -> Result<(), fidl_net_stack::Error> {
let Ctx { sync_ctx, non_sync_ctx } = self.ctx.deref_mut();
let device_info =
non_sync_ctx.as_ref().get_device(id).ok_or(fidl_net_stack::Error::NotFound)?;
let device_id = device_info.core_id();
add_ip_addr_subnet(
sync_ctx,
non_sync_ctx,
device_id,
addr.try_into_core().map_err(IntoFidl::into_fidl)?,
)
.map_err(IntoFidl::into_fidl)
}
fn fidl_del_interface_address(
mut self,
id: u64,
addr: fidl_net::Subnet,
) -> Result<(), fidl_net_stack::Error> {
let Ctx { sync_ctx, non_sync_ctx } = self.ctx.deref_mut();
let device_info =
non_sync_ctx.as_ref().get_device(id).ok_or(fidl_net_stack::Error::NotFound)?;
let device_id = device_info.core_id();
let addr: SpecifiedAddr<_> = addr.addr.try_into_core().map_err(IntoFidl::into_fidl)?;
del_ip_addr(sync_ctx, non_sync_ctx, device_id, addr.into()).map_err(IntoFidl::into_fidl)
}
fn fidl_get_forwarding_table(self) -> Vec<fidl_net_stack::ForwardingEntry> {
let Ctx { sync_ctx, non_sync_ctx } = self.ctx.deref();
get_all_routes(sync_ctx)
.filter_map(|entry| match entry.try_into_fidl_with_ctx(&non_sync_ctx) {
Ok(entry) => Some(entry),
Err(e) => {
error!("Failed to map forwarding entry into FIDL: {:?}", e);
None
}
})
.collect()
}
fn fidl_add_forwarding_entry(
mut self,
entry: ForwardingEntry,
) -> Result<(), fidl_net_stack::Error> {
let Ctx { sync_ctx, non_sync_ctx } = self.ctx.deref_mut();
let entry = match AddableEntryEither::try_from_fidl_with_ctx(&non_sync_ctx, entry) {
Ok(entry) => entry,
Err(e) => return Err(e.into()),
};
add_route(sync_ctx, non_sync_ctx, entry).map_err(IntoFidl::into_fidl)
}
}
impl<'a, C> LockedFidlWorker<'a, C>
where
C: LockableContext,
{
fn fidl_del_forwarding_entry(
mut self,
subnet: fidl_net::Subnet,
) -> Result<(), fidl_net_stack::Error> {
let Ctx { sync_ctx, non_sync_ctx } = self.ctx.deref_mut();
if let Ok(subnet) = subnet.try_into_core() {
del_route(sync_ctx, non_sync_ctx, subnet).map_err(IntoFidl::into_fidl)
} else {
Err(fidl_net_stack::Error::InvalidArgs)
}
}
}
impl<'a, C> LockedFidlWorker<'a, C>
where
C: LockableContext,
C::NonSyncCtx: DeviceStatusNotifier,
C::NonSyncCtx: AsRef<Devices> + AsMut<Devices>,
{
fn fidl_enable_interface(mut self, id: u64) -> Result<(), fidl_net_stack::Error> {
self.ctx.update_device_state(id, |dev_info| {
dev_info.info_mut().common_info_mut().admin_enabled = true;
});
self.ctx.enable_interface(id)
}
fn fidl_disable_interface(mut self, id: u64) -> Result<(), fidl_net_stack::Error> {
self.ctx.update_device_state(id, |dev_info| {
dev_info.info_mut().common_info_mut().admin_enabled = false;
});
self.ctx.disable_interface(id)
}
}