package app import ( "strconv" "strings" "time" ) func transportEnsureNetnsNAT(spec transportNetnsSpec) error { _ = transportRunSoft(4*time.Second, "nft", "add", "table", "ip", transportNetnsNATTable) _ = transportRunSoft( 4*time.Second, "nft", "add", "chain", "ip", transportNetnsNATTable, "postrouting", "{", "type", "nat", "hook", "postrouting", "priority", "srcnat;", "policy", "accept;", "}", ) if err := transportNetnsDeleteNATRule(spec.Name); err != nil { return err } tag := transportNetnsNATCommentTag(spec.Name) return transportRunMust( 4*time.Second, "nft", "add", "rule", "ip", transportNetnsNATTable, "postrouting", "ip", "saddr", spec.Prefix.String(), "oifname", spec.Uplink, "masquerade", "comment", tag, ) } func transportNetnsDeleteNATRule(nsName string) error { stdout, stderr, code, err := transportRunCommand(4*time.Second, "nft", "-a", "list", "chain", "ip", transportNetnsNATTable, "postrouting") if err != nil || code != 0 { if strings.Contains(strings.ToLower(strings.TrimSpace(stderr+" "+stdout)), "no such file") { return nil } return transportCommandError("nft -a list chain ip "+transportNetnsNATTable+" postrouting", stdout, stderr, code, err) } tag := `comment "` + transportNetnsNATCommentTag(nsName) + `"` legacyTag := `comment "svpn_netns:` + nsName + `"` for _, line := range strings.Split(stdout, "\n") { if !strings.Contains(line, tag) && !strings.Contains(line, legacyTag) { continue } h := parseNftHandle(line) if h <= 0 { continue } _ = transportRunSoft(3*time.Second, "nft", "delete", "rule", "ip", transportNetnsNATTable, "postrouting", "handle", strconv.Itoa(h)) } return nil } func transportEnsureNetnsPolicyRoute(spec transportNetnsSpec) error { table := strings.TrimSpace(routesTableName()) if table == "" { return nil } return transportRunMust( 4*time.Second, "ip", "-4", "route", "replace", spec.Prefix.String(), "dev", spec.HostVeth, "table", table, ) } func transportDeleteNetnsPolicyRoute(spec transportNetnsSpec) error { table := strings.TrimSpace(routesTableName()) if table == "" { return nil } if err := transportRunSoft( 4*time.Second, "ip", "-4", "route", "del", spec.Prefix.String(), "table", table, ); err != nil { return err } return nil } func transportNetnsNATCommentTag(nsName string) string { base := sanitizeID(strings.TrimSpace(nsName)) if base == "" { base = "ns" } return "svpn_netns_" + base }