WIP: rebase fork onto upstream/main (v1.103.0) #15

Closed
codinget wants to merge 670 commits from webnet into save/webnet-2026-07-29
19 changed files with 306 additions and 74 deletions
Showing only changes of commit 932260511e - Show all commits
+1
View File
@@ -108,6 +108,7 @@ tailscale.com/cmd/derper dependencies: (generated by github.com/tailscale/depawa
tailscale.com/net/netutil from tailscale.com/client/local
tailscale.com/net/netx from tailscale.com/net/dnscache+
tailscale.com/net/routecheck from tailscale.com/client/local
tailscale.com/net/routecheck/peernode from tailscale.com/net/routecheck
tailscale.com/net/sockstats from tailscale.com/derp/derphttp
tailscale.com/net/stun from tailscale.com/net/stunserver
tailscale.com/net/stunserver from tailscale.com/cmd/derper
+2 -1
View File
@@ -807,7 +807,8 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/
tailscale.com/net/portmapper from tailscale.com/feature/portmapper
tailscale.com/net/portmapper/portmappertype from tailscale.com/net/netcheck+
tailscale.com/net/proxymux from tailscale.com/tsnet
tailscale.com/net/routecheck from tailscale.com/client/local
tailscale.com/net/routecheck from tailscale.com/client/local+
tailscale.com/net/routecheck/peernode from tailscale.com/ipn/ipnlocal+
💣 tailscale.com/net/sockopts from tailscale.com/wgengine/magicsock
tailscale.com/net/socks5 from tailscale.com/tsnet
tailscale.com/net/sockstats from tailscale.com/control/controlclient+
+1
View File
@@ -236,6 +236,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
tailscale.com/net/portmapper from tailscale.com/feature/portmapper
tailscale.com/net/portmapper/portmappertype from tailscale.com/net/netcheck+
tailscale.com/net/routecheck from tailscale.com/client/local+
tailscale.com/net/routecheck/peernode from tailscale.com/net/routecheck
tailscale.com/net/sockstats from tailscale.com/control/controlhttp+
tailscale.com/net/stun from tailscale.com/net/netcheck
tailscale.com/net/tlsdial from tailscale.com/cmd/tailscale/cli+
+1
View File
@@ -107,6 +107,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
tailscale.com/net/packet/checksum from tailscale.com/net/tstun
tailscale.com/net/ping from tailscale.com/net/netcheck+
tailscale.com/net/portmapper/portmappertype from tailscale.com/net/netcheck+
tailscale.com/net/routecheck/peernode from tailscale.com/ipn/ipnlocal
tailscale.com/net/sockopts from tailscale.com/wgengine/magicsock
tailscale.com/net/sockstats from tailscale.com/control/controlclient+
tailscale.com/net/stun from tailscale.com/net/netcheck+
+1
View File
@@ -124,6 +124,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
tailscale.com/net/packet/checksum from tailscale.com/net/tstun
tailscale.com/net/ping from tailscale.com/net/netcheck+
tailscale.com/net/portmapper/portmappertype from tailscale.com/net/netcheck+
tailscale.com/net/routecheck/peernode from tailscale.com/ipn/ipnlocal
tailscale.com/net/sockopts from tailscale.com/wgengine/magicsock
tailscale.com/net/sockstats from tailscale.com/control/controlclient+
tailscale.com/net/stun from tailscale.com/net/netcheck+
+1
View File
@@ -380,6 +380,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
tailscale.com/net/portmapper/portmappertype from tailscale.com/feature/portmapper+
tailscale.com/net/proxymux from tailscale.com/cmd/tailscaled
tailscale.com/net/routecheck from tailscale.com/feature/routecheck+
tailscale.com/net/routecheck/peernode from tailscale.com/ipn/ipnlocal+
tailscale.com/net/routetable from tailscale.com/doctor/routetable
💣 tailscale.com/net/sockopts from tailscale.com/wgengine/magicsock+
tailscale.com/net/socks5 from tailscale.com/cmd/tailscaled
+1 -1
View File
@@ -208,7 +208,7 @@ func TestOmitRouteCheck(t *testing.T) {
GOARCH: "amd64",
Tags: "ts_omit_routecheck,ts_include_cli",
OnDep: func(dep string) {
if strings.Contains(dep, "routecheck") {
if strings.Contains(dep, "routecheck") && !strings.HasSuffix(dep, "/peernode") {
t.Errorf("unexpected dep: %q", dep)
}
},
+2 -1
View File
@@ -206,7 +206,8 @@ tailscale.com/cmd/tsidp dependencies: (generated by github.com/tailscale/depawar
tailscale.com/net/portmapper from tailscale.com/feature/portmapper
tailscale.com/net/portmapper/portmappertype from tailscale.com/net/netcheck+
tailscale.com/net/proxymux from tailscale.com/tsnet
tailscale.com/net/routecheck from tailscale.com/client/local
tailscale.com/net/routecheck from tailscale.com/client/local+
tailscale.com/net/routecheck/peernode from tailscale.com/ipn/ipnlocal+
💣 tailscale.com/net/sockopts from tailscale.com/wgengine/magicsock
tailscale.com/net/socks5 from tailscale.com/tsnet
tailscale.com/net/sockstats from tailscale.com/control/controlclient+
+15
View File
@@ -19,6 +19,7 @@ import (
"sync"
"tailscale.com/ipn/ipnext"
"tailscale.com/ipn/ipnlocal"
"tailscale.com/net/routecheck"
"tailscale.com/tailcfg"
"tailscale.com/types/logger"
@@ -36,6 +37,8 @@ func init() {
backend: b,
}, nil
})
ipnlocal.HookRouteCheckReport.Set(routeCheckReport)
}
// Extension implements the [ipnext.Extension] interface.
@@ -162,3 +165,15 @@ func (e *Extension) reconcileLoop() {
}
}
}
func routeCheckReport(b *ipnlocal.LocalBackend) ipnlocal.RouteCheckReport {
c := ClientFor(b)
if c == nil {
return nil
}
r := c.Report()
if r == nil {
return nil
}
return r
}
+21 -14
View File
@@ -2530,6 +2530,9 @@ func (b *LocalBackend) UpdateNetmapDelta(muts []netmap.NodeMutation) (handled bo
}
}
// TODO(sfllaw): If [buildfeatures.HasRouteCheck] is enabled,
// a probe may be triggered for an updated routecheck reachability report,
// which should influence which exit nodes are considered valid to suggest.
if cn.NetMap() != nil && mutationsAreWorthyOfRecalculatingSuggestedExitNode(muts, cn, b.lastSuggestedExitNode) {
// Recompute the suggested exit node
b.suggestExitNodeLocked()
@@ -8604,6 +8607,9 @@ func allowedAutoRoute(ipp netip.Prefix) bool {
return true
}
// HookRouteCheckReport is the hook that returns the latest routecheck Report for this LocalBackend.
var HookRouteCheckReport feature.Hook[func(*LocalBackend) RouteCheckReport]
var ErrNoPreferredDERP = errors.New("no preferred DERP, try again later")
// suggestExitNodeLocked computes a suggestion based on the current netmap and
@@ -8611,11 +8617,15 @@ var ErrNoPreferredDERP = errors.New("no preferred DERP, try again later")
// be selected at random, so the result is not stable. To be eligible for
// consideration, the peer must have [tailcfg.NodeAttrSuggestExitNode] in its CapMap.
//
// When the client has [tailcfg.NodeAttrClientSideReachabilityRouteCheck] in its CapMap,
// it will use [HookRouteCheckReport] to get the latest cached routecheck report.
//
// b.mu must be held.
func (b *LocalBackend) suggestExitNodeLocked() (response apitype.ExitNodeSuggestionResponse, err error) {
if !buildfeatures.HasUseExitNode {
return response, feature.ErrUnavailable
}
mc := b.MagicConn()
var preferredDERP int
if lastReport := mc.GetLastNetcheckReport(b.ctx); lastReport != nil {
@@ -8628,7 +8638,12 @@ func (b *LocalBackend) suggestExitNodeLocked() (response apitype.ExitNodeSuggest
regionLatency := mc.GetDERPRegionLatency()
prevSuggestion := b.lastSuggestedExitNode
res, err := suggestExitNode(preferredDERP, regionLatency, b.currentNode(), prevSuggestion, randomRegion, randomNode, b.getAllowedSuggestions())
var rp RouteCheckReport
if hook := HookRouteCheckReport.GetOrNil(); hook != nil {
rp = hook(b)
}
res, err := suggestExitNode(preferredDERP, regionLatency, rp, b.currentNode(), prevSuggestion, randomRegion, randomNode, b.getAllowedSuggestions())
if err != nil {
return res, err
}
@@ -8710,11 +8725,11 @@ func fillAllowedSuggestions(polc policyclient.Client) (set.Set[tailcfg.StableNod
// netcheck and are only used by the DERP-based algorithm.
//
// Errors are always logged. Suggestions are logged if they defer from prevSuggestion.
func suggestExitNode(preferredDERP int, regionLatency map[int]time.Duration, nb *nodeBackend, prevSuggestion tailcfg.StableNodeID, selectRegion selectRegionFunc, selectNode selectNodeFunc, allowList set.Set[tailcfg.StableNodeID]) (res apitype.ExitNodeSuggestionResponse, err error) {
func suggestExitNode(preferredDERP int, regionLatency map[int]time.Duration, rp RouteCheckReport, nb *nodeBackend, prevSuggestion tailcfg.StableNodeID, selectRegion selectRegionFunc, selectNode selectNodeFunc, allowList set.Set[tailcfg.StableNodeID]) (res apitype.ExitNodeSuggestionResponse, err error) {
switch {
case nb.SelfHasCap(tailcfg.NodeAttrTrafficSteering):
// The traffic-steering feature flag is enabled on this tailnet.
res, err = suggestExitNodeUsingTrafficSteering(nb, allowList)
res, err = suggestExitNodeUsingTrafficSteering(rp, nb, allowList)
default:
// The control plane will always strip the `traffic-steering`
// node attribute if it isnt enabled for this tailnet, even if
@@ -8747,10 +8762,6 @@ func suggestExitNode(preferredDERP int, regionLatency map[int]time.Duration, nb
// the lowest latency to this device. For peers without a DERP home, we look for
// geographic proximity to this device's DERP home.
func suggestExitNodeUsingDERP(preferredRegionID int, regionLatency map[int]time.Duration, nb *nodeBackend, prevSuggestion tailcfg.StableNodeID, selectRegion selectRegionFunc, selectNode selectNodeFunc, allowList set.Set[tailcfg.StableNodeID]) (res apitype.ExitNodeSuggestionResponse, err error) {
// TODO(sfllaw): Context needs to be plumbed down here to support
// reachability testing.
ctx := context.TODO()
netMap := nb.NetMap()
if preferredRegionID == 0 || netMap == nil || netMap.DERPMap == nil {
return res, ErrNoPreferredDERP
@@ -8759,7 +8770,7 @@ func suggestExitNodeUsingDERP(preferredRegionID int, regionLatency map[int]time.
// since the netmap doesn't include delta updates (e.g., home DERP or Online
// status changes) from the control plane since the last full update.
candidates := nb.AppendMatchingPeers(nil, func(peer tailcfg.NodeView) bool {
if !peer.Valid() || !nb.PeerIsReachable(ctx, peer) {
if !peer.Valid() || !nb.PeerIsReachable(nil, peer) {
return false
}
if allowList != nil && !allowList.Contains(peer.StableID()) {
@@ -8887,11 +8898,7 @@ var ErrNoNetMap = errors.New("no network map, try again later")
// pick one of the best exit nodes. These priorities are provided by Control in
// the nodes [tailcfg.Location]. To be eligible for consideration, the node
// must have NodeAttrSuggestExitNode in its CapMap.
func suggestExitNodeUsingTrafficSteering(nb *nodeBackend, allowed set.Set[tailcfg.StableNodeID]) (apitype.ExitNodeSuggestionResponse, error) {
// TODO(sfllaw): Context needs to be plumbed down here to support
// reachability testing.
ctx := context.TODO()
func suggestExitNodeUsingTrafficSteering(rp RouteCheckReport, nb *nodeBackend, allowed set.Set[tailcfg.StableNodeID]) (apitype.ExitNodeSuggestionResponse, error) {
nm := nb.NetMap()
if nm == nil {
return apitype.ExitNodeSuggestionResponse{}, ErrNoNetMap
@@ -8910,7 +8917,7 @@ func suggestExitNodeUsingTrafficSteering(nb *nodeBackend, allowed set.Set[tailcf
if !p.Valid() {
return false
}
if !nb.PeerIsReachable(ctx, p) {
if !nb.PeerIsReachable(rp, p) {
return false
}
if allowed != nil && !allowed.Contains(p.StableID()) {
+2 -2
View File
@@ -6062,7 +6062,7 @@ func TestSuggestExitNode(t *testing.T) {
regionLatency = tt.lastReport.RegionLatency
}
got, err := suggestExitNode(preferredDERP, regionLatency, nb, tt.lastSuggestion, selectRegion, selectNode, allowList)
got, err := suggestExitNode(preferredDERP, regionLatency, nil, nb, tt.lastSuggestion, selectRegion, selectNode, allowList)
if got.Name != tt.wantName {
t.Errorf("name=%v, want %v", got.Name, tt.wantName)
}
@@ -6620,7 +6620,7 @@ func TestSuggestExitNodeTrafficSteering(t *testing.T) {
defer nb.shutdown(errShutdown)
nb.SetNetMap(tt.netMap)
got, err := suggestExitNodeUsingTrafficSteering(nb, allowList)
got, err := suggestExitNodeUsingTrafficSteering(nil, nb, allowList)
if tt.wantErr == nil && err != nil {
t.Fatalf("err=%v, want nil", err)
}
+56 -21
View File
@@ -18,6 +18,7 @@ import (
"tailscale.com/feature/buildfeatures"
"tailscale.com/ipn"
"tailscale.com/net/dns"
"tailscale.com/net/routecheck/peernode"
"tailscale.com/net/tsaddr"
"tailscale.com/syncs"
"tailscale.com/tailcfg"
@@ -448,10 +449,37 @@ func (nb *nodeBackend) PeerAPIBase(p tailcfg.NodeView) string {
return peerAPIBase(nm, p)
}
// PeerIsReachable reports whether the current node can reach p. If the ctx is
// done, this function may return a result based on stale reachability data.
func (nb *nodeBackend) PeerIsReachable(ctx context.Context, p tailcfg.NodeView) bool {
if !nb.SelfHasCap(tailcfg.NodeAttrClientSideReachability) {
// RouteCheckReport is an interface that reports whether a peer is reachable by the current node.
type RouteCheckReport interface {
// IsReachable reports whether a peer is reachable by the current node.
IsReachable(tailcfg.NodeID) peernode.Reachability
}
// PeerIsReachable reports whether the current node can reach p.
// This function may return a result based on stale reachability data,
// either from the control plane or because the latest routecheck report is old.
// If rp is nil, then this will report whether p is connected to the control plane
// according to [tailcfg.NodeView.Online].
//
// The latest routecheck report will be considered if the current node has both
// [tailcfg.NodeAttrClientSideReachability] and [tailcfg.NodeAttrClientSideReachabilityRouteCheck]
// in its CapMap.
func (nb *nodeBackend) PeerIsReachable(rp RouteCheckReport, p tailcfg.NodeView) bool {
nb.mu.Lock()
nm := nb.netMap
nb.mu.Unlock()
if nm == nil || !p.Valid() {
// If there is no netmap, then how did we get a NodeView?
// Assuming that p came from the control plane,
// report whether it was connected to tailcontrol.
return p.Valid() && p.Online().Get()
}
self := nm.SelfNode
useRouteCheck := isRouteCheckEnabled(self)
if !useRouteCheck && !self.HasCap(tailcfg.NodeAttrClientSideReachability) {
// Legacy behavior is to always trust the control plane, which
// isnt always correct because the peer could be slow to check
// in so that control marks it as offline.
@@ -459,27 +487,34 @@ func (nb *nodeBackend) PeerIsReachable(ctx context.Context, p tailcfg.NodeView)
return p.Online().Get()
}
nb.mu.Lock()
nm := nb.netMap
nb.mu.Unlock()
if self := nm.SelfNode; self.Valid() && self.ID() == p.ID() {
if self.Valid() && self.ID() == p.ID() {
// This node can always reach itself.
return true
}
return nb.peerIsReachable(ctx, p)
}
func (nb *nodeBackend) peerIsReachable(ctx context.Context, p tailcfg.NodeView) bool {
// TODO(sfllaw): The following does not actually test for client-side
// reachability. This would require a mechanism that tracks whether the
// current node can actually reach this peer, either because they are
// already communicating or because they can ping each other.
//
// Instead, it makes the client ignore p.Online completely.
//
// See tailscale/corp#32686.
return true
if !useRouteCheck && !self.HasCap(tailcfg.NodeAttrClientSideReachabilityRouteCheck) {
// TODO(sfllaw): The following does not actually test for client-side
// reachability. This would require a mechanism that tracks whether the
// current node can actually reach this peer, either because they are
// already communicating or because they can ping each other.
//
// Instead, it makes the client ignore p.Online completely.
//
// See tailscale/corp#32686.
return true
}
if rp == nil {
// The routecheck report hasnt been collected yet,
// so fall back and report whether it was connected to tailcontrol.
return p.Online().Get()
}
r := rp.IsReachable(p.ID())
if r == peernode.Unknown {
// Reachability is unknown, because the node is a new router, so fall back.
return p.Online().Get()
}
return r.IsReachable()
}
func nodeIP(n tailcfg.NodeView, pred func(netip.Addr) bool) netip.Addr {
+115 -33
View File
@@ -6,13 +6,18 @@ package ipnlocal
import (
"context"
"errors"
"maps"
"slices"
"testing"
"time"
"tailscale.com/net/routecheck/peernode"
"tailscale.com/tailcfg"
"tailscale.com/tstest"
"tailscale.com/types/netmap"
"tailscale.com/util/eventbus"
"tailscale.com/util/mak"
"tailscale.com/util/set"
)
func TestNodeBackendReadiness(t *testing.T) {
@@ -134,58 +139,135 @@ func TestNodeBackendReachability(t *testing.T) {
//
// When disabled, the client relies on the control plane sending
// an accurate peer.Online flag. When enabled, the client
// ignores peer.Online and determines whether it can reach the
// peer node.
// ignores peer.Online and is forced to return true.
cap bool
// rchk sets [tailcfg.NodeAttrClientSideReachabilityRouteCheck]
// on the self node.
//
// When enabled with [tailcfg.NodeAttrClientSideReachability]
// above, the client ignores peer.Online and determines whether
// it can reach the peer node using [routecheck] reports.
rchk bool
peer tailcfg.Node
want bool
online bool
pong peernode.Reachability
want bool
}{
{
name: "disabled/offline",
cap: false,
peer: tailcfg.Node{
Online: new(false),
},
want: false,
name: "disabled/offline",
cap: false,
online: false,
want: false,
},
{
name: "disabled/online",
cap: false,
peer: tailcfg.Node{
Online: new(true),
},
want: true,
name: "disabled/online",
cap: false,
online: true,
want: true,
},
{
name: "enabled/offline",
cap: true,
peer: tailcfg.Node{
Online: new(false),
},
want: true,
name: "forced/offline",
cap: true,
rchk: false,
online: false,
want: true,
},
{
name: "enabled/online",
cap: true,
peer: tailcfg.Node{
Online: new(true),
},
want: true,
name: "forced/online",
cap: true,
rchk: false,
online: true,
want: true,
},
{
name: "routecheck/offline/needs-probe",
cap: true,
rchk: true,
online: false,
pong: peernode.Unknown,
want: false,
},
{
name: "routecheck/offline/unreachable",
cap: true,
rchk: true,
online: false,
pong: peernode.Unreachable,
want: false,
},
{
name: "routecheck/offline/reachable",
cap: true,
rchk: true,
online: false,
pong: peernode.Reachable,
want: true,
},
{
name: "routecheck/online/needs-probe",
cap: true,
rchk: true,
online: true,
pong: peernode.Unknown,
want: true,
},
{
name: "routecheck/online/unreachable",
cap: true,
rchk: true,
online: true,
pong: peernode.Unreachable,
want: false,
},
{
name: "routecheck/online/reachable",
cap: true,
rchk: true,
online: true,
pong: peernode.Reachable,
want: true,
},
} {
t.Run(tc.name, func(t *testing.T) {
nb := newNodeBackend(t.Context(), tstest.WhileTestRunningLogger(t), eventbus.New())
nb.netMap = &netmap.NetworkMap{}
self := &tailcfg.Node{
ID: 1,
StableID: "stable1",
Name: "self",
}
if tc.cap {
nb.netMap.AllCaps.Make()
nb.netMap.AllCaps.Add(tailcfg.NodeAttrClientSideReachability)
mak.Set(&self.CapMap, tailcfg.NodeAttrClientSideReachability, nil)
}
if tc.rchk {
mak.Set(&self.CapMap, tailcfg.NodeAttrClientSideReachabilityRouteCheck, nil)
}
got := nb.PeerIsReachable(t.Context(), tc.peer.View())
peer := &tailcfg.Node{
ID: 2,
StableID: "stable2",
Name: "peer",
Online: &tc.online,
}
nb := newNodeBackend(t.Context(), tstest.WhileTestRunningLogger(t), eventbus.New())
nb.netMap = &netmap.NetworkMap{
SelfNode: self.View(),
Peers: []tailcfg.NodeView{peer.View()},
// HACK: AllCaps is usually populated by Control
AllCaps: set.SetOf(slices.Collect(maps.Keys(self.CapMap))),
}
got := nb.PeerIsReachable(routecheckReport(tc.pong), peer.View())
if got != tc.want {
t.Errorf("got %v, want %v", got, tc.want)
}
})
}
}
type routecheckReport peernode.Reachability
var _ RouteCheckReport = *new(routecheckReport)
func (rp routecheckReport) IsReachable(_ tailcfg.NodeID) peernode.Reachability {
return peernode.Reachability(rp)
}
+12
View File
@@ -0,0 +1,12 @@
// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause
//go:build ts_omit_routecheck
package ipnlocal
import "tailscale.com/tailcfg"
func isRouteCheckEnabled(self tailcfg.NodeView) bool {
return false
}
+15
View File
@@ -0,0 +1,15 @@
// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause
//go:build !ts_omit_routecheck
package ipnlocal
import (
"tailscale.com/net/routecheck"
"tailscale.com/tailcfg"
)
func isRouteCheckEnabled(self tailcfg.NodeView) bool {
return routecheck.IsEnabled(self)
}
+5
View File
@@ -0,0 +1,5 @@
// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause
// Package peernode contains constants that describe a peer node in the routecheck report.
package peernode
+39
View File
@@ -0,0 +1,39 @@
// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause
package peernode
import "fmt"
// Reachability reports whether a peer node is reachable.
type Reachability int
const (
// Unknown is a peer node whose reachability is unknown.
// This peer is new and we have yet to probe it.
Unknown Reachability = iota
// Reachable is a peer node that is reachable from this node.
Reachable
// Unreachable is a peer node that is unreachable from this node.
// This peer is not responding to probes.
Unreachable
)
// String implements [fmt.Stringer].
func (r Reachability) String() string {
switch r {
case Unknown:
return "Unknown"
case Reachable:
return "Reachable"
case Unreachable:
return "Unreachable"
default:
panic(fmt.Sprintf("unknown %#v", r))
}
}
// IsReachable reports if r is [Reachable].
func (r Reachability) IsReachable() bool {
return r == Reachable
}
+14
View File
@@ -15,6 +15,7 @@ import (
"github.com/go-json-experiment/json/jsontext"
jsonv1 "github.com/go-json-experiment/json/v1"
"tailscale.com/net/routecheck/peernode"
"tailscale.com/tailcfg"
"tailscale.com/util/clientmetric"
"tailscale.com/util/mak"
@@ -46,6 +47,19 @@ type Report struct {
LastProbed map[tailcfg.NodeID]time.Time `json:"-"` // not marshaled
}
// IsReachable reports whether a peer is reachable by the current node.
func (rp Report) IsReachable(id tailcfg.NodeID) peernode.Reachability {
// TODO(sfllaw): We should actually track all routers and consider the
// absence of a router in the report as it being recently added for
// consideration, so it is unknown. Then we should positively track
// whether a node was reachable or not.
_, k := rp.Reachable[id]
if k {
return peernode.Reachable
}
return peernode.Unknown
}
// RoutablePrefixes returns a map of routable network prefixes associated with
// each prefixs routers that were reachable by the current host,
// at the time the report was finished.
+2 -1
View File
@@ -202,7 +202,8 @@ tailscale.com/tsnet dependencies: (generated by github.com/tailscale/depaware)
tailscale.com/net/portmapper from tailscale.com/feature/portmapper
tailscale.com/net/portmapper/portmappertype from tailscale.com/net/netcheck+
tailscale.com/net/proxymux from tailscale.com/tsnet
tailscale.com/net/routecheck from tailscale.com/client/local
tailscale.com/net/routecheck from tailscale.com/client/local+
tailscale.com/net/routecheck/peernode from tailscale.com/ipn/ipnlocal+
💣 tailscale.com/net/sockopts from tailscale.com/wgengine/magicsock
tailscale.com/net/socks5 from tailscale.com/tsnet
tailscale.com/net/sockstats from tailscale.com/control/controlclient+