This commit is contained in:
bluepython508 committed 2026-10-22 12:27:19 +01:00
1 parent 3530afb854
commit 6903fcff27
2146 files changed
+112256 -190703

No files matched your search

+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) Tailscale Inc & AUTHORS
// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause
package netns
+13 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) Tailscale Inc & AUTHORS
// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause
// Package netns contains the common code for using the Go net package
@@ -46,6 +46,18 @@ func SetBindToInterfaceByRoute(logf logger.Logf, v bool) {
}
}
// When true, disableAndroidBindToActiveNetwork skips binding sockets to the currently
// active network on Android.
var disableAndroidBindToActiveNetwork atomic.Bool
// SetDisableAndroidBindToActiveNetwork disables the default behavior of binding
// sockets to the currently active network on Android.
func SetDisableAndroidBindToActiveNetwork(logf logger.Logf, v bool) {
if runtime.GOOS == "android" && disableAndroidBindToActiveNetwork.Swap(v) != v {
logf("netns: disableAndroidBindToActiveNetwork changed to %v", v)
}
}
var disableBindConnToInterface atomic.Bool
// SetDisableBindConnToInterface disables the (normal) behavior of binding
+37 -4
View File
@@ -1,4 +1,4 @@
// Copyright (c) Tailscale Inc & AUTHORS
// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause
//go:build android
@@ -17,6 +17,9 @@ import (
var (
androidProtectFuncMu sync.Mutex
androidProtectFunc func(fd int) error
androidBindToNetworkFuncMu sync.Mutex
androidBindToNetworkFunc func(fd int) error
)
// UseSocketMark reports whether SO_MARK is in use. Android does not use SO_MARK.
@@ -50,6 +53,14 @@ func SetAndroidProtectFunc(f func(fd int) error) {
androidProtectFunc = f
}
// SetAndroidBindToNetworkFunc registers a func provided by Android that binds
// the socket FD to the currently selected underlying network.
func SetAndroidBindToNetworkFunc(f func(fd int) error) {
androidBindToNetworkFuncMu.Lock()
defer androidBindToNetworkFuncMu.Unlock()
androidBindToNetworkFunc = f
}
func control(logger.Logf, *netmon.Monitor) func(network, address string, c syscall.RawConn) error {
return controlC
}
@@ -60,14 +71,36 @@ func control(logger.Logf, *netmon.Monitor) func(network, address string, c sysca
// and net.ListenConfig.Control.
func controlC(network, address string, c syscall.RawConn) error {
var sockErr error
err := c.Control(func(fd uintptr) {
fdInt := int(fd)
// Protect from VPN loops
androidProtectFuncMu.Lock()
f := androidProtectFunc
pf := androidProtectFunc
androidProtectFuncMu.Unlock()
if f != nil {
sockErr = f(int(fd))
if pf != nil {
if err := pf(fdInt); err != nil {
sockErr = err
return
}
}
if disableAndroidBindToActiveNetwork.Load() {
return
}
androidBindToNetworkFuncMu.Lock()
bf := androidBindToNetworkFunc
androidBindToNetworkFuncMu.Unlock()
if bf != nil {
if err := bf(fdInt); err != nil {
sockErr = err
return
}
}
})
if err != nil {
return fmt.Errorf("RawConn.Control on %T: %w", c, err)
}
+2 -2
View File
@@ -1,4 +1,4 @@
// Copyright (c) Tailscale Inc & AUTHORS
// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause
//go:build darwin
@@ -85,7 +85,7 @@ func getInterfaceIndex(logf logger.Logf, netMon *netmon.Monitor, address string)
// Preferentially choose the OS's view of the default if index. Due to the way darwin sets the delegated
// interface on tunnel creation only, it is possible for netmon to have a stale view of the default and
// netmon's view is often temporarily wrong during network transitions, or for us to not have the
// the the oses view of the defaultIf yet.
// the oses view of the defaultIf yet.
if osIferr == nil {
idx = osIf.InterfaceIndex
errOut = nil
+2 -2
View File
@@ -1,7 +1,7 @@
// Copyright (c) Tailscale Inc & AUTHORS
// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause
//go:build !linux && !windows && !darwin
//go:build !linux && !windows && !darwin && !openbsd
package netns
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) Tailscale Inc & AUTHORS
// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause
//go:build darwin || windows
+35 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) Tailscale Inc & AUTHORS
// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause
//go:build linux && !android
@@ -6,7 +6,9 @@
package netns
import (
"errors"
"fmt"
"log"
"net"
"os"
"sync"
@@ -131,3 +133,35 @@ func bindToDevice(fd uintptr) error {
}
return nil
}
// SetListenConfigInterfaceName sets lc.Control so that sockets created
// with it are bound to the named interface using SO_BINDTODEVICE, and
// thus only receive packets that arrived on that interface. It is the
// Linux counterpart of darwin's SetListenConfigInterfaceIndex.
//
// Failures to set the socket option are logged and otherwise ignored:
// the socket then simply listens as it would have without this call.
// Setting SO_BINDTODEVICE requires CAP_NET_RAW, and hardening must not
// take out the caller's listener on systems where that is unavailable.
func SetListenConfigInterfaceName(lc *net.ListenConfig, ifName string) error {
if lc == nil {
return errors.New("nil ListenConfig")
}
if lc.Control != nil {
return errors.New("ListenConfig.Control already set")
}
lc.Control = func(network, address string, c syscall.RawConn) error {
var sockErr error
err := c.Control(func(fd uintptr) {
sockErr = unix.SetsockoptString(int(fd), unix.SOL_SOCKET, unix.SO_BINDTODEVICE, ifName)
})
if sockErr != nil {
log.Printf("[unexpected] netns: SO_BINDTODEVICE(%q) on %v %v: %v", ifName, network, address, sockErr)
}
if err != nil {
return fmt.Errorf("RawConn.Control on %T: %w", c, err)
}
return nil
}
return nil
}
+178
View File
@@ -0,0 +1,178 @@
// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause
//go:build openbsd
package netns
import (
"fmt"
"os/exec"
"strconv"
"strings"
"sync"
"syscall"
"golang.org/x/sys/unix"
"tailscale.com/net/netmon"
"tailscale.com/types/logger"
)
var (
bypassMu sync.Mutex
bypassRtable int
)
// Called by the router when exit node routes are configured.
func SetBypassRtable(rtable int) {
bypassMu.Lock()
defer bypassMu.Unlock()
bypassRtable = rtable
}
func GetBypassRtable() int {
bypassMu.Lock()
defer bypassMu.Unlock()
return bypassRtable
}
func control(logf logger.Logf, _ *netmon.Monitor) func(network, address string, c syscall.RawConn) error {
return func(network, address string, c syscall.RawConn) error {
return controlC(logf, network, address, c)
}
}
func controlC(logf logger.Logf, _, address string, c syscall.RawConn) error {
if isLocalhost(address) {
return nil
}
rtable := GetBypassRtable()
if rtable == 0 {
return nil
}
return bindToRtable(c, rtable, logf)
}
func bindToRtable(c syscall.RawConn, rtable int, logf logger.Logf) error {
var sockErr error
err := c.Control(func(fd uintptr) {
sockErr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_RTABLE, rtable)
})
if sockErr != nil {
logf("netns: SO_RTABLE(%d): %v", rtable, sockErr)
}
if err != nil {
return fmt.Errorf("RawConn.Control: %w", err)
}
return sockErr
}
// SetupBypassRtable creates a bypass rtable with the existing default route
// in it routing through its existing physical interface. It should be called
// by the router when exit node routes are being added.
// Returns the rtable number.
func SetupBypassRtable(logf logger.Logf) (int, error) {
bypassMu.Lock()
defer bypassMu.Unlock()
if bypassRtable != 0 {
return bypassRtable, nil
}
gw, err := getPhysicalGateway()
if err != nil {
return 0, fmt.Errorf("getPhysicalGateway: %w", err)
}
rtable, err := findAvailableRtable()
if err != nil {
return 0, fmt.Errorf("findAvailableRtable: %w", err)
}
// Add the existing default route interface to the new bypass rtable
out, err := exec.Command("route", "-T", strconv.Itoa(rtable), "-qn", "add", "default", gw).CombinedOutput()
if err != nil {
return 0, fmt.Errorf("route -T%d add default %s: %w\n%s", rtable, gw, err, out)
}
bypassRtable = rtable
logf("netns: created bypass rtable %d with default route via %s", rtable, gw)
return rtable, nil
}
func CleanupBypassRtable(logf logger.Logf) {
bypassMu.Lock()
defer bypassMu.Unlock()
if bypassRtable == 0 {
return
}
// Delete the default route from the bypass rtable which should clear it
out, err := exec.Command("route", "-T", strconv.Itoa(bypassRtable), "-qn", "delete", "default").CombinedOutput()
if err != nil {
logf("netns: failed to clear bypass route: %v\n%s", err, out)
} else {
logf("netns: cleared bypass rtable %d", bypassRtable)
}
bypassRtable = 0
}
// getPhysicalGateway returns the default gateway IP that goes through a
// physical interface (not tun).
func getPhysicalGateway() (string, error) {
out, err := exec.Command("route", "-n", "show", "-inet").CombinedOutput()
if err != nil {
return "", fmt.Errorf("route show: %w", err)
}
// Parse the routing table looking for default routes not via tun
for _, line := range strings.Split(string(out), "\n") {
fields := strings.Fields(line)
if len(fields) < 8 {
continue
}
// Format: Destination Gateway Flags Refs Use Mtu Prio Iface
dest := fields[0]
gateway := fields[1]
iface := fields[7]
if dest == "default" && !strings.HasPrefix(iface, "tun") {
return gateway, nil
}
}
return "", fmt.Errorf("no physical default gateway found")
}
func findAvailableRtable() (int, error) {
for i := 1; i <= 255; i++ {
out, err := exec.Command("route", "-T", strconv.Itoa(i), "-n", "show", "-inet").CombinedOutput()
if err != nil {
// rtable doesn't exist, consider it available
return i, nil
}
// Check if the output only contains the header (no actual routes)
lines := strings.Split(strings.TrimSpace(string(out)), "\n")
hasRoutes := false
for _, line := range lines {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "Routing") || strings.HasPrefix(line, "Destination") {
continue
}
hasRoutes = true
break
}
if !hasRoutes {
return i, nil
}
}
return 0, fmt.Errorf("no available rtable")
}
func UseSocketMark() bool {
return false
}
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) Tailscale Inc & AUTHORS
// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause
package netns
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) Tailscale Inc & AUTHORS
// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause
//go:build !ios && !js && !android && !ts_omit_useproxy