|
|
|
|
@ -110,45 +110,42 @@ func control(network, address string, c syscall.RawConn) error { |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if ipRuleAvailable() { |
|
|
|
|
var controlErr error |
|
|
|
|
err := c.Control(func(fd uintptr) { |
|
|
|
|
controlErr = unix.SetsockoptInt(int(fd), |
|
|
|
|
unix.SOL_SOCKET, unix.SO_MARK, |
|
|
|
|
tailscaleBypassMark) |
|
|
|
|
}) |
|
|
|
|
if (err != nil || controlErr != nil) && ignoreErrors() { |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
if err != nil { |
|
|
|
|
return fmt.Errorf("setting socket mark1: %w", err) |
|
|
|
|
} |
|
|
|
|
if controlErr != nil { |
|
|
|
|
return fmt.Errorf("setting socket mark2: %w", controlErr) |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
var controlErr error |
|
|
|
|
err := c.Control(func(fd uintptr) { |
|
|
|
|
ifc, err := defaultRouteInterface() |
|
|
|
|
if err != nil { |
|
|
|
|
// Make sure we bind to *some* interface,
|
|
|
|
|
// or we could get a routing loop.
|
|
|
|
|
// "lo" is always wrong, but if we don't have
|
|
|
|
|
// a default route anyway, it doesn't matter.
|
|
|
|
|
ifc = "lo" |
|
|
|
|
} |
|
|
|
|
controlErr = unix.SetsockoptString(int(fd), |
|
|
|
|
unix.SOL_SOCKET, unix.SO_BINDTODEVICE, ifc) |
|
|
|
|
}) |
|
|
|
|
if (err != nil || controlErr != nil) && ignoreErrors() { |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
if err != nil { |
|
|
|
|
return fmt.Errorf("setting SO_BINDTODEVICE 1: %w", err) |
|
|
|
|
} |
|
|
|
|
if controlErr != nil { |
|
|
|
|
return fmt.Errorf("setting SO_BINDTODEVICE 2: %w", controlErr) |
|
|
|
|
var sockErr error |
|
|
|
|
err := c.Control(func(fd uintptr) { |
|
|
|
|
if ipRuleAvailable() { |
|
|
|
|
sockErr = setBypassMark(fd) |
|
|
|
|
} else { |
|
|
|
|
sockErr = bindToDevice(fd) |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
if err != nil { |
|
|
|
|
return fmt.Errorf("RawConn.Control on %T: %w", c, err) |
|
|
|
|
} |
|
|
|
|
if sockErr != nil && ignoreErrors() { |
|
|
|
|
// TODO(bradfitz): maybe log once? probably too spammy for e.g. CLI tools like tailscale netcheck.
|
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
return sockErr |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func setBypassMark(fd uintptr) error { |
|
|
|
|
if err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_MARK, tailscaleBypassMark); err != nil { |
|
|
|
|
return fmt.Errorf("setting SO_MARK bypass: %w", err) |
|
|
|
|
} |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func bindToDevice(fd uintptr) error { |
|
|
|
|
ifc, err := defaultRouteInterface() |
|
|
|
|
if err != nil { |
|
|
|
|
// Make sure we bind to *some* interface,
|
|
|
|
|
// or we could get a routing loop.
|
|
|
|
|
// "lo" is always wrong, but if we don't have
|
|
|
|
|
// a default route anyway, it doesn't matter.
|
|
|
|
|
ifc = "lo" |
|
|
|
|
} |
|
|
|
|
if err := unix.SetsockoptString(int(fd), unix.SOL_SOCKET, unix.SO_BINDTODEVICE, ifc); err != nil { |
|
|
|
|
return fmt.Errorf("setting SO_BINDTODEVICE: %w", err) |
|
|
|
|
} |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
|