feat(forwarding): add hybrid forwarding mode with per-client bridge and VLAN settings
This commit is contained in:
@@ -225,6 +225,50 @@ pub async fn enable_proxy_arp(iface: &str) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// VLAN support (802.1Q via Linux bridge VLAN filtering)
|
||||
// ============================================================================
|
||||
|
||||
async fn run_bridge_cmd(args: &[&str]) -> Result<String> {
|
||||
let output = tokio::process::Command::new("bridge")
|
||||
.args(args)
|
||||
.output()
|
||||
.await?;
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
anyhow::bail!("bridge {} failed: {}", args.join(" "), stderr.trim());
|
||||
}
|
||||
Ok(String::from_utf8_lossy(&output.stdout).to_string())
|
||||
}
|
||||
|
||||
/// Enable VLAN filtering on a bridge.
|
||||
pub async fn enable_vlan_filtering(bridge: &str) -> Result<()> {
|
||||
run_ip_cmd(&["link", "set", bridge, "type", "bridge", "vlan_filtering", "1"]).await?;
|
||||
info!("Enabled VLAN filtering on bridge {}", bridge);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Add a VLAN ID to a bridge port (TAP or physical interface).
|
||||
/// `pvid` = set as port VLAN ID (untagged ingress), `untagged` = strip tag on egress.
|
||||
pub async fn add_vlan_to_port(port: &str, vlan_id: u16, pvid: bool, untagged: bool) -> Result<()> {
|
||||
let mut args = vec!["vlan", "add", "dev", port, "vid"];
|
||||
let vid_str = vlan_id.to_string();
|
||||
args.push(&vid_str);
|
||||
if pvid { args.push("pvid"); }
|
||||
if untagged { args.push("untagged"); }
|
||||
run_bridge_cmd(&args).await?;
|
||||
info!("Added VLAN {} to port {} (pvid={}, untagged={})", vlan_id, port, pvid, untagged);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Remove a VLAN ID from a bridge port.
|
||||
pub async fn remove_vlan_from_port(port: &str, vlan_id: u16) -> Result<()> {
|
||||
let vid_str = vlan_id.to_string();
|
||||
run_bridge_cmd(&["vlan", "del", "dev", port, "vid", &vid_str]).await?;
|
||||
info!("Removed VLAN {} from port {}", vlan_id, port);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Create a TAP device (L2) using the tun crate.
|
||||
pub fn create_tap(name: &str, mtu: u16) -> Result<tun::AsyncDevice> {
|
||||
let mut config = tun::Configuration::default();
|
||||
|
||||
Reference in New Issue
Block a user