blob: 731bc378eb74ce472cced3858c6549799b3f582b [file] [log] [blame]
// Copyright 2023 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.
//! A Netstack3 worker to serve fuchsia.net.root.Interfaces API requests.
use async_utils::channel::TrySend as _;
use fidl::endpoints::{ControlHandle as _, ProtocolMarker as _, ServerEnd};
use fidl_fuchsia_net_interfaces_admin as fnet_interfaces_admin;
use fidl_fuchsia_net_root as fnet_root;
use fuchsia_zircon as zx;
use futures::TryStreamExt as _;
use net_types::ip::{Ipv4, Ipv6};
use tracing::{debug, error};
use crate::bindings::{
devices::{BindingId, DeviceSpecificInfo, LOOPBACK_MAC},
interfaces_admin,
routes::admin::{serve_route_set, GlobalRouteSet},
util::{IntoFidl as _, TaskWaitGroupSpawner},
DeviceIdExt as _, Netstack,
};
// Serve a stream of fuchsia.net.root.Interfaces API requests for a single
// channel (e.g. a single client connection).
pub(crate) async fn serve_interfaces(
ns: Netstack,
rs: fnet_root::InterfacesRequestStream,
) -> Result<(), fidl::Error> {
debug!(protocol = fnet_root::InterfacesMarker::DEBUG_NAME, "serving");
rs.try_for_each(|req| async {
match req {
fnet_root::InterfacesRequest::GetAdmin { id, control, control_handle: _ } => {
handle_get_admin(&ns, id, control).await;
}
fnet_root::InterfacesRequest::GetMac { id, responder } => {
responder
.send(handle_get_mac(&ns, id).as_ref().map(Option::as_deref).map_err(|e| *e))
.unwrap_or_else(|e| error!("failed to respond: {e:?}"));
}
}
Ok(())
})
.await
}
async fn handle_get_admin(
ns: &Netstack,
interface_id: u64,
control: ServerEnd<fnet_interfaces_admin::ControlMarker>,
) {
debug!(interface_id, "handling fuchsia.net.root.Interfaces::GetAdmin");
let core_id =
BindingId::new(interface_id).and_then(|id| ns.ctx.bindings_ctx().devices.get_core_id(id));
let core_id = match core_id {
Some(c) => c,
None => {
control
.close_with_epitaph(zx::Status::NOT_FOUND)
.unwrap_or_else(|e| error!(err = ?e, "failed to send epitaph"));
return;
}
};
let mut sender = core_id.external_state().with_common_info(|i| i.control_hook.clone());
match sender.try_send_fut(interfaces_admin::OwnedControlHandle::new_unowned(control)).await {
Ok(()) => {}
Err(owned_control_handle) => {
owned_control_handle.into_control_handle().shutdown_with_epitaph(zx::Status::NOT_FOUND)
}
}
}
fn handle_get_mac(ns: &Netstack, interface_id: u64) -> fnet_root::InterfacesGetMacResult {
debug!(interface_id, "handling fuchsia.net.root.Interfaces::GetMac");
BindingId::new(interface_id)
.and_then(|id| ns.ctx.bindings_ctx().devices.get_core_id(id))
.ok_or(fnet_root::InterfacesGetMacError::NotFound)
.map(|core_id| {
let mac = match core_id.external_state() {
DeviceSpecificInfo::Loopback(_) => Some(LOOPBACK_MAC),
DeviceSpecificInfo::Ethernet(info) => Some(info.mac.into()),
DeviceSpecificInfo::PureIp(_) => None,
};
mac.map(|mac| Box::new(mac.into_fidl()))
})
}
pub(crate) async fn serve_routes_v4(
mut rs: fnet_root::RoutesV4RequestStream,
spawner: TaskWaitGroupSpawner,
ctx: &crate::bindings::Ctx,
) -> Result<(), fidl::Error> {
while let Some(req) = rs.try_next().await? {
match req {
fnet_root::RoutesV4Request::GlobalRouteSet { route_set, control_handle: _ } => {
let stream = route_set.into_stream()?;
let ctx = ctx.clone();
spawner.spawn(async {
serve_route_set::<Ipv4, _, _, _>(
stream,
GlobalRouteSet::new(ctx),
std::future::pending(), /* never cancelled */
)
.await;
});
}
}
}
Ok(())
}
pub(crate) async fn serve_routes_v6(
mut rs: fnet_root::RoutesV6RequestStream,
spawner: TaskWaitGroupSpawner,
ctx: &crate::bindings::Ctx,
) -> Result<(), fidl::Error> {
while let Some(req) = rs.try_next().await? {
match req {
fnet_root::RoutesV6Request::GlobalRouteSet { route_set, control_handle: _ } => {
let stream = route_set.into_stream()?;
let ctx = ctx.clone();
spawner.spawn(async {
serve_route_set::<Ipv6, _, _, _>(
stream,
GlobalRouteSet::new(ctx),
std::future::pending(), /* never cancelled */
)
.await;
});
}
}
}
Ok(())
}