network: implement host bridging

This commit is contained in:
Alex Zenla 2024-03-03 13:36:39 +00:00
parent 2eeb8d5034
commit 25a02f5491
No known key found for this signature in database
GPG Key ID: 067B238899B51269
5 changed files with 157 additions and 3 deletions

View File

@ -55,6 +55,7 @@ etherparse = "0.14.2"
async-trait = "0.1.77"
bytes = "1.5.0"
path-absolutize = "3.1.1"
tokio-tun = "0.11.2"
[workspace.dependencies.uuid]
version = "1.6.1"

View File

@ -20,6 +20,7 @@ etherparse = { workspace = true }
async-trait = { workspace = true }
uuid = { workspace = true }
bytes = { workspace = true }
tokio-tun = { workspace = true }
[dependencies.advmac]
path = "../libs/advmac"

View File

@ -10,6 +10,6 @@ struct NetworkArgs {}
async fn main() -> Result<()> {
env_logger::Builder::from_env(Env::default().default_filter_or("warn")).init();
let _ = NetworkArgs::parse();
let mut service = NetworkService::new()?;
let mut service = NetworkService::new().await?;
service.watch().await
}

146
network/src/hbridge.rs Normal file
View File

@ -0,0 +1,146 @@
use std::net::{IpAddr, Ipv4Addr};
use advmac::MacAddr6;
use anyhow::{anyhow, Result};
use bytes::BytesMut;
use futures::TryStreamExt;
use log::error;
use smoltcp::wire::EthernetAddress;
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
select,
sync::mpsc::channel,
task::JoinHandle,
};
use tokio_tun::Tun;
use crate::vbridge::{BridgeJoinHandle, VirtualBridge};
pub struct HostBridge {
task: JoinHandle<()>,
}
impl HostBridge {
pub async fn new(interface: String, bridge: &VirtualBridge) -> Result<HostBridge> {
let tun = Tun::builder()
.name(&interface)
.tap(true)
.mtu(1500)
.packet_info(false)
.try_build()?;
let (connection, handle, _) = rtnetlink::new_connection()?;
tokio::spawn(connection);
let mut mac = MacAddr6::random();
mac.set_local(true);
mac.set_multicast(false);
let mut links = handle.link().get().match_name(interface.clone()).execute();
let link = links.try_next().await?;
if link.is_none() {
return Err(anyhow!(
"unable to find network interface named {}",
interface
));
}
let link = link.unwrap();
handle
.address()
.add(
link.header.index,
IpAddr::V4(Ipv4Addr::new(10, 75, 0, 1)),
16,
)
.execute()
.await?;
handle
.address()
.add(link.header.index, IpAddr::V6(mac.to_link_local_ipv6()), 10)
.execute()
.await?;
handle
.link()
.set(link.header.index)
.address(mac.to_array().to_vec())
.up()
.execute()
.await?;
let mac = EthernetAddress(mac.to_array());
let bridge_handle = bridge.join(mac).await?;
let task = tokio::task::spawn(async move {
if let Err(error) = HostBridge::process(tun, bridge_handle).await {
error!("failed to process host bridge: {}", error);
}
});
Ok(HostBridge { task })
}
async fn process(tun: Tun, mut bridge_handle: BridgeJoinHandle) -> Result<()> {
let (rx_sender, mut rx_receiver) = channel::<BytesMut>(100);
let (mut read, mut write) = tokio::io::split(tun);
tokio::task::spawn(async move {
let mut buffer = vec![0u8; 1500];
loop {
let size = match read.read(&mut buffer).await {
Ok(size) => size,
Err(error) => {
error!("failed to read tap device: {}", error);
break;
}
};
match rx_sender.send(buffer[0..size].into()).await {
Ok(_) => {}
Err(error) => {
error!(
"failed to send data from tap device to processor: {}",
error
);
break;
}
}
}
});
loop {
select! {
x = bridge_handle.from_bridge_receiver.recv() => match x {
Some(bytes) => {
write.write_all(&bytes).await?;
},
None => {
break;
}
},
x = bridge_handle.from_broadcast_receiver.recv() => match x {
Ok(bytes) => {
write.write_all(&bytes).await?;
},
Err(error) => {
return Err(error.into());
}
},
x = rx_receiver.recv() => match x {
Some(bytes) => {
bridge_handle.to_bridge_sender.send(bytes).await?;
},
None => {
break;
}
}
};
}
Ok(())
}
}
impl Drop for HostBridge {
fn drop(&mut self) {
self.task.abort();
}
}

View File

@ -3,6 +3,7 @@ use std::{collections::HashMap, thread, time::Duration};
use anyhow::Result;
use autonet::{AutoNetworkChangeset, AutoNetworkCollector, NetworkMetadata};
use futures::{future::join_all, TryFutureExt};
use hbridge::HostBridge;
use log::warn;
use tokio::{task::JoinHandle, time::sleep};
use uuid::Uuid;
@ -13,6 +14,7 @@ use crate::backend::NetworkBackend;
pub mod autonet;
pub mod backend;
pub mod chandev;
pub mod hbridge;
pub mod icmp;
pub mod nat;
pub mod pkt;
@ -23,13 +25,17 @@ pub mod vbridge;
pub struct NetworkService {
pub backends: HashMap<Uuid, JoinHandle<()>>,
pub bridge: VirtualBridge,
pub hbridge: HostBridge,
}
impl NetworkService {
pub fn new() -> Result<NetworkService> {
pub async fn new() -> Result<NetworkService> {
let bridge = VirtualBridge::new()?;
let hbridge = HostBridge::new("krata0".to_string(), &bridge).await?;
Ok(NetworkService {
backends: HashMap::new(),
bridge: VirtualBridge::new()?,
bridge,
hbridge,
})
}
}