73 lines
1.8 KiB
Go
73 lines
1.8 KiB
Go
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
|
|
}
|