netcfg: add -connmark flag for DNAT reply routing
When VPN traffic is DNAT'd to local namespaces/VMs, reply packets have a different source IP (namespace veth) so the policy route's "from <VPN_IP>" rule doesn't match. CONNMARK marks all connections arriving on the VPN interface and restores the mark on reply packets, routing them back through the tunnel via fwmark rule. New flag: -connmark (requires -policy-route-table) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
857733863c
commit
51824b830e
5 changed files with 51 additions and 7 deletions
|
|
@ -21,6 +21,7 @@ type Options struct {
|
|||
AcceptStaticRoutes bool
|
||||
AcceptDNS bool
|
||||
PolicyRouteTable int
|
||||
ConnMark bool
|
||||
}
|
||||
|
||||
// ConfigureTAP sets the IP address, routes, and DNS on a TAP interface from a DHCP lease.
|
||||
|
|
@ -94,11 +95,16 @@ func ReconfigureTAP(ifname string, lease *dhcp.Lease, acceptDefaultGW, acceptSta
|
|||
// ConfigurePolicyRoute sets up policy routing so packets from the VPN IP are routed
|
||||
// back through the VPN gateway. Needed when the VPN server forwards ports to the
|
||||
// client — without it, reply packets use the default route instead of the VPN tunnel.
|
||||
func ConfigurePolicyRoute(ifname string, lease *dhcp.Lease, table int) func() {
|
||||
//
|
||||
// When connmark is true, also sets up CONNMARK rules so that DNAT'd connections
|
||||
// (e.g. port forwards to namespaces/VMs) have their reply traffic routed back
|
||||
// through the tunnel.
|
||||
func ConfigurePolicyRoute(ifname string, lease *dhcp.Lease, table int, connmark bool) func() {
|
||||
t := fmt.Sprintf("%d", table)
|
||||
clientIP := lease.ClientIP.String()
|
||||
gw := lease.Gateway.String()
|
||||
|
||||
// Policy route: packets from VPN IP use VPN gateway
|
||||
runQuiet("ip", "rule", "del", "table", t)
|
||||
run("ip", "route", "replace", "default", "via", gw, "dev", ifname, "table", t)
|
||||
if err := run("ip", "rule", "add", "from", clientIP, "table", t); err != nil {
|
||||
|
|
@ -107,9 +113,34 @@ func ConfigurePolicyRoute(ifname string, lease *dhcp.Lease, table int) func() {
|
|||
log.Printf("policy route: from %s via %s dev %s table %s", clientIP, gw, ifname, t)
|
||||
}
|
||||
|
||||
if connmark {
|
||||
mark := t
|
||||
// CONNMARK: mark connections arriving on VPN interface, restore mark on replies.
|
||||
// This ensures DNAT'd traffic (forwarded to namespaces/VMs) returns via the
|
||||
// tunnel instead of the default route. Without this, reply packets from DNAT
|
||||
// targets (e.g. namespace veth) have a different source IP than the VPN IP,
|
||||
// so the "from <VPN_IP>" policy rule doesn't match them.
|
||||
log.Printf("connmark: adding CONNMARK rules on %s (mark %s)", ifname, mark)
|
||||
runQuiet("ip", "rule", "del", "fwmark", mark, "table", t)
|
||||
run("iptables", "-t", "mangle", "-A", "PREROUTING", "-i", ifname, "-j", "CONNMARK", "--set-mark", mark)
|
||||
run("iptables", "-t", "mangle", "-A", "PREROUTING", "!", "-i", ifname, "-m", "connmark", "--mark", mark, "-j", "CONNMARK", "--restore-mark")
|
||||
if err := run("ip", "rule", "add", "fwmark", mark, "table", t); err != nil {
|
||||
log.Printf("warning: fwmark rule: %v", err)
|
||||
} else {
|
||||
log.Printf("connmark: fwmark %s → table %s for DNAT reply routing", mark, t)
|
||||
}
|
||||
}
|
||||
|
||||
return func() {
|
||||
runQuiet("ip", "rule", "del", "table", t)
|
||||
runQuiet("ip", "route", "del", "default", "table", t)
|
||||
if connmark {
|
||||
mark := t
|
||||
log.Printf("connmark: removing CONNMARK rules (mark %s)", mark)
|
||||
runQuiet("ip", "rule", "del", "fwmark", mark, "table", t)
|
||||
runQuiet("iptables", "-t", "mangle", "-D", "PREROUTING", "-i", ifname, "-j", "CONNMARK", "--set-mark", mark)
|
||||
runQuiet("iptables", "-t", "mangle", "-D", "PREROUTING", "!", "-i", ifname, "-m", "connmark", "--mark", mark, "-j", "CONNMARK", "--restore-mark")
|
||||
}
|
||||
log.Printf("policy route: cleaned up table %s", t)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue