Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F73593
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
10 KB
Subscribers
None
View Options
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
Details
Attached
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)
Attached To
rCOLLAR collar
Event Timeline
Log In to Comment