Add netns support

This commit is contained in:
世界 2026-07-11 00:57:57 +08:00
parent d0d4ebd8db
commit 994d6ccdbf
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
7 changed files with 220 additions and 80 deletions

73
netns_linux.go Normal file
View file

@ -0,0 +1,73 @@
package tun
import (
"context"
"net"
"runtime"
"strings"
"github.com/sagernet/sing/common/control"
E "github.com/sagernet/sing/common/exceptions"
"golang.org/x/sys/unix"
)
func listenNetworkNamespace(ctx context.Context, nameOrPath string, config net.ListenConfig, network, address string) (net.Listener, error) {
return execInNetworkNamespace(nameOrPath, func() (net.Listener, error) {
return config.Listen(ctx, network, address)
})
}
type networkNamespaceInterfaceFinder struct {
control.InterfaceFinder
options *Options
}
func (f *networkNamespaceInterfaceFinder) Update() error {
return runInNetworkNamespace(f.options.NetNs, f.InterfaceFinder.Update)
}
func execInNetworkNamespace[T any](nameOrPath string, block func() (T, error)) (T, error) {
if nameOrPath == "" {
return block()
}
type blockResult struct {
value T
err error
}
resultChannel := make(chan blockResult, 1)
go func() {
runtime.LockOSThread()
value, err := execInNetworkNamespaceThread(nameOrPath, block)
resultChannel <- blockResult{value, err}
}()
result := <-resultChannel
return result.value, result.err
}
func execInNetworkNamespaceThread[T any](nameOrPath string, block func() (T, error)) (T, error) {
var defaultValue T
var path string
if strings.HasPrefix(nameOrPath, "/") {
path = nameOrPath
} else {
path = "/run/netns/" + nameOrPath
}
targetFd, err := unix.Open(path, unix.O_RDONLY|unix.O_CLOEXEC, 0)
if err != nil {
return defaultValue, E.Cause(err, "open netns ", nameOrPath)
}
defer unix.Close(targetFd)
err = unix.Setns(targetFd, unix.CLONE_NEWNET)
if err != nil {
return defaultValue, E.Cause(err, "set netns to ", nameOrPath)
}
return block()
}
func runInNetworkNamespace(nameOrPath string, block func() error) error {
_, err := execInNetworkNamespace(nameOrPath, func() (struct{}, error) {
return struct{}{}, block()
})
return err
}