mirror of
https://github.com/tailscale/tailscale.git
synced 2024-12-02 22:45:37 +00:00
wip
This commit is contained in:
parent
c6d2c16ecc
commit
66c416a0a5
@ -1417,12 +1417,16 @@ func (lc *LocalClient) CheckUpdate(ctx context.Context) (*tailcfg.ClientVersion,
|
|||||||
return &cv, nil
|
return &cv, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lc *LocalClient) SuggestExitNode(ctx context.Context) error {
|
func (lc *LocalClient) SuggestExitNode(ctx context.Context) (*tailcfg.StableNodeID, error) {
|
||||||
body, err := lc.send(ctx, "POST", "/localapi/v0/suggest-exit-node", 200, nil)
|
body, err := lc.send(ctx, "POST", "/localapi/v0/suggest-exit-node", 200, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error %w: %s", err, body)
|
return nil, fmt.Errorf("error %w: %s", err, body)
|
||||||
}
|
}
|
||||||
return nil
|
nodeID, err := decodeJSON[tailcfg.StableNodeID](body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &nodeID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// IPNBusWatcher is an active subscription (watch) of the local tailscaled IPN bus.
|
// IPNBusWatcher is an active subscription (watch) of the local tailscaled IPN bus.
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
"github.com/peterbourgon/ff/v3/ffcli"
|
"github.com/peterbourgon/ff/v3/ffcli"
|
||||||
xmaps "golang.org/x/exp/maps"
|
xmaps "golang.org/x/exp/maps"
|
||||||
|
"tailscale.com/ipn"
|
||||||
"tailscale.com/ipn/ipnstate"
|
"tailscale.com/ipn/ipnstate"
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
)
|
)
|
||||||
@ -107,16 +108,32 @@ func runExitNodeList(ctx context.Context, args []string) error {
|
|||||||
}
|
}
|
||||||
fmt.Fprintln(w)
|
fmt.Fprintln(w)
|
||||||
fmt.Fprintln(w)
|
fmt.Fprintln(w)
|
||||||
fmt.Fprintln(w, "# To use an exit node, use `tailscale set --exit-node=` followed by the hostname or IP")
|
fmt.Fprintln(w, "# To use an exit node, use `tailscale set --exit-node=` followed by the hostname or IP. Or `tailscale exit-node suggest` to have Tailscale pick an exit node for you.")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func runExitNodeSuggest(ctx context.Context, args []string) error {
|
func runExitNodeSuggest(ctx context.Context, args []string) error {
|
||||||
err := localClient.SuggestExitNode(ctx)
|
var suggestedNodeID *tailcfg.StableNodeID
|
||||||
|
suggestedNodeID, err := localClient.SuggestExitNode(ctx)
|
||||||
|
if suggestedNodeID == nil {
|
||||||
|
fmt.Println("Unable to suggest exit node")
|
||||||
|
} else {
|
||||||
|
fmt.Printf("Chosen exit node id: %v", *suggestedNodeID)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to suggest exit node. Error: %v", err)
|
return fmt.Errorf("Failed to suggest exit node. Error: %v", err)
|
||||||
}
|
}
|
||||||
|
_, err = localClient.EditPrefs(ctx, &ipn.MaskedPrefs{
|
||||||
|
Prefs: ipn.Prefs{
|
||||||
|
ExitNodeID: *suggestedNodeID,
|
||||||
|
},
|
||||||
|
ExitNodeIDSet: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to set suggested exit node. Error: %v", err)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,8 +309,7 @@ type LocalBackend struct {
|
|||||||
clock tstime.Clock
|
clock tstime.Clock
|
||||||
|
|
||||||
// Last ClientVersion received in MapResponse, guarded by mu.
|
// Last ClientVersion received in MapResponse, guarded by mu.
|
||||||
lastClientVersion *tailcfg.ClientVersion
|
lastClientVersion *tailcfg.ClientVersion
|
||||||
suggestedExitNodeMap map[tailcfg.NodeID]tailcfg.DERPRegion
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type updateStatus struct {
|
type updateStatus struct {
|
||||||
@ -5913,35 +5912,64 @@ func mayDeref[T any](p *T) (v T) {
|
|||||||
return *p
|
return *p
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *LocalBackend) SuggestExitNode() error {
|
func (b *LocalBackend) SuggestExitNode() (*tailcfg.StableNodeID, error) {
|
||||||
//b.mu.Lock()
|
b.mu.Lock()
|
||||||
netMap := b.netMap
|
netMap := b.netMap
|
||||||
|
if netMap == nil {
|
||||||
|
b.mu.Unlock()
|
||||||
|
return nil, errors.New("no netmap")
|
||||||
|
}
|
||||||
peers := netMap.Peers
|
peers := netMap.Peers
|
||||||
lastReport := b.MagicConn().GetLastNetcheckReport()
|
lastReport := b.MagicConn().GetLastNetcheckReport()
|
||||||
var fastestRegionLatency = time.Duration(math.MaxInt64)
|
var fastestRegionLatency = time.Duration(math.MaxInt64)
|
||||||
var preferredExitNodeID tailcfg.StableNodeID
|
var preferredExitNodeID tailcfg.StableNodeID
|
||||||
|
peerRegionMap := make(map[int][]tailcfg.NodeView)
|
||||||
for _, peer := range peers {
|
for _, peer := range peers {
|
||||||
if tsaddr.ContainsExitRoutes(peer.AllowedIPs()) && strings.HasPrefix(peer.DERP(), derpPrefix) {
|
if online := peer.Online(); online != nil && !*online {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if tsaddr.ContainsExitRoutes(peer.AllowedIPs()) /*&& strings.HasPrefix(peer.DERP(), derpPrefix)*/ {
|
||||||
ipp, _ := netip.ParseAddrPort(peer.DERP())
|
ipp, _ := netip.ParseAddrPort(peer.DERP())
|
||||||
regionID := int(ipp.Port())
|
regionID := int(ipp.Port())
|
||||||
if lastReport.RegionLatency[regionID] < fastestRegionLatency {
|
regionLatency, ok := lastReport.RegionLatency[regionID]
|
||||||
|
peerRegionMap[regionID] = append(peerRegionMap[regionID], peer)
|
||||||
|
b.logf("region %d latency %v(%v) node id %v", regionID, regionLatency, ok, peer.Name())
|
||||||
|
if ok && regionLatency < fastestRegionLatency {
|
||||||
fastestRegionLatency = lastReport.RegionLatency[regionID]
|
fastestRegionLatency = lastReport.RegionLatency[regionID]
|
||||||
preferredExitNodeID = peer.StableID()
|
preferredExitNodeID = peer.StableID()
|
||||||
b.logf("fastest region latency %v preferred exit node id %v", lastReport.RegionLatency[regionID], peer.StableID())
|
b.logf("fastest region latency %v preferred exit node id %v", lastReport.RegionLatency[regionID], peer.StableID())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// b.mu.Unlock()
|
b.logf("self derp %v", b.netMap.SelfNode.DERP())
|
||||||
prefs := b.Prefs().AsStruct()
|
ipp, _ := netip.ParseAddrPort(netMap.SelfNode.DERP())
|
||||||
prefs.ExitNodeID = preferredExitNodeID
|
selfDerpRegionID := int(ipp.Port())
|
||||||
_, err := b.EditPrefs(&ipn.MaskedPrefs{
|
b.logf("self derp region id %v", selfDerpRegionID)
|
||||||
Prefs: ipn.Prefs{
|
sameRegionNodes, ok := peerRegionMap[selfDerpRegionID]
|
||||||
ExitNodeID: preferredExitNodeID,
|
b.mu.Unlock()
|
||||||
},
|
if ok {
|
||||||
ExitNodeIDSet: true,
|
preferredExitNodeID = balancedPick(netMap.SelfNode, sameRegionNodes).StableID()
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Failed to suggest exit node %v, err %v", preferredExitNodeID, err)
|
|
||||||
}
|
}
|
||||||
return nil
|
//b.logf("peer region map %v", peerRegionMap)
|
||||||
|
b.logf("chosen exit node id %v", preferredExitNodeID)
|
||||||
|
if preferredExitNodeID.IsZero() {
|
||||||
|
return &preferredExitNodeID, fmt.Errorf("Unable to choose exit node")
|
||||||
|
}
|
||||||
|
return &preferredExitNodeID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func balancedPick(selfNode tailcfg.NodeView, candidates []tailcfg.NodeView) tailcfg.NodeView {
|
||||||
|
if len(candidates) == 1 {
|
||||||
|
return candidates[0]
|
||||||
|
} else {
|
||||||
|
sort.Slice(candidates, func(i, j int) bool { return candidates[i].ID() < candidates[j].ID() })
|
||||||
|
|
||||||
|
mappedID := selfNode.ID() % 512
|
||||||
|
chosen := candidates[int(math.Floor(float64(mappedID)/512*float64(len(candidates))))]
|
||||||
|
return chosen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*func measureLatency(ctx context.Context, derpRegion *DERPRegion, p *ping.Pinger) (time.Duration, error) {
|
||||||
|
node := derpRegion.Nodes[0]
|
||||||
|
} */
|
||||||
|
@ -2515,9 +2515,10 @@ func (h *Handler) serveSuggestExitNode(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Error(w, "want POST", http.StatusBadRequest)
|
http.Error(w, "want POST", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err := h.b.SuggestExitNode()
|
suggestedExitNodeID, err := h.b.SuggestExitNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeErrorJSON(w, err)
|
writeErrorJSON(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
json.NewEncoder(w).Encode(suggestedExitNodeID)
|
||||||
}
|
}
|
||||||
|
@ -3010,5 +3010,6 @@ func getPeerMTUsProbedMetric(mtu tstun.WireMTU) *clientmetric.Metric {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Conn) GetLastNetcheckReport() *netcheck.Report {
|
func (c *Conn) GetLastNetcheckReport() *netcheck.Report {
|
||||||
return c.lastNetCheckReport.Load()
|
report, _ := c.updateNetInfo(c.connCtx)
|
||||||
|
return report
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user