mirror of
https://github.com/tailscale/tailscale.git
synced 2025-03-12 00:11:46 +00:00
derp: add PeerPresentFlags bitmask to Watch messages
Updates tailscale/corp#17816 Change-Id: Ib5baf6c981a6a4c279f8bbfef02048cfbfb3323b Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
parent
d7a4f9d31c
commit
5ffb2668ef
26
derp/derp.go
26
derp/derp.go
@ -83,9 +83,16 @@ const (
|
|||||||
// a bug).
|
// a bug).
|
||||||
framePeerGone = frameType(0x08) // 32B pub key of peer that's gone + 1 byte reason
|
framePeerGone = frameType(0x08) // 32B pub key of peer that's gone + 1 byte reason
|
||||||
|
|
||||||
// framePeerPresent is like framePeerGone, but for other
|
// framePeerPresent is like framePeerGone, but for other members of the DERP
|
||||||
// members of the DERP region when they're meshed up together.
|
// region when they're meshed up together.
|
||||||
framePeerPresent = frameType(0x09) // 32B pub key of peer that's connected + optional 18B ip:port (16 byte IP + 2 byte BE uint16 port)
|
//
|
||||||
|
// The message is at least 32 bytes (the public key of the peer that's
|
||||||
|
// connected). If there are at least 18 bytes remaining after that, it's the
|
||||||
|
// 16 byte IP + 2 byte BE uint16 port of the client. If there's another byte
|
||||||
|
// remaining after that, it's a PeerPresentFlags byte.
|
||||||
|
// While current servers send 41 bytes, old servers will send fewer, and newer
|
||||||
|
// servers might send more.
|
||||||
|
framePeerPresent = frameType(0x09)
|
||||||
|
|
||||||
// frameWatchConns is how one DERP node in a regional mesh
|
// frameWatchConns is how one DERP node in a regional mesh
|
||||||
// subscribes to the others in the region.
|
// subscribes to the others in the region.
|
||||||
@ -128,6 +135,19 @@ const (
|
|||||||
PeerGoneReasonNotHere = PeerGoneReasonType(0x01) // server doesn't know about this peer, unexpected
|
PeerGoneReasonNotHere = PeerGoneReasonType(0x01) // server doesn't know about this peer, unexpected
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// PeerPresentFlags is an optional byte of bit flags sent after a framePeerPresent message.
|
||||||
|
//
|
||||||
|
// For a modern server, the value should always be non-zero. If the value is zero,
|
||||||
|
// that means the server doesn't support this field.
|
||||||
|
type PeerPresentFlags byte
|
||||||
|
|
||||||
|
// PeerPresentFlags bits.
|
||||||
|
const (
|
||||||
|
PeerPresentIsRegular = 1 << 0
|
||||||
|
PeerPresentIsMeshPeer = 1 << 1
|
||||||
|
PeerPresentIsProber = 1 << 2
|
||||||
|
)
|
||||||
|
|
||||||
var bin = binary.BigEndian
|
var bin = binary.BigEndian
|
||||||
|
|
||||||
func writeUint32(bw *bufio.Writer, v uint32) error {
|
func writeUint32(bw *bufio.Writer, v uint32) error {
|
||||||
|
@ -368,6 +368,8 @@ type PeerPresentMessage struct {
|
|||||||
Key key.NodePublic
|
Key key.NodePublic
|
||||||
// IPPort is the remote IP and port of the client.
|
// IPPort is the remote IP and port of the client.
|
||||||
IPPort netip.AddrPort
|
IPPort netip.AddrPort
|
||||||
|
// Flags is a bitmask of info about the client.
|
||||||
|
Flags PeerPresentFlags
|
||||||
}
|
}
|
||||||
|
|
||||||
func (PeerPresentMessage) msg() {}
|
func (PeerPresentMessage) msg() {}
|
||||||
@ -547,18 +549,33 @@ func (c *Client) recvTimeout(timeout time.Duration) (m ReceivedMessage, err erro
|
|||||||
return pg, nil
|
return pg, nil
|
||||||
|
|
||||||
case framePeerPresent:
|
case framePeerPresent:
|
||||||
if n < keyLen {
|
remain := b
|
||||||
|
chunk, remain, ok := cutLeadingN(remain, keyLen)
|
||||||
|
if !ok {
|
||||||
c.logf("[unexpected] dropping short peerPresent frame from DERP server")
|
c.logf("[unexpected] dropping short peerPresent frame from DERP server")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var msg PeerPresentMessage
|
var msg PeerPresentMessage
|
||||||
msg.Key = key.NodePublicFromRaw32(mem.B(b[:keyLen]))
|
msg.Key = key.NodePublicFromRaw32(mem.B(chunk))
|
||||||
if n >= keyLen+16+2 {
|
|
||||||
msg.IPPort = netip.AddrPortFrom(
|
const ipLen = 16
|
||||||
netip.AddrFrom16([16]byte(b[keyLen:keyLen+16])).Unmap(),
|
const portLen = 2
|
||||||
binary.BigEndian.Uint16(b[keyLen+16:keyLen+16+2]),
|
chunk, remain, ok = cutLeadingN(remain, ipLen+portLen)
|
||||||
)
|
if !ok {
|
||||||
|
// Older server which didn't send the IP.
|
||||||
|
return msg, nil
|
||||||
}
|
}
|
||||||
|
msg.IPPort = netip.AddrPortFrom(
|
||||||
|
netip.AddrFrom16([16]byte(chunk[:ipLen])).Unmap(),
|
||||||
|
binary.BigEndian.Uint16(chunk[ipLen:]),
|
||||||
|
)
|
||||||
|
|
||||||
|
chunk, _, ok = cutLeadingN(remain, 1)
|
||||||
|
if !ok {
|
||||||
|
// Older server which doesn't send PeerPresentFlags.
|
||||||
|
return msg, nil
|
||||||
|
}
|
||||||
|
msg.Flags = PeerPresentFlags(chunk[0])
|
||||||
return msg, nil
|
return msg, nil
|
||||||
|
|
||||||
case frameRecvPacket:
|
case frameRecvPacket:
|
||||||
@ -636,3 +653,10 @@ func (c *Client) LocalAddr() (netip.AddrPort, error) {
|
|||||||
}
|
}
|
||||||
return netip.ParseAddrPort(a.String())
|
return netip.ParseAddrPort(a.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cutLeadingN(b []byte, n int) (chunk, remain []byte, ok bool) {
|
||||||
|
if len(b) >= n {
|
||||||
|
return b[:n], b[n:], true
|
||||||
|
}
|
||||||
|
return nil, b, false
|
||||||
|
}
|
||||||
|
@ -566,7 +566,7 @@ func (s *Server) registerClient(c *sclient) {
|
|||||||
}
|
}
|
||||||
s.keyOfAddr[c.remoteIPPort] = c.key
|
s.keyOfAddr[c.remoteIPPort] = c.key
|
||||||
s.curClients.Add(1)
|
s.curClients.Add(1)
|
||||||
s.broadcastPeerStateChangeLocked(c.key, c.remoteIPPort, true)
|
s.broadcastPeerStateChangeLocked(c.key, c.remoteIPPort, c.presentFlags(), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// broadcastPeerStateChangeLocked enqueues a message to all watchers
|
// broadcastPeerStateChangeLocked enqueues a message to all watchers
|
||||||
@ -574,12 +574,13 @@ func (s *Server) registerClient(c *sclient) {
|
|||||||
// presence changed.
|
// presence changed.
|
||||||
//
|
//
|
||||||
// s.mu must be held.
|
// s.mu must be held.
|
||||||
func (s *Server) broadcastPeerStateChangeLocked(peer key.NodePublic, ipPort netip.AddrPort, present bool) {
|
func (s *Server) broadcastPeerStateChangeLocked(peer key.NodePublic, ipPort netip.AddrPort, flags PeerPresentFlags, present bool) {
|
||||||
for w := range s.watchers {
|
for w := range s.watchers {
|
||||||
w.peerStateChange = append(w.peerStateChange, peerConnState{
|
w.peerStateChange = append(w.peerStateChange, peerConnState{
|
||||||
peer: peer,
|
peer: peer,
|
||||||
present: present,
|
present: present,
|
||||||
ipPort: ipPort,
|
ipPort: ipPort,
|
||||||
|
flags: flags,
|
||||||
})
|
})
|
||||||
go w.requestMeshUpdate()
|
go w.requestMeshUpdate()
|
||||||
}
|
}
|
||||||
@ -601,7 +602,7 @@ func (s *Server) unregisterClient(c *sclient) {
|
|||||||
delete(s.clientsMesh, c.key)
|
delete(s.clientsMesh, c.key)
|
||||||
s.notePeerGoneFromRegionLocked(c.key)
|
s.notePeerGoneFromRegionLocked(c.key)
|
||||||
}
|
}
|
||||||
s.broadcastPeerStateChangeLocked(c.key, netip.AddrPort{}, false)
|
s.broadcastPeerStateChangeLocked(c.key, netip.AddrPort{}, 0, false)
|
||||||
case *dupClientSet:
|
case *dupClientSet:
|
||||||
c.debugLogf("removed duplicate client")
|
c.debugLogf("removed duplicate client")
|
||||||
if set.removeClient(c) {
|
if set.removeClient(c) {
|
||||||
@ -700,6 +701,7 @@ func (s *Server) addWatcher(c *sclient) {
|
|||||||
peer: peer,
|
peer: peer,
|
||||||
present: true,
|
present: true,
|
||||||
ipPort: ac.remoteIPPort,
|
ipPort: ac.remoteIPPort,
|
||||||
|
flags: ac.presentFlags(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1435,11 +1437,26 @@ type sclient struct {
|
|||||||
peerGoneLim *rate.Limiter
|
peerGoneLim *rate.Limiter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *sclient) presentFlags() PeerPresentFlags {
|
||||||
|
var f PeerPresentFlags
|
||||||
|
if c.info.IsProber {
|
||||||
|
f |= PeerPresentIsProber
|
||||||
|
}
|
||||||
|
if c.canMesh {
|
||||||
|
f |= PeerPresentIsMeshPeer
|
||||||
|
}
|
||||||
|
if f == 0 {
|
||||||
|
return PeerPresentIsRegular
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
// peerConnState represents whether a peer is connected to the server
|
// peerConnState represents whether a peer is connected to the server
|
||||||
// or not.
|
// or not.
|
||||||
type peerConnState struct {
|
type peerConnState struct {
|
||||||
ipPort netip.AddrPort // if present, the peer's IP:port
|
ipPort netip.AddrPort // if present, the peer's IP:port
|
||||||
peer key.NodePublic
|
peer key.NodePublic
|
||||||
|
flags PeerPresentFlags
|
||||||
present bool
|
present bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1634,9 +1651,9 @@ func (c *sclient) sendPeerGone(peer key.NodePublic, reason PeerGoneReasonType) e
|
|||||||
}
|
}
|
||||||
|
|
||||||
// sendPeerPresent sends a peerPresent frame, without flushing.
|
// sendPeerPresent sends a peerPresent frame, without flushing.
|
||||||
func (c *sclient) sendPeerPresent(peer key.NodePublic, ipPort netip.AddrPort) error {
|
func (c *sclient) sendPeerPresent(peer key.NodePublic, ipPort netip.AddrPort, flags PeerPresentFlags) error {
|
||||||
c.setWriteDeadline()
|
c.setWriteDeadline()
|
||||||
const frameLen = keyLen + 16 + 2
|
const frameLen = keyLen + 16 + 2 + 1 // 16 byte IP + 2 byte port + 1 byte flags
|
||||||
if err := writeFrameHeader(c.bw.bw(), framePeerPresent, frameLen); err != nil {
|
if err := writeFrameHeader(c.bw.bw(), framePeerPresent, frameLen); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -1645,6 +1662,7 @@ func (c *sclient) sendPeerPresent(peer key.NodePublic, ipPort netip.AddrPort) er
|
|||||||
a16 := ipPort.Addr().As16()
|
a16 := ipPort.Addr().As16()
|
||||||
copy(payload[keyLen:], a16[:])
|
copy(payload[keyLen:], a16[:])
|
||||||
binary.BigEndian.PutUint16(payload[keyLen+16:], ipPort.Port())
|
binary.BigEndian.PutUint16(payload[keyLen+16:], ipPort.Port())
|
||||||
|
payload[keyLen+18] = byte(flags)
|
||||||
_, err := c.bw.Write(payload)
|
_, err := c.bw.Write(payload)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -1675,7 +1693,7 @@ drainUpdates:
|
|||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
if pcs.present {
|
if pcs.present {
|
||||||
err = c.sendPeerPresent(pcs.peer, pcs.ipPort)
|
err = c.sendPeerPresent(pcs.peer, pcs.ipPort, pcs.flags)
|
||||||
} else {
|
} else {
|
||||||
err = c.sendPeerGone(pcs.peer, PeerGoneReasonDisconnected)
|
err = c.sendPeerGone(pcs.peer, PeerGoneReasonDisconnected)
|
||||||
}
|
}
|
||||||
|
@ -623,7 +623,13 @@ func (tc *testClient) wantPresent(t *testing.T, peers ...key.NodePublic) {
|
|||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
t.Logf("got present with IP %v", m.IPPort)
|
t.Logf("got present with IP %v, flags=%v", m.IPPort, m.Flags)
|
||||||
|
switch m.Flags {
|
||||||
|
case PeerPresentIsMeshPeer, PeerPresentIsRegular:
|
||||||
|
// Okay
|
||||||
|
default:
|
||||||
|
t.Errorf("unexpected PeerPresentIsMeshPeer flags %v", m.Flags)
|
||||||
|
}
|
||||||
delete(want, got)
|
delete(want, got)
|
||||||
if len(want) == 0 {
|
if len(want) == 0 {
|
||||||
return
|
return
|
||||||
|
Loading…
x
Reference in New Issue
Block a user