fix: race in parallel wan veth creation cross-wiring namespaces

All wan.net.vmix@* instances created their veth pair with the same
temporary peer name 'vhost' in the host namespace before moving it into
their netns. Parallel starts at boot could steal each other's peer ends,
pairing a host-side vn-<ns> with another namespace's vhost (mismatched
/30s, dead links) or leaving the pair stranded in the host namespace.

Use a per-namespace temporary name (vh-<ns>) and rename to vhost only
after the move, making concurrent creation collision-free.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Git Sagar 2026-07-23 20:23:54 -03:00
parent 40e80df84a
commit 2f8925d439

View file

@ -172,6 +172,10 @@ let
let let
wanCfg = cfg // { spaceName = spaceName; }; wanCfg = cfg // { spaceName = spaceName; };
vethInNSToHost.iface = "vhost"; vethInNSToHost.iface = "vhost";
# Temporary peer name, unique per namespace. The peer briefly exists in the
# host namespace before being moved; a shared name ("vhost") lets parallel
# wan.net.vmix@* starts steal each other's peer ends, cross-wiring namespaces.
vethInNSToHost.tempIface = "vh-${wanCfg.spaceName}";
vethOnHostToNS.iface = "vn-${wanCfg.spaceName}"; vethOnHostToNS.iface = "vn-${wanCfg.spaceName}";
vethOnHostToNS.ipv4.address = calc.cidr.host 1 wanCfg.ipv4.range; vethOnHostToNS.ipv4.address = calc.cidr.host 1 wanCfg.ipv4.range;
vethInNSToHost.ipv4.address = calc.cidr.host 2 wanCfg.ipv4.range; vethInNSToHost.ipv4.address = calc.cidr.host 2 wanCfg.ipv4.range;
@ -180,8 +184,9 @@ let
portForwardRules = lib.concatStringsSep "\n" (lib.mapAttrsToList (hostIPnPort: nsPort: "iptables -t nat -A PREROUTING -p tcp --dport ${hostIPnPort} -j DNAT --to-destination ${vethInNSToHost.ipv4.address}:${toString nsPort}") wanCfg.forwardPorts); portForwardRules = lib.concatStringsSep "\n" (lib.mapAttrsToList (hostIPnPort: nsPort: "iptables -t nat -A PREROUTING -p tcp --dport ${hostIPnPort} -j DNAT --to-destination ${vethInNSToHost.ipv4.address}:${toString nsPort}") wanCfg.forwardPorts);
createWanCommands = '' createWanCommands = ''
ip link add ${vethOnHostToNS.iface} type veth peer name ${vethInNSToHost.iface} ip link add ${vethOnHostToNS.iface} type veth peer name ${vethInNSToHost.tempIface}
ip link set ${vethInNSToHost.iface} netns ${wanCfg.spaceName}.vmix ip link set ${vethInNSToHost.tempIface} netns ${wanCfg.spaceName}.vmix
ip netns exec ${wanCfg.spaceName}.vmix ip link set ${vethInNSToHost.tempIface} name ${vethInNSToHost.iface}
ip address add ${vethOnHostToNS.ipv4.address}/${networkPrefix} dev ${vethOnHostToNS.iface} ip address add ${vethOnHostToNS.ipv4.address}/${networkPrefix} dev ${vethOnHostToNS.iface}
ip netns exec ${wanCfg.spaceName}.vmix ip address add ${vethInNSToHost.ipv4.address}/${networkPrefix} dev ${vethInNSToHost.iface} ip netns exec ${wanCfg.spaceName}.vmix ip address add ${vethInNSToHost.ipv4.address}/${networkPrefix} dev ${vethInNSToHost.iface}