mirror of
https://github.com/edera-dev/krata.git
synced 2025-08-03 13:11:31 +00:00
hypha: implement automated network backend launcher
This commit is contained in:
@ -44,6 +44,7 @@ cli-tables = "0.2.1"
|
|||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
arrayvec = "0.7.4"
|
arrayvec = "0.7.4"
|
||||||
rtnetlink = "0.14.1"
|
rtnetlink = "0.14.1"
|
||||||
|
netlink-packet-route = "0.19.0"
|
||||||
futures = "0.3.30"
|
futures = "0.3.30"
|
||||||
ipnetwork = "0.20.0"
|
ipnetwork = "0.20.0"
|
||||||
smoltcp = "0.11.0"
|
smoltcp = "0.11.0"
|
||||||
|
@ -27,6 +27,7 @@ oci-spec = { workspace = true }
|
|||||||
backhand = { workspace = true }
|
backhand = { workspace = true }
|
||||||
uuid = { workspace = true }
|
uuid = { workspace = true }
|
||||||
rtnetlink = { workspace = true }
|
rtnetlink = { workspace = true }
|
||||||
|
netlink-packet-route = { workspace = true }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
futures = { workspace = true }
|
futures = { workspace = true }
|
||||||
ipnetwork = { workspace = true }
|
ipnetwork = { workspace = true }
|
||||||
|
@ -1,20 +1,19 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use env_logger::Env;
|
use env_logger::Env;
|
||||||
use hypha::network::HyphaNetwork;
|
use hypha::network::NetworkService;
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
struct NetworkArgs {
|
struct NetworkArgs {
|
||||||
#[arg(short, long)]
|
|
||||||
interface: String,
|
|
||||||
#[arg(short, long, default_value = "192.168.42.1/24")]
|
#[arg(short, long, default_value = "192.168.42.1/24")]
|
||||||
network: String,
|
network: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<()> {
|
||||||
env_logger::Builder::from_env(Env::default().default_filter_or("warn")).init();
|
env_logger::Builder::from_env(Env::default().default_filter_or("warn")).init();
|
||||||
let args = NetworkArgs::parse();
|
let args = NetworkArgs::parse();
|
||||||
let mut network = HyphaNetwork::new(&args.interface, &[&args.network])?;
|
let mut service = NetworkService::new(args.network)?;
|
||||||
network.run()?;
|
service.watch().await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,39 @@
|
|||||||
use std::os::fd::AsRawFd;
|
use std::os::fd::AsRawFd;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
use std::thread;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use advmac::MacAddr6;
|
use advmac::MacAddr6;
|
||||||
use anyhow::{anyhow, Result};
|
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::iface::{Config, Interface, SocketSet};
|
||||||
use smoltcp::phy::{self, RawSocket};
|
use smoltcp::phy::{self, RawSocket};
|
||||||
use smoltcp::time::Instant;
|
use smoltcp::time::Instant;
|
||||||
use smoltcp::wire::{EthernetAddress, HardwareAddress, IpCidr};
|
use smoltcp::wire::{EthernetAddress, HardwareAddress, IpCidr};
|
||||||
|
use tokio::time::sleep;
|
||||||
|
|
||||||
pub struct HyphaNetwork {
|
pub struct NetworkBackend {
|
||||||
|
pub interface: String,
|
||||||
pub device: RawSocket,
|
pub device: RawSocket,
|
||||||
pub addresses: Vec<IpCidr>,
|
pub addresses: Vec<IpCidr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HyphaNetwork {
|
unsafe impl Send for NetworkBackend {}
|
||||||
pub fn new(iface: &str, cidrs: &[&str]) -> Result<HyphaNetwork> {
|
|
||||||
|
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 device = RawSocket::new(iface, smoltcp::phy::Medium::Ethernet)?;
|
||||||
let mut addresses: Vec<IpCidr> = Vec::new();
|
let mut addresses: Vec<IpCidr> = Vec::new();
|
||||||
for cidr in cidrs {
|
for cidr in cidrs {
|
||||||
@ -22,7 +41,32 @@ impl HyphaNetwork {
|
|||||||
IpCidr::from_str(cidr).map_err(|_| anyhow!("failed to parse cidr: {}", *cidr))?;
|
IpCidr::from_str(cidr).map_err(|_| anyhow!("failed to parse cidr: {}", *cidr))?;
|
||||||
addresses.push(address);
|
addresses.push(address);
|
||||||
}
|
}
|
||||||
Ok(HyphaNetwork { device, addresses })
|
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?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&mut self) -> Result<()> {
|
pub fn run(&mut self) -> Result<()> {
|
||||||
@ -45,3 +89,62 @@ impl HyphaNetwork {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(5)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn add_network_backend(&mut self, interface: &str) -> Result<()> {
|
||||||
|
let interface = interface.to_string();
|
||||||
|
let mut network = NetworkBackend::new(&interface, &[&self.network])?;
|
||||||
|
network.init().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