|
|
|
@ -9,6 +9,7 @@ import (
|
|
|
|
"net"
|
|
|
|
"net"
|
|
|
|
"net/http"
|
|
|
|
"net/http"
|
|
|
|
"net/netip"
|
|
|
|
"net/netip"
|
|
|
|
|
|
|
|
"path"
|
|
|
|
"runtime"
|
|
|
|
"runtime"
|
|
|
|
"slices"
|
|
|
|
"slices"
|
|
|
|
"sort"
|
|
|
|
"sort"
|
|
|
|
@ -28,6 +29,18 @@ import (
|
|
|
|
// same interface and subnet.
|
|
|
|
// same interface and subnet.
|
|
|
|
var forceAllIPv6Endpoints = envknob.RegisterBool("TS_DEBUG_FORCE_ALL_IPV6_ENDPOINTS")
|
|
|
|
var forceAllIPv6Endpoints = envknob.RegisterBool("TS_DEBUG_FORCE_ALL_IPV6_ENDPOINTS")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// avoidInterfaces is a debug/power-user knob to exclude specific interfaces from consideration
|
|
|
|
|
|
|
|
// when gathering endpoints.
|
|
|
|
|
|
|
|
var avoidInterfaces = envknob.RegisterString("TS_AVOID_INTERFACES")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Likewise, onlyInterfaces can be set to specify the _only_ interfaces Tailscale is allowed to
|
|
|
|
|
|
|
|
// consider when gathering endpoints.
|
|
|
|
|
|
|
|
var onlyInterfaces = envknob.RegisterString("TS_ONLY_INTERFACES")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// In the same vein, avoidPrefix is a debug/power-user knob to exclude any addresses within specific
|
|
|
|
|
|
|
|
// prefixes from being used as endpoints. This is a more granular version of $TS_AVOID_INTERFACES.
|
|
|
|
|
|
|
|
var avoidPrefix = envknob.RegisterString("TS_AVOID_PREFIX")
|
|
|
|
|
|
|
|
|
|
|
|
// LoginEndpointForProxyDetermination is the URL used for testing
|
|
|
|
// LoginEndpointForProxyDetermination is the URL used for testing
|
|
|
|
// which HTTP proxy the system should use.
|
|
|
|
// which HTTP proxy the system should use.
|
|
|
|
var LoginEndpointForProxyDetermination = "https://controlplane.tailscale.com/"
|
|
|
|
var LoginEndpointForProxyDetermination = "https://controlplane.tailscale.com/"
|
|
|
|
@ -35,6 +48,39 @@ var LoginEndpointForProxyDetermination = "https://controlplane.tailscale.com/"
|
|
|
|
func isUp(nif *net.Interface) bool { return nif.Flags&net.FlagUp != 0 }
|
|
|
|
func isUp(nif *net.Interface) bool { return nif.Flags&net.FlagUp != 0 }
|
|
|
|
func isLoopback(nif *net.Interface) bool { return nif.Flags&net.FlagLoopback != 0 }
|
|
|
|
func isLoopback(nif *net.Interface) bool { return nif.Flags&net.FlagLoopback != 0 }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func matchInterfaceName(name string, patterns string) bool {
|
|
|
|
|
|
|
|
patternsList := strings.Split(patterns, ",")
|
|
|
|
|
|
|
|
for _, pattern := range patternsList {
|
|
|
|
|
|
|
|
pattern = strings.TrimSpace(pattern)
|
|
|
|
|
|
|
|
if pattern == "" {
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if matched, _ := path.Match(pattern, name); matched {
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func matchIP(ip netip.Addr, prefixes string) bool {
|
|
|
|
|
|
|
|
prefixList := strings.Split(prefixes, ",")
|
|
|
|
|
|
|
|
for _, arg := range prefixList {
|
|
|
|
|
|
|
|
arg = strings.TrimSpace(arg)
|
|
|
|
|
|
|
|
if arg == "" {
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
prefix, err := netip.ParsePrefix(arg)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if prefix.Contains(ip) {
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func isProblematicInterface(nif *net.Interface) bool {
|
|
|
|
func isProblematicInterface(nif *net.Interface) bool {
|
|
|
|
name := nif.Name
|
|
|
|
name := nif.Name
|
|
|
|
// Don't try to send disco/etc packets over zerotier; they effectively
|
|
|
|
// Don't try to send disco/etc packets over zerotier; they effectively
|
|
|
|
@ -47,6 +93,24 @@ func isProblematicInterface(nif *net.Interface) bool {
|
|
|
|
return false
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func isAllowedInterface(nif *net.Interface) bool {
|
|
|
|
|
|
|
|
name := nif.Name
|
|
|
|
|
|
|
|
if onlyInterfaces() != "" && !matchInterfaceName(name, onlyInterfaces()) {
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if avoidInterfaces() != "" && matchInterfaceName(name, avoidInterfaces()) {
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func isAllowedAddress(ip netip.Addr) bool {
|
|
|
|
|
|
|
|
if avoidPrefix() != "" && matchIP(ip, avoidPrefix()) {
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// LocalAddresses returns the machine's IP addresses, separated by
|
|
|
|
// LocalAddresses returns the machine's IP addresses, separated by
|
|
|
|
// whether they're loopback addresses. If there are no regular addresses
|
|
|
|
// whether they're loopback addresses. If there are no regular addresses
|
|
|
|
// it will return any IPv4 linklocal or IPv6 unique local addresses because we
|
|
|
|
// it will return any IPv4 linklocal or IPv6 unique local addresses because we
|
|
|
|
@ -66,6 +130,10 @@ func LocalAddresses() (regular, loopback []netip.Addr, err error) {
|
|
|
|
// send Tailscale traffic over.
|
|
|
|
// send Tailscale traffic over.
|
|
|
|
continue
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if !isAllowedInterface(stdIf) {
|
|
|
|
|
|
|
|
// Skip interfaces that the user does not want to use with Tailscale.
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
}
|
|
|
|
ifcIsLoopback := isLoopback(stdIf)
|
|
|
|
ifcIsLoopback := isLoopback(stdIf)
|
|
|
|
|
|
|
|
|
|
|
|
addrs, err := iface.Addrs()
|
|
|
|
addrs, err := iface.Addrs()
|
|
|
|
@ -81,6 +149,10 @@ func LocalAddresses() (regular, loopback []netip.Addr, err error) {
|
|
|
|
continue
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ip = ip.Unmap()
|
|
|
|
ip = ip.Unmap()
|
|
|
|
|
|
|
|
if !isAllowedAddress(ip) {
|
|
|
|
|
|
|
|
// Skip addresses that the user does not want to use with Tailscale.
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
}
|
|
|
|
// TODO(apenwarr): don't special case cgNAT.
|
|
|
|
// TODO(apenwarr): don't special case cgNAT.
|
|
|
|
// In the general wireguard case, it might
|
|
|
|
// In the general wireguard case, it might
|
|
|
|
// very well be something we can route to
|
|
|
|
// very well be something we can route to
|
|
|
|
|