Page MenuHomePhabricator

No OneTemporary

diff --git a/src/bin/collared.rs b/src/bin/collared.rs
index 4507d09..ca76558 100644
--- a/src/bin/collared.rs
+++ b/src/bin/collared.rs
@@ -1,33 +1,53 @@
use std::env::args;
use tokio::net::TcpListener;
use tower_http::trace::TraceLayer;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
+use std::sync::Arc;
+
use axum::{
- routing::get
+ routing::get,
+ extract::{State, Request, Json, Path, Extension, Query},
+ routing::post,
};
+struct AppState {
+ leash: collar::NetworkLeash,
+}
+
#[tokio::main]
async fn main() {
tracing_subscriber::registry()
.with(
tracing_subscriber::filter::EnvFilter::try_from_default_env().unwrap_or_else(|_| {
- "collar=debug,tower_http=debug,axum::rejection=trace"
+ "collar=trace,tower_http=debug,axum::rejection=trace"
.into()
}),
)
.with(tracing_subscriber::fmt::layer())
.init();
+ let leash = collar::NetworkLeash::new();
+ let state = Arc::new(AppState { leash });
// build our application with a single route
let app = axum::Router::new()
- .route("/", get(|| async { "hic!" }))
+ .route("/", get(|| async { "collared." }))
+ .route("/ng/eiface/{name}", post(post_ng_eiface))
+ .with_state(state)
.layer(tower_http::trace::TraceLayer::new_for_http());
// run our app with hyper, listening globally on port 3000
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
tracing::info!("listening on {}", listener.local_addr().unwrap());
axum::serve(listener, app).await.unwrap();
}
+
+async fn post_ng_eiface(State(state): State<Arc<AppState>>, Path(name): Path<String>) -> Result<String, String> {
+ let fname = name.clone();
+ let result = state.leash.gated(move || {
+ collar::ng::new_eiface(&name)
+ }).await;
+ Ok(format!("sup {} => {:?}", &fname, result))
+}
diff --git a/src/error.rs b/src/error.rs
index 0ba8d67..d8757f9 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,53 +1,56 @@
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("sys err")]
SystemError(errno::Errno),
#[error("not a bridge")]
PathIsNotABridge,
#[error("invalid node type")]
InvalidNodeType(String, String),
#[error("bridge already exists")]
BridgeLinkExists(String, String),
#[error("utf8")]
InvalidUtf8(std::str::Utf8Error),
#[error("nul")]
NulError(String),
#[error("exists")]
Exists,
#[error("invalid file descriptor")]
InvalidDescriptor,
#[error("name too long")]
NameTooLong,
#[error("description too long")]
DescriptionTooLong,
#[error("interface not found")]
InterfaceNotFound,
#[error("out of memory")]
OutOfMemory,
#[error("operation not supported")]
NotSupported,
+ #[error("join error")]
+ TaskJoin(#[from] tokio::task::JoinError),
+
#[error(transparent)]
Io(#[from] std::io::Error),
#[error(transparent)]
Nul(#[from] std::ffi::NulError),
#[error(transparent)]
ParseNum(#[from] std::num::ParseIntError),
}
pub type Result<T, E = Error> = ::std::result::Result<T, E>;
diff --git a/src/lib.rs b/src/lib.rs
index a8b27b7..ed942eb 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,5 +1,49 @@
pub mod error;
pub mod netif;
pub mod ng;
+
+use std::sync::Arc;
+use tokio::sync::Mutex;
+
+use crate::error::{Error, Result};
+
+pub struct NetworkLeash {
+ lock: Arc<Mutex<()>>
+}
+
+impl NetworkLeash {
+ pub fn new() -> Self {
+ tracing::trace!("Creating new NetworkLeash");
+ Self {
+ lock: Arc::new(Mutex::new(())),
+ }
+ }
+
+ pub async fn with_interface<F, R>(&self, name: &str, operation: F) -> Result<R>
+ where
+ F: FnOnce(&mut crate::netif::Iface) -> Result<R> + Send + 'static,
+ R: Send + 'static,
+ {
+ let _guard = self.lock.lock().await;
+ let name = name.to_string();
+
+ tokio::task::spawn_blocking(move || {
+ let mut iface = crate::netif::Iface::new(&name)?;
+ operation(&mut iface)
+ }).await?
+ }
+
+ pub async fn gated<F, R>(&self, operation: F) -> Result<R>
+ where
+ F: FnOnce() -> Result<R> + Send + 'static,
+ R: Send + 'static,
+ {
+ let _guard = self.lock.lock().await;
+
+ tokio::task::spawn_blocking(move || {
+ operation()
+ }).await?
+ }
+}
diff --git a/src/netif/iface.rs b/src/netif/iface.rs
index 4c41493..ee36087 100644
--- a/src/netif/iface.rs
+++ b/src/netif/iface.rs
@@ -1,155 +1,178 @@
use libc::{
- self, AF_INET, IFNAMSIZ, SOCK_DGRAM, c_char, c_void, ifreq,
+ self, AF_INET, IFNAMSIZ, SOCK_DGRAM, IFF_UP, IFF_RUNNING, c_char, c_void, ifreq, c_short
};
use std::{
ffi::CString,
mem,
os::unix::io::AsRawFd,
ptr,
};
use crate::error::{Error, Result};
use crate::netif::{fd::Fd, sys, sys::*};
pub struct Iface {
pub name: String,
ctl: Fd
}
impl Iface {
pub fn new(name: &str) -> Result<Self> {
let ctl = unsafe { Fd::new(libc::socket(AF_INET, SOCK_DGRAM, 0), true)? };
Ok(Self {
name: name.to_string(),
ctl,
})
}
unsafe fn request(&self) -> Result<ifreq> {
if self.name.len() >= IFNAMSIZ {
return Err(Error::NameTooLong);
}
let mut req: ifreq = mem::zeroed();
let name_bytes = self.name.as_bytes();
ptr::copy_nonoverlapping(
name_bytes.as_ptr() as *const c_char,
req.ifr_name.as_mut_ptr(),
name_bytes.len(),
);
// Ensure null termination (mem::zeroed already did this)
Ok(req)
}
pub fn set_name(&mut self, value: &str) -> Result<()> {
if value.len() >= IFNAMSIZ {
return Err(Error::NameTooLong);
}
unsafe {
let mut req = self.request()?;
let if_name = CString::new(value)?;
req.ifr_ifru.ifru_data = if_name.as_ptr() as *mut c_char;
// Perform syscall while CString is still alive
let result = siocsifname(self.ctl.as_raw_fd(), &req);
// CString drops here naturally, after syscall
if let Err(err) = result {
return Err(std::io::Error::from(err).into());
}
self.name = value.to_string();
Ok(())
}
}
pub fn exists(&self) -> Result<bool> {
unsafe {
let mut req = self.request()?;
// Try to get interface flags to check if interface exists
match siocgifflags(self.ctl.as_raw_fd(), &mut req) {
Ok(_) => Ok(true),
Err(nix::errno::Errno::ENXIO) => Ok(false), // Device not configured
Err(err) => Err(std::io::Error::from(err).into()),
}
}
}
pub fn set_description(&mut self, value: &str) -> Result<()> {
unsafe {
// Check if interface exists first
if !self.exists()? {
return Err(Error::InterfaceNotFound);
}
let mut req: sys::ifreq_ext = mem::zeroed();
// Copy interface name with proper bounds checking
let name_bytes = self.name.as_bytes();
if name_bytes.len() >= IFNAMSIZ {
return Err(Error::NameTooLong);
}
ptr::copy_nonoverlapping(
name_bytes.as_ptr() as *const c_char,
req.ifr_name.as_mut_ptr(),
name_bytes.len(),
);
if value.is_empty() {
// Clear description
req.ifr_ifru.ifru_buffer.length = 0;
req.ifr_ifru.ifru_buffer.buffer = ptr::null_mut();
let result = siocsifdescr(self.ctl.as_raw_fd(), &req as *const _ as *const ifreq);
if let Err(err) = result {
return Err(std::io::Error::from(err).into());
}
} else {
if value.len() >= IFDESCRSIZ {
return Err(Error::DescriptionTooLong);
}
// Create null-terminated string and keep it alive during syscall
let desc_cstring = CString::new(value)?;
req.ifr_ifru.ifru_buffer.length = desc_cstring.as_bytes_with_nul().len() as u32;
req.ifr_ifru.ifru_buffer.buffer = desc_cstring.as_ptr() as *mut c_void;
// Perform syscall while CString is still alive
let result = siocsifdescr(self.ctl.as_raw_fd(), &req as *const _ as *const ifreq);
// CString drops here naturally, after syscall
if let Err(err) = result {
return Err(std::io::Error::from(err).into());
}
}
Ok(())
}
}
pub fn set_mtu(&mut self, mtu: i32) -> Result<()> {
unsafe {
// Check if interface exists first
if !self.exists()? {
return Err(Error::InterfaceNotFound);
}
let mut req = self.request()?;
-
+
// Set the MTU value
req.ifr_ifru.ifru_mtu = mtu;
// Perform the ioctl to set MTU
let result = siocsifmtu(self.ctl.as_raw_fd(), &req);
if let Err(err) = result {
return Err(std::io::Error::from(err).into());
}
Ok(())
}
}
+
+ pub fn enabled(&mut self, value: bool) -> Result<()> {
+ unsafe {
+ let mut req = self.request()?;
+
+ if let Err(err) = siocgifflags(self.ctl.as_raw_fd(), &mut req) {
+ return Err(std::io::Error::from(err).into());
+ }
+
+ if value {
+ req.ifr_ifru.ifru_flags[0] |= (IFF_UP | IFF_RUNNING) as c_short;
+ } else {
+ req.ifr_ifru.ifru_flags[0] &= !(IFF_UP as c_short);
+ }
+
+ if let Err(err) = siocsifflags(self.ctl.as_raw_fd(), &req) {
+ return Err(std::io::Error::from(err).into());
+ }
+
+ Ok(())
+ }
+ }
+
}

File Metadata

Mime Type
text/x-diff
Expires
Sat, Jun 7, 5:59 AM (12 h, 10 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
47506
Default Alt Text
(10 KB)

Event Timeline