use std::net::Ipv6Addr;
use tracing::{debug, error, info, warn};
pub fn check_ipv6_forwarding() {
match std::fs::read_to_string("/proc/sys/net/ipv6/conf/all/forwarding") {
Ok(val) if val.trim() == "1" => {
debug!("IPv6 forwarding is enabled");
}
Ok(_) => {
error!(
"IPv6 forwarding is disabled. Enable with: \
sysctl -w net.ipv6.conf.all.forwarding=1"
);
std::process::exit(1);
}
Err(e) => {
error!(error = %e, "Could not check IPv6 forwarding state");
std::process::exit(1);
}
}
}
pub async fn check_interface_exists(name: &str) -> Result<u32, std::io::Error> {
let index = rustables::iface_index(name)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::NotFound, e.to_string()))?;
debug!(interface = %name, index, "Interface found");
Ok(index)
}
pub struct NetSetup {
lan_interface: String,
proxy_entries: Vec<Ipv6Addr>,
route_added: bool,
pool_cidr: String,
}
impl NetSetup {
pub fn new(lan_interface: String, pool_cidr: String) -> Self {
Self {
lan_interface,
proxy_entries: Vec::new(),
route_added: false,
pool_cidr,
}
}
pub async fn add_pool_route(&mut self) -> Result<(), std::io::Error> {
let output = tokio::process::Command::new("ip")
.args(["-6", "route", "add", "local", &self.pool_cidr, "dev", "lo"])
.output()
.await?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
if stderr.contains("File exists") {
debug!(cidr = %self.pool_cidr, "Pool route already exists");
return Ok(());
}
return Err(std::io::Error::other(format!(
"Failed to add pool route: {stderr}"
)));
}
self.route_added = true;
info!(cidr = %self.pool_cidr, "Added local pool route");
Ok(())
}
pub async fn add_proxy_ndp(&mut self, addr: Ipv6Addr) -> Result<(), std::io::Error> {
let addr_str = addr.to_string();
let output = tokio::process::Command::new("ip")
.args([
"-6",
"neigh",
"add",
"proxy",
&addr_str,
"dev",
&self.lan_interface,
])
.output()
.await?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
if stderr.contains("File exists") {
debug!(addr = %addr, "Proxy NDP entry already exists");
return Ok(());
}
return Err(std::io::Error::other(format!(
"Failed to add proxy NDP: {stderr}"
)));
}
self.proxy_entries.push(addr);
debug!(addr = %addr, iface = %self.lan_interface, "Added proxy NDP entry");
Ok(())
}
pub async fn remove_proxy_ndp(&mut self, addr: Ipv6Addr) -> Result<(), std::io::Error> {
let addr_str = addr.to_string();
let output = tokio::process::Command::new("ip")
.args([
"-6",
"neigh",
"del",
"proxy",
&addr_str,
"dev",
&self.lan_interface,
])
.output()
.await?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
if !stderr.contains("No such file") {
warn!(addr = %addr, error = %stderr.trim(), "Failed to remove proxy NDP");
}
}
self.proxy_entries.retain(|a| *a != addr);
Ok(())
}
pub async fn cleanup(&mut self) {
let entries: Vec<Ipv6Addr> = self.proxy_entries.clone();
for addr in entries {
let _ = self.remove_proxy_ndp(addr).await;
}
if self.route_added {
let output = tokio::process::Command::new("ip")
.args(["-6", "route", "del", "local", &self.pool_cidr, "dev", "lo"])
.output()
.await;
match output {
Ok(o) if o.status.success() => {
info!(cidr = %self.pool_cidr, "Removed pool route");
}
Ok(o) => {
let stderr = String::from_utf8_lossy(&o.stderr);
warn!(error = %stderr.trim(), "Failed to remove pool route");
}
Err(e) => {
warn!(error = %e, "Failed to run ip route del");
}
}
self.route_added = false;
}
}
}