blob: f2c177b22b300e584fde1f3713cefbf079240443 [file] [log] [blame]
// Copyright 2020 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.
//! LoWPAN Network Tunnel Abstraction
use crate::driver::*;
use crate::prelude::*;
use crate::spinel::Subnet;
use anyhow::Context as _;
use anyhow::Error;
use async_trait::async_trait;
use fidl::endpoints::{create_endpoints, create_proxy};
use fidl_fuchsia_hardware_network as fhwnet;
use fidl_fuchsia_net as fnet;
use fidl_fuchsia_net_ext as fnetext;
use fidl_fuchsia_net_stack as fnetstack;
use fidl_fuchsia_net_stack_ext::FidlReturn as _;
use fidl_fuchsia_net_tun as ftun;
use fuchsia_component::client::{connect_channel_to_service, connect_to_service};
use fuchsia_zircon as zx;
use futures::stream::BoxStream;
use parking_lot::Mutex;
const IPV6_MIN_MTU: u32 = 1280;
#[derive(Debug)]
pub struct TunNetworkInterface {
tun_dev: ftun::DeviceProxy,
stack: fnetstack::StackProxy,
stack_sync: Mutex<fnetstack::StackSynchronousProxy>,
id: u64,
}
impl TunNetworkInterface {
pub async fn try_new(name: Option<String>) -> Result<TunNetworkInterface, Error> {
let tun_control = connect_to_service::<ftun::ControlMarker>()?;
let (tun_dev, req) = create_proxy::<ftun::DeviceMarker>()?;
tun_control
.create_device(
ftun::DeviceConfig {
base: Some(ftun::BaseConfig {
mtu: Some(IPV6_MIN_MTU),
rx_types: Some(vec![
fhwnet::FrameType::Ipv6,
// TODO(fxbug.dev/64292): Remove this once netstack doesn't require it.
fhwnet::FrameType::Ipv4,
]),
tx_types: Some(vec![
fhwnet::FrameTypeSupport {
type_: fhwnet::FrameType::Ipv6,
features: fhwnet::FRAME_FEATURES_RAW,
supported_flags: fhwnet::TxFlags::empty(),
},
// TODO(fxbug.dev/64292): Remove this once netstack doesn't require it.
fhwnet::FrameTypeSupport {
type_: fhwnet::FrameType::Ipv4,
features: fhwnet::FRAME_FEATURES_RAW,
supported_flags: fhwnet::TxFlags::empty(),
},
]),
..ftun::BaseConfig::empty()
}),
online: Some(true),
blocking: Some(true),
..ftun::DeviceConfig::empty()
},
req,
)
.context("failed to create tun pair")?;
let (device, device_req) = create_endpoints::<fhwnet::DeviceMarker>()?;
tun_dev
.connect_protocols(ftun::Protocols {
network_device: Some(device_req),
..ftun::Protocols::empty()
})
.context("connect protocols failed")?;
let stack = connect_to_service::<fnetstack::StackMarker>()?;
let id = stack
.add_interface(
fnetstack::InterfaceConfig { name, ..fnetstack::InterfaceConfig::empty() },
&mut fnetstack::DeviceDefinition::Ip(device),
)
.await
.squash_result()
.context("Unable to add TUN interface to netstack")?;
let (client, server) = zx::Channel::create()?;
connect_channel_to_service::<fnetstack::StackMarker>(server)?;
let stack_sync = Mutex::new(fnetstack::StackSynchronousProxy::new(client));
Ok(TunNetworkInterface { tun_dev, stack, stack_sync, id })
}
}
#[async_trait]
impl NetworkInterface for TunNetworkInterface {
async fn outbound_packet_from_stack(&self) -> Result<Vec<u8>, Error> {
let frame = self
.tun_dev
.read_frame()
.await
.context("FIDL error on read_frame")?
.map_err(fuchsia_zircon::Status::from_raw)
.context("Error calling read_frame")?;
Ok(frame.data.ok_or(format_err!("data field was absent"))?)
}
async fn inbound_packet_to_stack(&self, packet: &[u8]) -> Result<(), Error> {
traceln!("Packet to stack: {}", hex::encode(packet));
Ok(self
.tun_dev
.write_frame(ftun::Frame {
frame_type: Some(fhwnet::FrameType::Ipv6),
data: Some(packet.to_vec()),
meta: None,
..fidl_fuchsia_net_tun::Frame::empty()
})
.await?
.map_err(fuchsia_zircon::Status::from_raw)?)
}
async fn set_online(&self, online: bool) -> Result<(), Error> {
fx_log_info!("Interface online: {:?}", online);
if online {
self.stack.enable_interface(self.id).await.squash_result()?;
} else {
self.stack.disable_interface(self.id).await.squash_result()?;
}
Ok(())
}
fn add_address(&self, addr: &Subnet) -> Result<(), Error> {
fx_log_info!("Address Added: {:?}", addr);
let mut addr = fnet::Subnet {
addr: fnetext::IpAddress(addr.addr.into()).into(),
prefix_len: addr.prefix_len,
};
self.stack_sync
.lock()
.add_interface_address(self.id, &mut addr, zx::Time::INFINITE)
.squash_result()?;
Ok(())
}
fn remove_address(&self, addr: &Subnet) -> Result<(), Error> {
fx_log_info!("Address Removed: {:?}", addr);
let mut addr = fnet::Subnet {
addr: fnetext::IpAddress(addr.addr.into()).into(),
prefix_len: addr.prefix_len,
};
self.stack_sync
.lock()
.del_interface_address(self.id, &mut addr, zx::Time::INFINITE)
.squash_result()?;
Ok(())
}
fn add_on_link_route(&self, addr: &Subnet) -> Result<(), Error> {
fx_log_info!("On Mesh Route Added: {:?} (IGNORED)", addr);
Ok(())
}
fn remove_on_link_route(&self, addr: &Subnet) -> Result<(), Error> {
fx_log_info!("On Mesh Route Removed: {:?} (IGNORED)", addr);
Ok(())
}
fn take_event_stream(&self) -> BoxStream<'_, Result<NetworkInterfaceEvent, Error>> {
let enabled_stream = futures::stream::try_unfold((), move |()| async move {
loop {
if let ftun::InternalState { has_session: Some(has_session), .. } =
self.tun_dev.watch_state().await?
{
break Ok(Some((
NetworkInterfaceEvent::InterfaceEnabledChanged(has_session),
(),
)));
}
}
});
enabled_stream.boxed()
}
}