mirror of
https://github.com/edera-dev/krata.git
synced 2025-08-04 05:31:32 +00:00
hypha: move components into separate crates
This commit is contained in:
169
network/src/lib.rs
Normal file
169
network/src/lib.rs
Normal file
@ -0,0 +1,169 @@
|
||||
use std::os::fd::AsRawFd;
|
||||
use std::panic::UnwindSafe;
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
use std::{panic, thread};
|
||||
|
||||
use advmac::MacAddr6;
|
||||
use anyhow::{anyhow, Result};
|
||||
use futures::TryStreamExt;
|
||||
use log::{error, info, warn};
|
||||
use netlink_packet_route::link::LinkAttribute;
|
||||
use smoltcp::iface::{Config, Interface, SocketSet};
|
||||
use smoltcp::phy::{self, RawSocket};
|
||||
use smoltcp::time::Instant;
|
||||
use smoltcp::wire::{EthernetAddress, HardwareAddress, IpCidr};
|
||||
use tokio::time::sleep;
|
||||
|
||||
pub struct NetworkBackend {
|
||||
pub interface: String,
|
||||
pub device: RawSocket,
|
||||
pub addresses: Vec<IpCidr>,
|
||||
}
|
||||
|
||||
unsafe impl Send for NetworkBackend {}
|
||||
impl UnwindSafe for NetworkBackend {}
|
||||
|
||||
pub struct NetworkService {
|
||||
pub network: String,
|
||||
}
|
||||
|
||||
impl NetworkService {
|
||||
pub fn new(network: String) -> Result<NetworkService> {
|
||||
Ok(NetworkService { network })
|
||||
}
|
||||
}
|
||||
|
||||
impl NetworkBackend {
|
||||
pub fn new(iface: &str, cidrs: &[&str]) -> Result<NetworkBackend> {
|
||||
let device = RawSocket::new(iface, smoltcp::phy::Medium::Ethernet)?;
|
||||
let mut addresses: Vec<IpCidr> = Vec::new();
|
||||
for cidr in cidrs {
|
||||
let address =
|
||||
IpCidr::from_str(cidr).map_err(|_| anyhow!("failed to parse cidr: {}", *cidr))?;
|
||||
addresses.push(address);
|
||||
}
|
||||
Ok(NetworkBackend {
|
||||
interface: iface.to_string(),
|
||||
device,
|
||||
addresses,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn init(&mut self) -> Result<()> {
|
||||
let (connection, handle, _) = rtnetlink::new_connection()?;
|
||||
tokio::spawn(connection);
|
||||
|
||||
let mut links = handle
|
||||
.link()
|
||||
.get()
|
||||
.match_name(self.interface.clone())
|
||||
.execute();
|
||||
let link = links.try_next().await?;
|
||||
if link.is_none() {
|
||||
return Err(anyhow!(
|
||||
"unable to find network interface named {}",
|
||||
self.interface
|
||||
));
|
||||
}
|
||||
let link = link.unwrap();
|
||||
handle.link().set(link.header.index).up().execute().await?;
|
||||
tokio::time::sleep(Duration::from_secs(3)).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn run(mut self) -> Result<()> {
|
||||
let interface = self.interface.clone();
|
||||
let result = panic::catch_unwind(move || self.run_maybe_panic());
|
||||
|
||||
if result.is_err() {
|
||||
return Err(anyhow!(
|
||||
"network backend for interface {} encountered an error and is now shutdown",
|
||||
interface
|
||||
));
|
||||
}
|
||||
|
||||
result.unwrap()
|
||||
}
|
||||
|
||||
fn run_maybe_panic(&mut self) -> Result<()> {
|
||||
let mac = MacAddr6::random();
|
||||
let mac = HardwareAddress::Ethernet(EthernetAddress(mac.to_array()));
|
||||
let config = Config::new(mac);
|
||||
let mut iface = Interface::new(config, &mut self.device, Instant::now());
|
||||
iface.update_ip_addrs(|addrs| {
|
||||
addrs
|
||||
.extend_from_slice(&self.addresses)
|
||||
.expect("failed to set ip addresses");
|
||||
});
|
||||
|
||||
let mut sockets = SocketSet::new(vec![]);
|
||||
let fd = self.device.as_raw_fd();
|
||||
loop {
|
||||
let timestamp = Instant::now();
|
||||
iface.poll(timestamp, &mut self.device, &mut sockets);
|
||||
phy::wait(fd, iface.poll_delay(timestamp, &sockets))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NetworkService {
|
||||
pub async fn watch(&mut self) -> Result<()> {
|
||||
let mut spawned: Vec<String> = Vec::new();
|
||||
let (connection, handle, _) = rtnetlink::new_connection()?;
|
||||
tokio::spawn(connection);
|
||||
loop {
|
||||
let mut stream = handle.link().get().execute();
|
||||
while let Some(message) = stream.try_next().await? {
|
||||
let mut name: Option<String> = None;
|
||||
for attribute in &message.attributes {
|
||||
if let LinkAttribute::IfName(if_name) = attribute {
|
||||
name = Some(if_name.clone());
|
||||
}
|
||||
}
|
||||
|
||||
if name.is_none() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let name = name.unwrap();
|
||||
if !name.starts_with("vif") {
|
||||
continue;
|
||||
}
|
||||
|
||||
if spawned.contains(&name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Err(error) = self.add_network_backend(&name).await {
|
||||
warn!(
|
||||
"failed to initialize network backend for interface {}: {}",
|
||||
name, error
|
||||
);
|
||||
}
|
||||
|
||||
spawned.push(name);
|
||||
}
|
||||
|
||||
sleep(Duration::from_secs(2)).await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn add_network_backend(&mut self, interface: &str) -> Result<()> {
|
||||
let interface = interface.to_string();
|
||||
let mut network = NetworkBackend::new(&interface, &[&self.network])?;
|
||||
info!("initializing network backend for interface {}", interface);
|
||||
network.init().await?;
|
||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||
info!("spawning network backend for interface {}", interface);
|
||||
thread::spawn(move || {
|
||||
if let Err(error) = network.run() {
|
||||
error!(
|
||||
"failed to run network backend for interface {}: {}",
|
||||
interface, error
|
||||
);
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user