Update
This commit is contained in:
1 parent
3530afb854
commit
6903fcff27
2146 files changed
+112256
-190703
No files matched your search
+105
-35
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// Copyright (c) Tailscale Inc & contributors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package magicsock
|
||||
@@ -6,6 +6,7 @@ package magicsock
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"maps"
|
||||
"net"
|
||||
@@ -44,12 +45,12 @@ const frameReceiveRecordRate = 5 * time.Second
|
||||
// current connection for that regionID is dc. (but dc should not be
|
||||
// used to write directly; it's owned by the read/write loops)
|
||||
type derpRoute struct {
|
||||
regionID int
|
||||
regionID tailcfg.DERPRegionID
|
||||
dc *derphttp.Client // don't use directly; see comment above
|
||||
}
|
||||
|
||||
// removeDerpPeerRoute removes a DERP route entry previously added by addDerpPeerRoute.
|
||||
func (c *Conn) removeDerpPeerRoute(peer key.NodePublic, regionID int, dc *derphttp.Client) {
|
||||
func (c *Conn) removeDerpPeerRoute(peer key.NodePublic, regionID tailcfg.DERPRegionID, dc *derphttp.Client) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
r2 := derpRoute{regionID, dc}
|
||||
@@ -61,7 +62,7 @@ func (c *Conn) removeDerpPeerRoute(peer key.NodePublic, regionID int, dc *derpht
|
||||
// addDerpPeerRoute adds a DERP route entry, noting that peer was seen
|
||||
// on DERP node derpID, at least on the connection identified by dc.
|
||||
// See issue 150 for details.
|
||||
func (c *Conn) addDerpPeerRoute(peer key.NodePublic, regionID int, dc *derphttp.Client) {
|
||||
func (c *Conn) addDerpPeerRoute(peer key.NodePublic, regionID tailcfg.DERPRegionID, dc *derphttp.Client) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
mak.Set(&c.derpRoute, peer, derpRoute{regionID, dc})
|
||||
@@ -78,7 +79,7 @@ func (c *Conn) addDerpPeerRoute(peer key.NodePublic, regionID int, dc *derphttp.
|
||||
// first connection.
|
||||
//
|
||||
// This can also help nodes from a slow or misbehaving control plane.
|
||||
func (c *Conn) fallbackDERPRegionForPeer(peer key.NodePublic) (regionID int) {
|
||||
func (c *Conn) fallbackDERPRegionForPeer(peer key.NodePublic) (regionID tailcfg.DERPRegionID) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if dr, ok := c.derpRoute[peer]; ok {
|
||||
@@ -100,7 +101,8 @@ type activeDerp struct {
|
||||
}
|
||||
|
||||
var (
|
||||
pickDERPFallbackForTests func() int
|
||||
pickDERPFallbackForTests func() tailcfg.DERPRegionID
|
||||
reSTUNHookForTests func(why string)
|
||||
)
|
||||
|
||||
// pickDERPFallback returns a non-zero but deterministic DERP node to
|
||||
@@ -109,7 +111,7 @@ var (
|
||||
// checks aren't working).
|
||||
//
|
||||
// c.mu must NOT be held.
|
||||
func (c *Conn) pickDERPFallback() int {
|
||||
func (c *Conn) pickDERPFallback() tailcfg.DERPRegionID {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
@@ -154,7 +156,7 @@ var checkControlHealthDuringNearestDERPInTests = false
|
||||
// region that it selected and set (via setNearestDERP).
|
||||
//
|
||||
// c.mu must NOT be held.
|
||||
func (c *Conn) maybeSetNearestDERP(report *netcheck.Report) (preferredDERP int) {
|
||||
func (c *Conn) maybeSetNearestDERP(report *netcheck.Report, force bool) (preferredDERP tailcfg.DERPRegionID) {
|
||||
// Don't change our PreferredDERP if we don't have a connection to
|
||||
// control; if we don't, then we can't inform peers about a DERP home
|
||||
// change, which breaks all connectivity. Even if this DERP region is
|
||||
@@ -168,7 +170,10 @@ func (c *Conn) maybeSetNearestDERP(report *netcheck.Report) (preferredDERP int)
|
||||
//
|
||||
// Despite the above behaviour, ensure that we set the nearest DERP if
|
||||
// we don't currently have one set; any DERP server is better than
|
||||
// none, even if not connected to control.
|
||||
// none, even if not connected to control. The exception here is if we have
|
||||
// a cached netmap with a previous DERP server. Retaining the previous DERP
|
||||
// makes it easier for other nodes to find each other when control is not
|
||||
// available.
|
||||
var connectedToControl bool
|
||||
if testenv.InTest() && !checkControlHealthDuringNearestDERPInTests {
|
||||
connectedToControl = true
|
||||
@@ -178,7 +183,7 @@ func (c *Conn) maybeSetNearestDERP(report *netcheck.Report) (preferredDERP int)
|
||||
c.mu.Lock()
|
||||
myDerp := c.myDerp
|
||||
c.mu.Unlock()
|
||||
if !connectedToControl {
|
||||
if !connectedToControl && !force {
|
||||
if myDerp != 0 {
|
||||
metricDERPHomeNoChangeNoControl.Add(1)
|
||||
return myDerp
|
||||
@@ -197,16 +202,33 @@ func (c *Conn) maybeSetNearestDERP(report *netcheck.Report) (preferredDERP int)
|
||||
}
|
||||
if preferredDERP != myDerp {
|
||||
c.logf(
|
||||
"magicsock: home DERP changing from derp-%d [%dms] to derp-%d [%dms]",
|
||||
c.myDerp, report.RegionLatency[myDerp].Milliseconds(), preferredDERP, report.RegionLatency[preferredDERP].Milliseconds())
|
||||
"magicsock: home DERP changing from derp-%d [%dms] to derp-%d [%dms] (forced=%t)",
|
||||
c.myDerp, report.RegionLatency[myDerp].Milliseconds(), preferredDERP, report.RegionLatency[preferredDERP].Milliseconds(), force)
|
||||
}
|
||||
if !c.setNearestDERP(preferredDERP) {
|
||||
preferredDERP = 0
|
||||
} else if preferredDERP != myDerp {
|
||||
c.homeDERPChangedPub.Publish(HomeDERPChanged{Old: myDerp, New: preferredDERP})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Conn) derpRegionCodeLocked(regionID int) string {
|
||||
// HomeDERPChanged is an event sent on the [eventbus.Bus] when a new home DERP
|
||||
// server has been selected. Its publisher is [magicsock.Coon]; its main
|
||||
// subscriber is [ipnlocal.LocalBackend] that updates the homeDERP used by the
|
||||
// netmap cache.
|
||||
// TODO(cmol): Move the subscriber to not inject into localBackend, but rather
|
||||
// into the netmap at the controlClient mapSession level once there is a stable
|
||||
// abstraction to use.
|
||||
type HomeDERPChanged struct {
|
||||
Old, New tailcfg.DERPRegionID
|
||||
}
|
||||
|
||||
func (c *Conn) ForceSetNearestDERP(regionID tailcfg.DERPRegionID) tailcfg.DERPRegionID {
|
||||
return c.maybeSetNearestDERP(&netcheck.Report{PreferredDERP: regionID}, true)
|
||||
}
|
||||
|
||||
func (c *Conn) derpRegionCodeLocked(regionID tailcfg.DERPRegionID) string {
|
||||
if c.derpMap == nil {
|
||||
return ""
|
||||
}
|
||||
@@ -219,14 +241,14 @@ func (c *Conn) derpRegionCodeLocked(regionID int) string {
|
||||
// setHomeDERPGaugeLocked updates the home DERP gauge metric.
|
||||
//
|
||||
// c.mu must be held.
|
||||
func (c *Conn) setHomeDERPGaugeLocked(derpNum int) {
|
||||
func (c *Conn) setHomeDERPGaugeLocked(derpNum tailcfg.DERPRegionID) {
|
||||
if c.homeDERPGauge != nil {
|
||||
c.homeDERPGauge.Set(float64(derpNum))
|
||||
}
|
||||
}
|
||||
|
||||
// c.mu must NOT be held.
|
||||
func (c *Conn) setNearestDERP(derpNum int) (wantDERP bool) {
|
||||
func (c *Conn) setNearestDERP(derpNum tailcfg.DERPRegionID) (wantDERP bool) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if !c.wantDerpLocked() {
|
||||
@@ -284,7 +306,7 @@ func (c *Conn) startDerpHomeConnectLocked() {
|
||||
// DERP region ID.
|
||||
//
|
||||
// c.mu may be held, but does not need to be.
|
||||
func (c *Conn) goDerpConnect(regionID int) {
|
||||
func (c *Conn) goDerpConnect(regionID tailcfg.DERPRegionID) {
|
||||
if regionID == 0 {
|
||||
return
|
||||
}
|
||||
@@ -314,7 +336,7 @@ const derpWriteQueueDepth = 32
|
||||
//
|
||||
// It returns nil if the network is down, the Conn is closed, or the regionID is
|
||||
// not known.
|
||||
func (c *Conn) derpWriteChanForRegion(regionID int, peer key.NodePublic) chan derpWriteRequest {
|
||||
func (c *Conn) derpWriteChanForRegion(regionID tailcfg.DERPRegionID, peer key.NodePublic) chan derpWriteRequest {
|
||||
if c.networkDown() {
|
||||
return nil
|
||||
}
|
||||
@@ -368,8 +390,8 @@ func (c *Conn) derpWriteChanForRegion(regionID int, peer key.NodePublic) chan de
|
||||
firstDerp := false
|
||||
if c.activeDerp == nil {
|
||||
firstDerp = true
|
||||
c.activeDerp = make(map[int]activeDerp)
|
||||
c.prevDerp = make(map[int]*syncs.WaitGroupChan)
|
||||
c.activeDerp = make(map[tailcfg.DERPRegionID]activeDerp)
|
||||
c.prevDerp = make(map[tailcfg.DERPRegionID]*syncs.WaitGroupChan)
|
||||
}
|
||||
|
||||
// Note that derphttp.NewRegionClient does not dial the server
|
||||
@@ -392,6 +414,10 @@ func (c *Conn) derpWriteChanForRegion(regionID int, peer key.NodePublic) chan de
|
||||
return derpMap.Regions[regionID]
|
||||
})
|
||||
dc.HealthTracker = c.health
|
||||
dc.AppName = c.derpAppName
|
||||
if c.extraRootCAs != nil {
|
||||
dc.TLSConfig = &tls.Config{RootCAs: c.extraRootCAs}
|
||||
}
|
||||
|
||||
dc.SetCanAckPings(true)
|
||||
dc.NotePreferred(c.myDerp == regionID)
|
||||
@@ -436,7 +462,14 @@ func (c *Conn) derpWriteChanForRegion(regionID int, peer key.NodePublic) chan de
|
||||
|
||||
go c.runDerpReader(ctx, regionID, dc, wg, startGate)
|
||||
go c.runDerpWriter(ctx, dc, ch, wg, startGate)
|
||||
go c.derpActiveFunc()
|
||||
go func() {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-startGate:
|
||||
c.derpActiveFunc()
|
||||
}
|
||||
}()
|
||||
|
||||
return ad.writeCh
|
||||
}
|
||||
@@ -448,7 +481,7 @@ func (c *Conn) derpWriteChanForRegion(regionID int, peer key.NodePublic) chan de
|
||||
// If there's any change, it logs.
|
||||
//
|
||||
// c.mu must be held.
|
||||
func (c *Conn) setPeerLastDerpLocked(peer key.NodePublic, regionID, homeID int) {
|
||||
func (c *Conn) setPeerLastDerpLocked(peer key.NodePublic, regionID, homeID tailcfg.DERPRegionID) {
|
||||
if peer.IsZero() {
|
||||
return
|
||||
}
|
||||
@@ -484,7 +517,7 @@ func (c *Conn) setPeerLastDerpLocked(peer key.NodePublic, regionID, homeID int)
|
||||
// get at the packet contents they need to call copyBuf to copy it
|
||||
// out, which also releases the buffer.
|
||||
type derpReadResult struct {
|
||||
regionID int
|
||||
regionID tailcfg.DERPRegionID
|
||||
n int // length of data received
|
||||
src key.NodePublic
|
||||
// copyBuf is called to copy the data to dst. It returns how
|
||||
@@ -497,7 +530,7 @@ type derpReadResult struct {
|
||||
|
||||
// runDerpReader runs in a goroutine for the life of a DERP
|
||||
// connection, handling received packets.
|
||||
func (c *Conn) runDerpReader(ctx context.Context, regionID int, dc *derphttp.Client, wg *syncs.WaitGroupChan, startGate <-chan struct{}) {
|
||||
func (c *Conn) runDerpReader(ctx context.Context, regionID tailcfg.DERPRegionID, dc *derphttp.Client, wg *syncs.WaitGroupChan, startGate <-chan struct{}) {
|
||||
defer wg.Decr()
|
||||
defer dc.Close()
|
||||
|
||||
@@ -675,7 +708,7 @@ func (c *Conn) runDerpWriter(ctx context.Context, dc *derphttp.Client, ch <-chan
|
||||
}
|
||||
}
|
||||
|
||||
func (c *connBind) receiveDERP(buffs [][]byte, sizes []int, eps []conn.Endpoint) (int, error) {
|
||||
func (c *connBind) receiveDERP(slab []byte, packets []conn.ReceivedPacket) (int, error) {
|
||||
if s := c.Conn.health.ReceiveFuncStats(health.ReceiveDERP); s != nil {
|
||||
s.Enter()
|
||||
defer s.Exit()
|
||||
@@ -685,13 +718,14 @@ func (c *connBind) receiveDERP(buffs [][]byte, sizes []int, eps []conn.Endpoint)
|
||||
if c.isClosed() {
|
||||
break
|
||||
}
|
||||
n, ep := c.processDERPReadResult(dm, buffs[0])
|
||||
n, ep := c.processDERPReadResult(dm, slab)
|
||||
if n == 0 {
|
||||
// No data read occurred. Wait for another packet.
|
||||
continue
|
||||
}
|
||||
sizes[0] = n
|
||||
eps[0] = ep
|
||||
packets[0].Size = n
|
||||
packets[0].Offset = 0
|
||||
packets[0].Endpoint = ep
|
||||
return 1, nil
|
||||
}
|
||||
return 0, net.ErrClosed
|
||||
@@ -701,7 +735,7 @@ func (c *Conn) processDERPReadResult(dm derpReadResult, b []byte) (n int, ep *en
|
||||
if dm.copyBuf == nil {
|
||||
return 0, nil
|
||||
}
|
||||
var regionID int
|
||||
var regionID tailcfg.DERPRegionID
|
||||
n, regionID = dm.n, dm.regionID
|
||||
ncopy := dm.copyBuf(b)
|
||||
if ncopy != n {
|
||||
@@ -718,6 +752,10 @@ func (c *Conn) processDERPReadResult(dm derpReadResult, b []byte) (n int, ep *en
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
if c.onDERPRecv != nil && c.onDERPRecv(regionID, dm.src, b[:n]) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
var ok bool
|
||||
c.mu.Lock()
|
||||
ep, ok = c.peerMap.endpointForNodeKey(dm.src)
|
||||
@@ -738,6 +776,15 @@ func (c *Conn) processDERPReadResult(dm derpReadResult, b []byte) (n int, ep *en
|
||||
return n, ep
|
||||
}
|
||||
|
||||
// SendDERPPacketTo sends an arbitrary packet to the given node key via
|
||||
// the DERP relay for the given region. It creates the DERP connection
|
||||
// to the region if one doesn't already exist.
|
||||
func (c *Conn) SendDERPPacketTo(dstKey key.NodePublic, regionID tailcfg.DERPRegionID, pkt []byte) (sent bool, err error) {
|
||||
return c.sendAddr(
|
||||
netip.AddrPortFrom(tailcfg.DerpMagicIPAddr, uint16(regionID)),
|
||||
dstKey, pkt, false, false)
|
||||
}
|
||||
|
||||
// SetOnlyTCP443 set whether the magicsock connection is restricted
|
||||
// to only using TCP port 443 outbound. If true, no UDP is allowed,
|
||||
// no STUN checks are performend, etc.
|
||||
@@ -747,7 +794,24 @@ func (c *Conn) SetOnlyTCP443(v bool) {
|
||||
|
||||
// SetDERPMap controls which (if any) DERP servers are used.
|
||||
// A nil value means to disable DERP; it's disabled by default.
|
||||
//
|
||||
// SetDERPMap triggers a ReSTUN after updating the map. Callers that want to
|
||||
// set the map without triggering a ReSTUN should use [Conn.SetDERPMapWithoutReSTUN]
|
||||
// instead.
|
||||
func (c *Conn) SetDERPMap(dm *tailcfg.DERPMap) {
|
||||
c.setDERPMap(dm, true)
|
||||
}
|
||||
|
||||
// SetDERPMapWithoutReSTUN is like [Conn.SetDERPMap] but does not trigger a
|
||||
// ReSTUN after updating the map.
|
||||
//
|
||||
// It is used for setting the map from a cache, so the homeDERP can be set
|
||||
// from cache before any STUN happens.
|
||||
func (c *Conn) SetDERPMapWithoutReSTUN(dm *tailcfg.DERPMap) {
|
||||
c.setDERPMap(dm, false)
|
||||
}
|
||||
|
||||
func (c *Conn) setDERPMap(dm *tailcfg.DERPMap, doReStun bool) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
@@ -760,7 +824,7 @@ func (c *Conn) SetDERPMap(dm *tailcfg.DERPMap) {
|
||||
}
|
||||
dm = &tailcfg.DERPMap{
|
||||
OmitDefaultRegions: true,
|
||||
Regions: map[int]*tailcfg.DERPRegion{
|
||||
Regions: map[tailcfg.DERPRegionID]*tailcfg.DERPRegion{
|
||||
999: {
|
||||
RegionID: 999,
|
||||
Nodes: []*tailcfg.DERPNode{{
|
||||
@@ -804,8 +868,14 @@ func (c *Conn) SetDERPMap(dm *tailcfg.DERPMap) {
|
||||
}
|
||||
}
|
||||
|
||||
go c.ReSTUN("derp-map-update")
|
||||
if doReStun {
|
||||
if reSTUNHookForTests != nil {
|
||||
reSTUNHookForTests("derp-map-update")
|
||||
}
|
||||
go c.ReSTUN("derp-map-update")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Conn) wantDerpLocked() bool { return c.derpMap != nil }
|
||||
|
||||
// c.mu must be held.
|
||||
@@ -871,7 +941,7 @@ func (c *Conn) maybeCloseDERPsOnRebind(okayLocalIPs []netip.Prefix) {
|
||||
// why is a reason for logging.
|
||||
//
|
||||
// c.mu must be held.
|
||||
func (c *Conn) closeOrReconnectDERPLocked(regionID int, why string) {
|
||||
func (c *Conn) closeOrReconnectDERPLocked(regionID tailcfg.DERPRegionID, why string) {
|
||||
c.closeDerpLocked(regionID, why)
|
||||
if !c.privateKey.IsZero() && c.myDerp == regionID {
|
||||
c.startDerpHomeConnectLocked()
|
||||
@@ -880,7 +950,7 @@ func (c *Conn) closeOrReconnectDERPLocked(regionID int, why string) {
|
||||
|
||||
// c.mu must be held.
|
||||
// It is the responsibility of the caller to call logActiveDerpLocked after any set of closes.
|
||||
func (c *Conn) closeDerpLocked(regionID int, why string) {
|
||||
func (c *Conn) closeDerpLocked(regionID tailcfg.DERPRegionID, why string) {
|
||||
if ad, ok := c.activeDerp[regionID]; ok {
|
||||
c.logf("magicsock: closing connection to derp-%v (%v), age %v", regionID, why, time.Since(ad.createTime).Round(time.Second))
|
||||
go ad.c.Close()
|
||||
@@ -898,14 +968,14 @@ func (c *Conn) logActiveDerpLocked() {
|
||||
return
|
||||
}
|
||||
buf.WriteString(":")
|
||||
c.foreachActiveDerpSortedLocked(func(node int, ad activeDerp) {
|
||||
c.foreachActiveDerpSortedLocked(func(node tailcfg.DERPRegionID, ad activeDerp) {
|
||||
fmt.Fprintf(buf, " derp-%d=cr%v,wr%v", node, simpleDur(now.Sub(ad.createTime)), simpleDur(now.Sub(*ad.lastWrite)))
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
// c.mu must be held.
|
||||
func (c *Conn) foreachActiveDerpSortedLocked(fn func(regionID int, ad activeDerp)) {
|
||||
func (c *Conn) foreachActiveDerpSortedLocked(fn func(regionID tailcfg.DERPRegionID, ad activeDerp)) {
|
||||
if len(c.activeDerp) < 2 {
|
||||
for id, ad := range c.activeDerp {
|
||||
fn(id, ad)
|
||||
@@ -971,7 +1041,7 @@ func (c *Conn) DERPs() int {
|
||||
return len(c.activeDerp)
|
||||
}
|
||||
|
||||
func (c *Conn) derpRegionCodeOfIDLocked(regionID int) string {
|
||||
func (c *Conn) derpRegionCodeOfIDLocked(regionID tailcfg.DERPRegionID) string {
|
||||
if c.derpMap == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user