Add TUN interface with async netlink configuration

- Implement fips.rs binary with config loading, node creation, and logging
- Create src/tun.rs module (TunDevice, TunState, TunError)
- Use rtnetlink crate for IPv6 address configuration
- Make TunDevice::create() and Node::init_tun() async
- Add IPv6 disabled check with helpful error message
- Add rtnetlink, tokio, futures dependencies
- Single tokio current_thread runtime for entire driver

162 tests pass.
This commit is contained in:
Johnathan Corgan
2026-01-29 22:12:27 +00:00
parent 185fe50570
commit 6d95bdc979
7 changed files with 1258 additions and 12 deletions
+76 -2
View File
@@ -9,6 +9,7 @@ use crate::cache::CoordCache;
use crate::peer::Peer;
use crate::transport::{Link, LinkId, TransportId};
use crate::tree::TreeState;
use crate::tun::{TunDevice, TunError, TunState};
use crate::{Config, ConfigError, Identity, IdentityError, NodeId};
use std::collections::HashMap;
use std::fmt;
@@ -49,6 +50,9 @@ pub enum NodeError {
#[error("identity error: {0}")]
Identity(#[from] IdentityError),
#[error("TUN error: {0}")]
Tun(#[from] TunError),
}
/// Node operational state.
@@ -148,6 +152,12 @@ pub struct Node {
next_link_id: u64,
/// Next transport ID to allocate.
next_transport_id: u32,
// === TUN Interface ===
/// TUN device state.
tun_state: TunState,
/// TUN device (if active).
tun_device: Option<TunDevice>,
}
impl Node {
@@ -155,14 +165,27 @@ impl Node {
pub fn new(config: Config) -> Result<Self, NodeError> {
let identity = config.create_identity()?;
let node_id = *identity.node_id();
let is_leaf_only = config.is_leaf_only();
let bloom_state = if is_leaf_only {
BloomState::leaf_only(node_id)
} else {
BloomState::new(node_id)
};
let tun_state = if config.tun.enabled {
TunState::Configured
} else {
TunState::Disabled
};
Ok(Self {
identity,
config,
state: NodeState::Created,
is_leaf_only: false,
is_leaf_only,
tree_state: TreeState::new(node_id),
bloom_state: BloomState::new(node_id),
bloom_state,
coord_cache: CoordCache::with_defaults(),
transport_ids: Vec::new(),
links: HashMap::new(),
@@ -171,12 +194,19 @@ impl Node {
max_links: 256,
next_link_id: 1,
next_transport_id: 1,
tun_state,
tun_device: None,
})
}
/// Create a node with a specific identity.
pub fn with_identity(identity: Identity, config: Config) -> Self {
let node_id = *identity.node_id();
let tun_state = if config.tun.enabled {
TunState::Configured
} else {
TunState::Disabled
};
Self {
identity,
config,
@@ -192,6 +222,8 @@ impl Node {
max_links: 256,
next_link_id: 1,
next_transport_id: 1,
tun_state,
tun_device: None,
}
}
@@ -280,6 +312,48 @@ impl Node {
&mut self.coord_cache
}
// === TUN Interface ===
/// Get the TUN state.
pub fn tun_state(&self) -> TunState {
self.tun_state
}
/// Get the TUN device if active.
pub fn tun_device(&self) -> Option<&TunDevice> {
self.tun_device.as_ref()
}
/// Get mutable TUN device if active.
pub fn tun_device_mut(&mut self) -> Option<&mut TunDevice> {
self.tun_device.as_mut()
}
/// Initialize the TUN interface.
///
/// Creates and configures the TUN device based on the node's configuration.
/// Requires CAP_NET_ADMIN capability (run with sudo or setcap).
///
/// Returns Ok(true) if TUN was initialized, Ok(false) if TUN is disabled.
pub async fn init_tun(&mut self) -> Result<bool, NodeError> {
if !self.config.tun.enabled {
return Ok(false);
}
let address = *self.identity.address();
match TunDevice::create(&self.config.tun, address).await {
Ok(device) => {
self.tun_device = Some(device);
self.tun_state = TunState::Active;
Ok(true)
}
Err(e) => {
self.tun_state = TunState::Failed;
Err(e.into())
}
}
}
// === Resource Limits ===
/// Set the maximum number of peers.