Files
elmprodvpn/selective-vpn-api/app/transport_netns_rules.go

91 lines
2.4 KiB
Go

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
}