mirror of
https://github.com/tailscale/tailscale.git
synced 2025-12-01 17:49:02 +00:00
all: avoid repeated default interface lookups
On some platforms (notably macOS and iOS) we look up the default interface to bind outgoing connections to. This is both duplicated work and results in logspam when the default interface is not available (i.e. when a phone has no connectivity, we log an error and thus cause more things that we will try to upload and fail). Fixed by passing around a netmon.Monitor to more places, so that we can use its cached interface state. Fixes #7850 Updates #7621 Signed-off-by: Mihai Parparita <mihai@tailscale.com>
This commit is contained in:
committed by
Mihai Parparita
parent
7f17e04a5a
commit
7330aa593e
@@ -64,6 +64,7 @@ type Manager struct {
|
||||
}
|
||||
|
||||
// NewManagers created a new manager from the given config.
|
||||
// The netMon parameter is optional; if non-nil it's used to do faster interface lookups.
|
||||
func NewManager(logf logger.Logf, oscfg OSConfigurator, netMon *netmon.Monitor, dialer *tsdial.Dialer, linkSel resolver.ForwardLinkSelector) *Manager {
|
||||
if dialer == nil {
|
||||
panic("nil Dialer")
|
||||
|
||||
@@ -380,11 +380,12 @@ func (f *forwarder) getKnownDoHClientForProvider(urlBase string) (c *http.Client
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
nsDialer := netns.NewDialer(f.logf)
|
||||
nsDialer := netns.NewDialer(f.logf, f.netMon)
|
||||
dialer := dnscache.Dialer(nsDialer.DialContext, &dnscache.Resolver{
|
||||
SingleHost: dohURL.Hostname(),
|
||||
SingleHostStaticResult: allIPs,
|
||||
Logf: f.logf,
|
||||
NetMon: f.netMon,
|
||||
})
|
||||
c = &http.Client{
|
||||
Transport: &http.Transport{
|
||||
|
||||
@@ -17,8 +17,8 @@ func init() {
|
||||
initListenConfig = initListenConfigNetworkExtension
|
||||
}
|
||||
|
||||
func initListenConfigNetworkExtension(nc *net.ListenConfig, mon *netmon.Monitor, tunName string) error {
|
||||
nif, ok := mon.InterfaceState().Interface[tunName]
|
||||
func initListenConfigNetworkExtension(nc *net.ListenConfig, netMon *netmon.Monitor, tunName string) error {
|
||||
nif, ok := netMon.InterfaceState().Interface[tunName]
|
||||
if !ok {
|
||||
return errors.New("utun not found")
|
||||
}
|
||||
|
||||
@@ -997,7 +997,7 @@ func TestMarshalResponseFormatError(t *testing.T) {
|
||||
|
||||
func TestForwardLinkSelection(t *testing.T) {
|
||||
configCall := make(chan string, 1)
|
||||
tstest.Replace(t, &initListenConfig, func(nc *net.ListenConfig, mon *netmon.Monitor, tunName string) error {
|
||||
tstest.Replace(t, &initListenConfig, func(nc *net.ListenConfig, netMon *netmon.Monitor, tunName string) error {
|
||||
select {
|
||||
case configCall <- tunName:
|
||||
return nil
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"time"
|
||||
|
||||
"tailscale.com/envknob"
|
||||
"tailscale.com/net/netmon"
|
||||
"tailscale.com/types/logger"
|
||||
"tailscale.com/util/cloudenv"
|
||||
"tailscale.com/util/singleflight"
|
||||
@@ -91,6 +92,11 @@ type Resolver struct {
|
||||
// be added to all log messages printed with this logger.
|
||||
Logf logger.Logf
|
||||
|
||||
// NetMon optionally provides a netmon.Monitor to use to get the current
|
||||
// (cached) network interface.
|
||||
// If nil, the interface will be looked up dynamically.
|
||||
NetMon *netmon.Monitor
|
||||
|
||||
sf singleflight.Group[string, ipRes]
|
||||
|
||||
mu sync.Mutex
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"time"
|
||||
|
||||
"tailscale.com/atomicfile"
|
||||
"tailscale.com/net/netmon"
|
||||
"tailscale.com/net/netns"
|
||||
"tailscale.com/net/tlsdial"
|
||||
"tailscale.com/net/tshttpproxy"
|
||||
@@ -31,13 +32,16 @@ import (
|
||||
"tailscale.com/util/slicesx"
|
||||
)
|
||||
|
||||
func Lookup(logf logger.Logf) func(ctx context.Context, host string) ([]netip.Addr, error) {
|
||||
// MakeLookupFunc creates a function that can be used to resolve hostnames
|
||||
// (e.g. as a LookupIPFallback from dnscache.Resolver).
|
||||
// The netMon parameter is optional; if non-nil it's used to do faster interface lookups.
|
||||
func MakeLookupFunc(logf logger.Logf, netMon *netmon.Monitor) func(ctx context.Context, host string) ([]netip.Addr, error) {
|
||||
return func(ctx context.Context, host string) ([]netip.Addr, error) {
|
||||
return lookup(ctx, host, logf)
|
||||
return lookup(ctx, host, logf, netMon)
|
||||
}
|
||||
}
|
||||
|
||||
func lookup(ctx context.Context, host string, logf logger.Logf) ([]netip.Addr, error) {
|
||||
func lookup(ctx context.Context, host string, logf logger.Logf, netMon *netmon.Monitor) ([]netip.Addr, error) {
|
||||
if ip, err := netip.ParseAddr(host); err == nil && ip.IsValid() {
|
||||
return []netip.Addr{ip}, nil
|
||||
}
|
||||
@@ -85,7 +89,7 @@ func lookup(ctx context.Context, host string, logf logger.Logf) ([]netip.Addr, e
|
||||
logf("trying bootstrapDNS(%q, %q) for %q ...", cand.dnsName, cand.ip, host)
|
||||
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
|
||||
defer cancel()
|
||||
dm, err := bootstrapDNSMap(ctx, cand.dnsName, cand.ip, host, logf)
|
||||
dm, err := bootstrapDNSMap(ctx, cand.dnsName, cand.ip, host, logf, netMon)
|
||||
if err != nil {
|
||||
logf("bootstrapDNS(%q, %q) for %q error: %v", cand.dnsName, cand.ip, host, err)
|
||||
continue
|
||||
@@ -104,8 +108,8 @@ func lookup(ctx context.Context, host string, logf logger.Logf) ([]netip.Addr, e
|
||||
|
||||
// serverName and serverIP of are, say, "derpN.tailscale.com".
|
||||
// queryName is the name being sought (e.g. "controlplane.tailscale.com"), passed as hint.
|
||||
func bootstrapDNSMap(ctx context.Context, serverName string, serverIP netip.Addr, queryName string, logf logger.Logf) (dnsMap, error) {
|
||||
dialer := netns.NewDialer(logf)
|
||||
func bootstrapDNSMap(ctx context.Context, serverName string, serverIP netip.Addr, queryName string, logf logger.Logf, netMon *netmon.Monitor) (dnsMap, error) {
|
||||
dialer := netns.NewDialer(logf, netMon)
|
||||
tr := http.DefaultTransport.(*http.Transport).Clone()
|
||||
tr.Proxy = tshttpproxy.ProxyFromEnvironment
|
||||
tr.DialContext = func(ctx context.Context, netw, addr string) (net.Conn, error) {
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
"tailscale.com/net/interfaces"
|
||||
"tailscale.com/net/netaddr"
|
||||
"tailscale.com/net/neterror"
|
||||
"tailscale.com/net/netmon"
|
||||
"tailscale.com/net/netns"
|
||||
"tailscale.com/net/ping"
|
||||
"tailscale.com/net/portmapper"
|
||||
@@ -158,6 +159,11 @@ type Client struct {
|
||||
// If nil, log.Printf is used.
|
||||
Logf logger.Logf
|
||||
|
||||
// NetMon optionally provides a netmon.Monitor to use to get the current
|
||||
// (cached) network interface.
|
||||
// If nil, the interface will be looked up dynamically.
|
||||
NetMon *netmon.Monitor
|
||||
|
||||
// TimeNow, if non-nil, is used instead of time.Now.
|
||||
TimeNow func() time.Time
|
||||
|
||||
@@ -864,22 +870,29 @@ func (c *Client) GetReport(ctx context.Context, dm *tailcfg.DERPMap) (_ *Report,
|
||||
return c.finishAndStoreReport(rs, dm), nil
|
||||
}
|
||||
|
||||
ifState, err := interfaces.GetState()
|
||||
if err != nil {
|
||||
c.logf("[v1] interfaces: %v", err)
|
||||
return nil, err
|
||||
var ifState *interfaces.State
|
||||
if c.NetMon == nil {
|
||||
directState, err := interfaces.GetState()
|
||||
if err != nil {
|
||||
c.logf("[v1] interfaces: %v", err)
|
||||
return nil, err
|
||||
} else {
|
||||
ifState = directState
|
||||
}
|
||||
} else {
|
||||
ifState = c.NetMon.InterfaceState()
|
||||
}
|
||||
|
||||
// See if IPv6 works at all, or if it's been hard disabled at the
|
||||
// OS level.
|
||||
v6udp, err := nettype.MakePacketListenerWithNetIP(netns.Listener(c.logf)).ListenPacket(ctx, "udp6", "[::1]:0")
|
||||
v6udp, err := nettype.MakePacketListenerWithNetIP(netns.Listener(c.logf, c.NetMon)).ListenPacket(ctx, "udp6", "[::1]:0")
|
||||
if err == nil {
|
||||
rs.report.OSHasIPv6 = true
|
||||
v6udp.Close()
|
||||
}
|
||||
|
||||
// Create a UDP4 socket used for sending to our discovered IPv4 address.
|
||||
rs.pc4Hair, err = nettype.MakePacketListenerWithNetIP(netns.Listener(c.logf)).ListenPacket(ctx, "udp4", ":0")
|
||||
rs.pc4Hair, err = nettype.MakePacketListenerWithNetIP(netns.Listener(c.logf, c.NetMon)).ListenPacket(ctx, "udp4", ":0")
|
||||
if err != nil {
|
||||
c.logf("udp4: %v", err)
|
||||
return nil, err
|
||||
@@ -909,7 +922,7 @@ func (c *Client) GetReport(ctx context.Context, dm *tailcfg.DERPMap) (_ *Report,
|
||||
if f := c.GetSTUNConn4; f != nil {
|
||||
rs.pc4 = f()
|
||||
} else {
|
||||
u4, err := nettype.MakePacketListenerWithNetIP(netns.Listener(c.logf)).ListenPacket(ctx, "udp4", c.udpBindAddr())
|
||||
u4, err := nettype.MakePacketListenerWithNetIP(netns.Listener(c.logf, nil)).ListenPacket(ctx, "udp4", c.udpBindAddr())
|
||||
if err != nil {
|
||||
c.logf("udp4: %v", err)
|
||||
return nil, err
|
||||
@@ -922,7 +935,7 @@ func (c *Client) GetReport(ctx context.Context, dm *tailcfg.DERPMap) (_ *Report,
|
||||
if f := c.GetSTUNConn6; f != nil {
|
||||
rs.pc6 = f()
|
||||
} else {
|
||||
u6, err := nettype.MakePacketListenerWithNetIP(netns.Listener(c.logf)).ListenPacket(ctx, "udp6", c.udpBindAddr())
|
||||
u6, err := nettype.MakePacketListenerWithNetIP(netns.Listener(c.logf, nil)).ListenPacket(ctx, "udp6", c.udpBindAddr())
|
||||
if err != nil {
|
||||
c.logf("udp6: %v", err)
|
||||
} else {
|
||||
@@ -1297,7 +1310,7 @@ func (c *Client) measureAllICMPLatency(ctx context.Context, rs *reportState, nee
|
||||
ctx, done := context.WithTimeout(ctx, icmpProbeTimeout)
|
||||
defer done()
|
||||
|
||||
p, err := ping.New(ctx, c.logf)
|
||||
p, err := ping.New(ctx, c.logf, c.NetMon)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1635,6 +1648,7 @@ func (c *Client) nodeAddr(ctx context.Context, n *tailcfg.DERPNode, proto probeP
|
||||
Forward: net.DefaultResolver,
|
||||
UseLastGood: true,
|
||||
Logf: c.logf,
|
||||
NetMon: c.NetMon,
|
||||
}
|
||||
}
|
||||
resolver := c.resolver
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"sync/atomic"
|
||||
|
||||
"tailscale.com/net/netknob"
|
||||
"tailscale.com/net/netmon"
|
||||
"tailscale.com/types/logger"
|
||||
)
|
||||
|
||||
@@ -55,19 +56,21 @@ func SetDisableBindConnToInterface(v bool) {
|
||||
// Listener returns a new net.Listener with its Control hook func
|
||||
// initialized as necessary to run in logical network namespace that
|
||||
// doesn't route back into Tailscale.
|
||||
func Listener(logf logger.Logf) *net.ListenConfig {
|
||||
// The netMon parameter is optional; if non-nil it's used to do faster interface lookups.
|
||||
func Listener(logf logger.Logf, netMon *netmon.Monitor) *net.ListenConfig {
|
||||
if disabled.Load() {
|
||||
return new(net.ListenConfig)
|
||||
}
|
||||
return &net.ListenConfig{Control: control(logf)}
|
||||
return &net.ListenConfig{Control: control(logf, netMon)}
|
||||
}
|
||||
|
||||
// NewDialer returns a new Dialer using a net.Dialer with its Control
|
||||
// hook func initialized as necessary to run in a logical network
|
||||
// namespace that doesn't route back into Tailscale. It also handles
|
||||
// using a SOCKS if configured in the environment with ALL_PROXY.
|
||||
func NewDialer(logf logger.Logf) Dialer {
|
||||
return FromDialer(logf, &net.Dialer{
|
||||
// The netMon parameter is optional; if non-nil it's used to do faster interface lookups.
|
||||
func NewDialer(logf logger.Logf, netMon *netmon.Monitor) Dialer {
|
||||
return FromDialer(logf, netMon, &net.Dialer{
|
||||
KeepAlive: netknob.PlatformTCPKeepAlive(),
|
||||
})
|
||||
}
|
||||
@@ -76,11 +79,12 @@ func NewDialer(logf logger.Logf) Dialer {
|
||||
// network namespace that doesn't route back into Tailscale. It also
|
||||
// handles using a SOCKS if configured in the environment with
|
||||
// ALL_PROXY.
|
||||
func FromDialer(logf logger.Logf, d *net.Dialer) Dialer {
|
||||
// The netMon parameter is optional; if non-nil it's used to do faster interface lookups.
|
||||
func FromDialer(logf logger.Logf, netMon *netmon.Monitor, d *net.Dialer) Dialer {
|
||||
if disabled.Load() {
|
||||
return d
|
||||
}
|
||||
d.Control = control(logf)
|
||||
d.Control = control(logf, netMon)
|
||||
if wrapDialer != nil {
|
||||
return wrapDialer(d)
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"tailscale.com/net/netmon"
|
||||
"tailscale.com/types/logger"
|
||||
)
|
||||
|
||||
@@ -49,7 +50,7 @@ func SetAndroidProtectFunc(f func(fd int) error) {
|
||||
androidProtectFunc = f
|
||||
}
|
||||
|
||||
func control(logger.Logf) func(network, address string, c syscall.RawConn) error {
|
||||
func control(logger.Logf, *netmon.Monitor) func(network, address string, c syscall.RawConn) error {
|
||||
return controlC
|
||||
}
|
||||
|
||||
|
||||
@@ -19,24 +19,25 @@ import (
|
||||
"golang.org/x/sys/unix"
|
||||
"tailscale.com/envknob"
|
||||
"tailscale.com/net/interfaces"
|
||||
"tailscale.com/net/netmon"
|
||||
"tailscale.com/types/logger"
|
||||
)
|
||||
|
||||
func control(logf logger.Logf) func(network, address string, c syscall.RawConn) error {
|
||||
func control(logf logger.Logf, netMon *netmon.Monitor) func(network, address string, c syscall.RawConn) error {
|
||||
return func(network, address string, c syscall.RawConn) error {
|
||||
return controlLogf(logf, network, address, c)
|
||||
return controlLogf(logf, netMon, network, address, c)
|
||||
}
|
||||
}
|
||||
|
||||
var bindToInterfaceByRouteEnv = envknob.RegisterBool("TS_BIND_TO_INTERFACE_BY_ROUTE")
|
||||
|
||||
var errInterfaceIndexInvalid = errors.New("interface index invalid")
|
||||
var errInterfaceStateInvalid = errors.New("interface state invalid")
|
||||
|
||||
// controlLogf marks c as necessary to dial in a separate network namespace.
|
||||
//
|
||||
// It's intentionally the same signature as net.Dialer.Control
|
||||
// and net.ListenConfig.Control.
|
||||
func controlLogf(logf logger.Logf, network, address string, c syscall.RawConn) error {
|
||||
func controlLogf(logf logger.Logf, netMon *netmon.Monitor, network, address string, c syscall.RawConn) error {
|
||||
if isLocalhost(address) {
|
||||
// Don't bind to an interface for localhost connections.
|
||||
return nil
|
||||
@@ -47,7 +48,7 @@ func controlLogf(logf logger.Logf, network, address string, c syscall.RawConn) e
|
||||
return nil
|
||||
}
|
||||
|
||||
idx, err := getInterfaceIndex(logf, address)
|
||||
idx, err := getInterfaceIndex(logf, netMon, address)
|
||||
if err != nil {
|
||||
// callee logged
|
||||
return nil
|
||||
@@ -56,20 +57,31 @@ func controlLogf(logf logger.Logf, network, address string, c syscall.RawConn) e
|
||||
return bindConnToInterface(c, network, address, idx, logf)
|
||||
}
|
||||
|
||||
func getInterfaceIndex(logf logger.Logf, address string) (int, error) {
|
||||
func getInterfaceIndex(logf logger.Logf, netMon *netmon.Monitor, address string) (int, error) {
|
||||
// Helper so we can log errors.
|
||||
defaultIdx := func() (int, error) {
|
||||
idx, err := interfaces.DefaultRouteInterfaceIndex()
|
||||
if err != nil {
|
||||
// It's somewhat common for there to be no default gateway route
|
||||
// (e.g. on a phone with no connectivity), don't log those errors
|
||||
// since they are expected.
|
||||
if !errors.Is(err, interfaces.ErrNoGatewayIndexFound) {
|
||||
logf("[unexpected] netns: DefaultRouteInterfaceIndex: %v", err)
|
||||
if netMon == nil {
|
||||
idx, err := interfaces.DefaultRouteInterfaceIndex()
|
||||
if err != nil {
|
||||
// It's somewhat common for there to be no default gateway route
|
||||
// (e.g. on a phone with no connectivity), don't log those errors
|
||||
// since they are expected.
|
||||
if !errors.Is(err, interfaces.ErrNoGatewayIndexFound) {
|
||||
logf("[unexpected] netns: DefaultRouteInterfaceIndex: %v", err)
|
||||
}
|
||||
return -1, err
|
||||
}
|
||||
return -1, err
|
||||
return idx, nil
|
||||
}
|
||||
return idx, nil
|
||||
state := netMon.InterfaceState()
|
||||
if state == nil {
|
||||
return -1, errInterfaceStateInvalid
|
||||
}
|
||||
|
||||
if iface, ok := state.Interface[state.DefaultRouteInterface]; ok {
|
||||
return iface.Index, nil
|
||||
}
|
||||
return -1, errInterfaceStateInvalid
|
||||
}
|
||||
|
||||
useRoute := bindToInterfaceByRoute.Load() || bindToInterfaceByRouteEnv()
|
||||
|
||||
@@ -34,7 +34,7 @@ func TestGetInterfaceIndex(t *testing.T) {
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
idx, err := getInterfaceIndex(t.Logf, tc.addr)
|
||||
idx, err := getInterfaceIndex(t.Logf, nil, tc.addr)
|
||||
if err != nil {
|
||||
if tc.err == "" {
|
||||
t.Fatalf("got unexpected error: %v", err)
|
||||
@@ -68,7 +68,7 @@ func TestGetInterfaceIndex(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
idx, err := getInterfaceIndex(t.Logf, "100.100.100.100:53")
|
||||
idx, err := getInterfaceIndex(t.Logf, nil, "100.100.100.100:53")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -8,10 +8,11 @@ package netns
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"tailscale.com/net/netmon"
|
||||
"tailscale.com/types/logger"
|
||||
)
|
||||
|
||||
func control(logger.Logf) func(network, address string, c syscall.RawConn) error {
|
||||
func control(logger.Logf, *netmon.Monitor) func(network, address string, c syscall.RawConn) error {
|
||||
return controlC
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"golang.org/x/sys/unix"
|
||||
"tailscale.com/envknob"
|
||||
"tailscale.com/net/interfaces"
|
||||
"tailscale.com/net/netmon"
|
||||
"tailscale.com/types/logger"
|
||||
)
|
||||
|
||||
@@ -85,7 +86,7 @@ func ignoreErrors() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func control(logger.Logf) func(network, address string, c syscall.RawConn) error {
|
||||
func control(logger.Logf, *netmon.Monitor) func(network, address string, c syscall.RawConn) error {
|
||||
return controlC
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ func TestDial(t *testing.T) {
|
||||
if !*extNetwork {
|
||||
t.Skip("skipping test without --use-external-network")
|
||||
}
|
||||
d := NewDialer(t.Logf)
|
||||
d := NewDialer(t.Logf, nil)
|
||||
c, err := d.Dial("tcp", "google.com:80")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"golang.org/x/sys/windows"
|
||||
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
|
||||
"tailscale.com/net/interfaces"
|
||||
"tailscale.com/net/netmon"
|
||||
"tailscale.com/types/logger"
|
||||
)
|
||||
|
||||
@@ -26,7 +27,7 @@ func interfaceIndex(iface *winipcfg.IPAdapterAddresses) uint32 {
|
||||
return iface.IfIndex
|
||||
}
|
||||
|
||||
func control(logger.Logf) func(network, address string, c syscall.RawConn) error {
|
||||
func control(logger.Logf, *netmon.Monitor) func(network, address string, c syscall.RawConn) error {
|
||||
return controlC
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
|
||||
"golang.org/x/net/icmp"
|
||||
"golang.org/x/net/ipv4"
|
||||
"tailscale.com/net/netmon"
|
||||
"tailscale.com/net/netns"
|
||||
"tailscale.com/types/logger"
|
||||
)
|
||||
@@ -52,8 +53,9 @@ type Pinger struct {
|
||||
|
||||
// New creates a new Pinger. The Context provided will be used to create
|
||||
// network listeners, and to set an absolute deadline (if any) on the net.Conn
|
||||
func New(ctx context.Context, logf logger.Logf) (*Pinger, error) {
|
||||
p, err := newUnstarted(ctx, logf)
|
||||
// The netMon parameter is optional; if non-nil it's used to do faster interface lookups.
|
||||
func New(ctx context.Context, logf logger.Logf, netMon *netmon.Monitor) (*Pinger, error) {
|
||||
p, err := newUnstarted(ctx, logf, netMon)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -72,14 +74,14 @@ func New(ctx context.Context, logf logger.Logf) (*Pinger, error) {
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func newUnstarted(ctx context.Context, logf logger.Logf) (*Pinger, error) {
|
||||
func newUnstarted(ctx context.Context, logf logger.Logf, netMon *netmon.Monitor) (*Pinger, error) {
|
||||
var id [2]byte
|
||||
_, err := rand.Read(id[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conn, err := netns.Listener(logf).ListenPacket(ctx, "ip4:icmp", "0.0.0.0")
|
||||
conn, err := netns.Listener(logf, netMon).ListenPacket(ctx, "ip4:icmp", "0.0.0.0")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -249,7 +249,7 @@ func (d *TestIGD) handlePCPQuery(pkt []byte, src netip.AddrPort) {
|
||||
|
||||
func newTestClient(t *testing.T, igd *TestIGD) *Client {
|
||||
var c *Client
|
||||
c = NewClient(t.Logf, nil, func() {
|
||||
c = NewClient(t.Logf, nil, nil, func() {
|
||||
t.Logf("port map changed")
|
||||
t.Logf("have mapping: %v", c.HaveMapping())
|
||||
})
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"tailscale.com/net/interfaces"
|
||||
"tailscale.com/net/netaddr"
|
||||
"tailscale.com/net/neterror"
|
||||
"tailscale.com/net/netmon"
|
||||
"tailscale.com/net/netns"
|
||||
"tailscale.com/net/sockstats"
|
||||
"tailscale.com/types/logger"
|
||||
@@ -59,6 +60,7 @@ const trustServiceStillAvailableDuration = 10 * time.Minute
|
||||
// Client is a port mapping client.
|
||||
type Client struct {
|
||||
logf logger.Logf
|
||||
netMon *netmon.Monitor // optional; nil means interfaces will be looked up on-demand
|
||||
ipAndGateway func() (gw, ip netip.Addr, ok bool)
|
||||
onChange func() // or nil
|
||||
debug DebugKnobs
|
||||
@@ -153,15 +155,19 @@ func (m *pmpMapping) Release(ctx context.Context) {
|
||||
|
||||
// NewClient returns a new portmapping client.
|
||||
//
|
||||
// The netMon parameter is optional; if non-nil it's used to do faster interface
|
||||
// lookups.
|
||||
//
|
||||
// The debug argument allows configuring the behaviour of the portmapper for
|
||||
// debugging; if nil, a sensible set of defaults will be used.
|
||||
//
|
||||
// The optional onChange argument specifies a func to run in a new
|
||||
// goroutine whenever the port mapping status has changed. If nil,
|
||||
// it doesn't make a callback.
|
||||
func NewClient(logf logger.Logf, debug *DebugKnobs, onChange func()) *Client {
|
||||
func NewClient(logf logger.Logf, netMon *netmon.Monitor, debug *DebugKnobs, onChange func()) *Client {
|
||||
ret := &Client{
|
||||
logf: logf,
|
||||
netMon: netMon,
|
||||
ipAndGateway: interfaces.LikelyHomeRouterIP,
|
||||
onChange: onChange,
|
||||
}
|
||||
@@ -271,7 +277,7 @@ func (c *Client) listenPacket(ctx context.Context, network, addr string) (nettyp
|
||||
}
|
||||
return pc.(*net.UDPConn), nil
|
||||
}
|
||||
pc, err := netns.Listener(c.logf).ListenPacket(ctx, network, addr)
|
||||
pc, err := netns.Listener(c.logf, c.netMon).ListenPacket(ctx, network, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ func TestCreateOrGetMapping(t *testing.T) {
|
||||
if v, _ := strconv.ParseBool(os.Getenv("HIT_NETWORK")); !v {
|
||||
t.Skip("skipping test without HIT_NETWORK=1")
|
||||
}
|
||||
c := NewClient(t.Logf, nil, nil)
|
||||
c := NewClient(t.Logf, nil, nil, nil)
|
||||
defer c.Close()
|
||||
c.SetLocalPort(1234)
|
||||
for i := 0; i < 2; i++ {
|
||||
@@ -32,7 +32,7 @@ func TestClientProbe(t *testing.T) {
|
||||
if v, _ := strconv.ParseBool(os.Getenv("HIT_NETWORK")); !v {
|
||||
t.Skip("skipping test without HIT_NETWORK=1")
|
||||
}
|
||||
c := NewClient(t.Logf, nil, nil)
|
||||
c := NewClient(t.Logf, nil, nil, nil)
|
||||
defer c.Close()
|
||||
for i := 0; i < 3; i++ {
|
||||
if i > 0 {
|
||||
@@ -47,7 +47,7 @@ func TestClientProbeThenMap(t *testing.T) {
|
||||
if v, _ := strconv.ParseBool(os.Getenv("HIT_NETWORK")); !v {
|
||||
t.Skip("skipping test without HIT_NETWORK=1")
|
||||
}
|
||||
c := NewClient(t.Logf, nil, nil)
|
||||
c := NewClient(t.Logf, nil, nil, nil)
|
||||
defer c.Close()
|
||||
c.SetLocalPort(1234)
|
||||
res, err := c.Probe(context.Background())
|
||||
|
||||
@@ -237,7 +237,7 @@ func (c *Client) upnpHTTPClientLocked() *http.Client {
|
||||
if c.uPnPHTTPClient == nil {
|
||||
c.uPnPHTTPClient = &http.Client{
|
||||
Transport: &http.Transport{
|
||||
DialContext: netns.NewDialer(c.logf).DialContext,
|
||||
DialContext: netns.NewDialer(c.logf, c.netMon).DialContext,
|
||||
IdleConnTimeout: 2 * time.Second, // LAN is cheap
|
||||
},
|
||||
}
|
||||
|
||||
@@ -109,8 +109,8 @@ func GetValidation() *ValidationSockStats {
|
||||
|
||||
// SetNetMon configures the sockstats package to monitor the active
|
||||
// interface, so that per-interface stats can be collected.
|
||||
func SetNetMon(lm *netmon.Monitor) {
|
||||
setNetMon(lm)
|
||||
func SetNetMon(netMon *netmon.Monitor) {
|
||||
setNetMon(netMon)
|
||||
}
|
||||
|
||||
// DebugInfo returns a string containing debug information about the tracked
|
||||
|
||||
@@ -30,7 +30,7 @@ func getValidation() *ValidationSockStats {
|
||||
return nil
|
||||
}
|
||||
|
||||
func setNetMon(lm *netmon.Monitor) {
|
||||
func setNetMon(netMon *netmon.Monitor) {
|
||||
}
|
||||
|
||||
func debugInfo() string {
|
||||
|
||||
@@ -249,13 +249,13 @@ func getValidation() *ValidationSockStats {
|
||||
return r
|
||||
}
|
||||
|
||||
func setNetMon(lm *netmon.Monitor) {
|
||||
func setNetMon(netMon *netmon.Monitor) {
|
||||
sockStats.mu.Lock()
|
||||
defer sockStats.mu.Unlock()
|
||||
|
||||
// We intentionally populate all known interfaces now, so that we can
|
||||
// increment stats for them without holding mu.
|
||||
state := lm.InterfaceState()
|
||||
state := netMon.InterfaceState()
|
||||
for ifName, iface := range state.Interface {
|
||||
sockStats.knownInterfaces[iface.Index] = ifName
|
||||
}
|
||||
@@ -266,7 +266,7 @@ func setNetMon(lm *netmon.Monitor) {
|
||||
sockStats.usedInterfaces[ifIndex] = 1
|
||||
}
|
||||
|
||||
lm.RegisterChangeCallback(func(changed bool, state *interfaces.State) {
|
||||
netMon.RegisterChangeCallback(func(changed bool, state *interfaces.State) {
|
||||
if changed {
|
||||
if ifName := state.DefaultRouteInterface; ifName != "" {
|
||||
ifIndex := state.Interface[ifName].Index
|
||||
|
||||
@@ -128,14 +128,14 @@ func (d *Dialer) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Dialer) SetNetMon(mon *netmon.Monitor) {
|
||||
func (d *Dialer) SetNetMon(netMon *netmon.Monitor) {
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
if d.netMonUnregister != nil {
|
||||
go d.netMonUnregister()
|
||||
d.netMonUnregister = nil
|
||||
}
|
||||
d.netMon = mon
|
||||
d.netMon = netMon
|
||||
d.netMonUnregister = d.netMon.RegisterChangeCallback(d.linkChanged)
|
||||
}
|
||||
|
||||
@@ -279,7 +279,7 @@ func (d *Dialer) SystemDial(ctx context.Context, network, addr string) (net.Conn
|
||||
if logf == nil {
|
||||
logf = logger.Discard
|
||||
}
|
||||
d.netnsDialer = netns.NewDialer(logf)
|
||||
d.netnsDialer = netns.NewDialer(logf, d.netMon)
|
||||
})
|
||||
c, err := d.netnsDialer.DialContext(ctx, network, addr)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user