platform: modularize api/gui, add docs-tests-web foundation, and refresh root config
This commit is contained in:
144
selective-vpn-api/app/transport_netns.go
Normal file
144
selective-vpn-api/app/transport_netns.go
Normal file
@@ -0,0 +1,144 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
transportNetnsNATTable = "svpn_netns"
|
||||
)
|
||||
|
||||
type transportNetnsSpec struct {
|
||||
Name string
|
||||
HostVeth string
|
||||
PeerVeth string
|
||||
Prefix netip.Prefix
|
||||
HostIP netip.Addr
|
||||
PeerIP netip.Addr
|
||||
Uplink string
|
||||
}
|
||||
|
||||
func transportNetnsEnabled(client TransportClient) bool {
|
||||
if !transportConfigHasKey(client.Config, "netns_enabled") {
|
||||
return false
|
||||
}
|
||||
return transportConfigBool(client.Config, "netns_enabled")
|
||||
}
|
||||
|
||||
func transportNetnsStrict(client TransportClient) bool {
|
||||
return transportConfigBool(client.Config, "netns_setup_strict")
|
||||
}
|
||||
|
||||
func transportNetnsAutoCleanup(client TransportClient) bool {
|
||||
return transportConfigBool(client.Config, "netns_auto_cleanup")
|
||||
}
|
||||
|
||||
func transportEnsureNetnsForClient(client TransportClient) (string, error) {
|
||||
spec, err := transportBuildNetnsSpec(client)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
created := false
|
||||
exists, err := transportNetnsExists(spec.Name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !exists {
|
||||
if err := transportRunMust(6*time.Second, "ip", "netns", "add", spec.Name); err != nil {
|
||||
return "", err
|
||||
}
|
||||
created = true
|
||||
}
|
||||
|
||||
// Recreate veth pair for deterministic state before every start/restart.
|
||||
_ = transportRunSoft(3*time.Second, "ip", "link", "del", spec.HostVeth)
|
||||
_ = transportRunInNetnsSoft(3*time.Second, client, spec.Name, "ip", "link", "del", spec.PeerVeth)
|
||||
if err := transportRunMust(5*time.Second, "ip", "link", "add", spec.HostVeth, "type", "veth", "peer", "name", spec.PeerVeth); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := transportRunMust(5*time.Second, "ip", "link", "set", spec.PeerVeth, "netns", spec.Name); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
mask := strconv.Itoa(spec.Prefix.Bits())
|
||||
if err := transportRunMust(5*time.Second, "ip", "addr", "replace", spec.HostIP.String()+"/"+mask, "dev", spec.HostVeth); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := transportRunMust(5*time.Second, "ip", "link", "set", spec.HostVeth, "up"); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := transportRunInNetnsMust(5*time.Second, client, spec.Name, "ip", "link", "set", "lo", "up"); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := transportRunInNetnsMust(5*time.Second, client, spec.Name, "ip", "addr", "replace", spec.PeerIP.String()+"/"+mask, "dev", spec.PeerVeth); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := transportRunInNetnsMust(5*time.Second, client, spec.Name, "ip", "link", "set", spec.PeerVeth, "up"); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := transportRunInNetnsMust(5*time.Second, client, spec.Name, "ip", "route", "replace", "default", "via", spec.HostIP.String(), "dev", spec.PeerVeth); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := transportRunMust(5*time.Second, "sysctl", "-w", "net.ipv4.ip_forward=1"); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := transportEnsureNetnsNAT(spec); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := transportEnsureNetnsPolicyRoute(spec); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
msg := fmt.Sprintf(
|
||||
"netns ready: %s (%s<->%s, subnet=%s, uplink=%s)",
|
||||
spec.Name,
|
||||
spec.HostVeth,
|
||||
spec.PeerVeth,
|
||||
spec.Prefix.String(),
|
||||
spec.Uplink,
|
||||
)
|
||||
if created {
|
||||
msg += "; created"
|
||||
}
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
func transportCleanupNetnsForClient(client TransportClient) (string, error) {
|
||||
spec, err := transportBuildNetnsSpec(client)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
exists, err := transportNetnsExists(spec.Name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !exists {
|
||||
_ = transportNetnsDeleteNATRule(spec.Name)
|
||||
return "netns not found (skip cleanup)", nil
|
||||
}
|
||||
|
||||
var warnings []string
|
||||
if err := transportNetnsDeleteNATRule(spec.Name); err != nil {
|
||||
warnings = append(warnings, err.Error())
|
||||
}
|
||||
if err := transportDeleteNetnsPolicyRoute(spec); err != nil {
|
||||
warnings = append(warnings, err.Error())
|
||||
}
|
||||
if err := transportRunSoft(4*time.Second, "ip", "netns", "del", spec.Name); err != nil {
|
||||
warnings = append(warnings, err.Error())
|
||||
}
|
||||
_ = transportRunSoft(3*time.Second, "ip", "link", "del", spec.HostVeth)
|
||||
|
||||
msg := "netns cleaned: " + spec.Name
|
||||
if len(warnings) > 0 {
|
||||
return msg, errors.New(strings.Join(warnings, "; "))
|
||||
}
|
||||
return msg, nil
|
||||
}
|
||||
Reference in New Issue
Block a user