|
|
@ -28,12 +28,20 @@ import (
|
|
|
|
// wgengine/router/router_linux.go.
|
|
|
|
// wgengine/router/router_linux.go.
|
|
|
|
const tailscaleBypassMark = 0x20000
|
|
|
|
const tailscaleBypassMark = 0x20000
|
|
|
|
|
|
|
|
|
|
|
|
// checkIPRule runs the ipRuleAvailable check exactly once.
|
|
|
|
// ipRuleOnce is the sync.Once & cached value for ipRuleAvailable.
|
|
|
|
var checkIPRule sync.Once
|
|
|
|
var ipRuleOnce struct {
|
|
|
|
|
|
|
|
sync.Once
|
|
|
|
|
|
|
|
v bool
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ipRuleAvailable is true if and only if the 'ip rule' command works.
|
|
|
|
// ipRuleAvailable reports whether the 'ip rule' command works.
|
|
|
|
// If it doesn't, we have to use SO_BINDTODEVICE on our sockets instead.
|
|
|
|
// If it doesn't, we have to use SO_BINDTODEVICE on our sockets instead.
|
|
|
|
var ipRuleAvailable bool
|
|
|
|
func ipRuleAvailable() bool {
|
|
|
|
|
|
|
|
ipRuleOnce.Do(func() {
|
|
|
|
|
|
|
|
ipRuleOnce.v = exec.Command("ip", "rule").Run() == nil
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
return ipRuleOnce.v
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// defaultRouteInterface returns the name of the network interface that owns
|
|
|
|
// defaultRouteInterface returns the name of the network interface that owns
|
|
|
|
// the default route, not including any tailscale interfaces. We only use
|
|
|
|
// the default route, not including any tailscale interfaces. We only use
|
|
|
@ -98,18 +106,13 @@ func ignoreErrors() bool {
|
|
|
|
// It's intentionally the same signature as net.Dialer.Control
|
|
|
|
// It's intentionally the same signature as net.Dialer.Control
|
|
|
|
// and net.ListenConfig.Control.
|
|
|
|
// and net.ListenConfig.Control.
|
|
|
|
func control(network, address string, c syscall.RawConn) error {
|
|
|
|
func control(network, address string, c syscall.RawConn) error {
|
|
|
|
checkIPRule.Do(func() {
|
|
|
|
|
|
|
|
_, err := exec.Command("ip", "rule").Output()
|
|
|
|
|
|
|
|
ipRuleAvailable = (err == nil)
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if skipPrivileged.Get() {
|
|
|
|
if skipPrivileged.Get() {
|
|
|
|
// We can't set socket marks without CAP_NET_ADMIN on linux,
|
|
|
|
// We can't set socket marks without CAP_NET_ADMIN on linux,
|
|
|
|
// skip as requested.
|
|
|
|
// skip as requested.
|
|
|
|
return nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ipRuleAvailable {
|
|
|
|
if ipRuleAvailable() {
|
|
|
|
var controlErr error
|
|
|
|
var controlErr error
|
|
|
|
err := c.Control(func(fd uintptr) {
|
|
|
|
err := c.Control(func(fd uintptr) {
|
|
|
|
controlErr = unix.SetsockoptInt(int(fd),
|
|
|
|
controlErr = unix.SetsockoptInt(int(fd),
|
|
|
|