derp{,/derphttp},magicsock: tell DERP server when ping acks can be expected

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
bradfitz/derp_steer
Brad Fitzpatrick 4 years ago committed by Brad Fitzpatrick
parent f9f3b67f3a
commit c81814e4f8

@ -21,13 +21,14 @@ import (
// Client is a DERP client. // Client is a DERP client.
type Client struct { type Client struct {
serverKey key.Public // of the DERP server; not a machine or node key serverKey key.Public // of the DERP server; not a machine or node key
privateKey key.Private privateKey key.Private
publicKey key.Public // of privateKey publicKey key.Public // of privateKey
logf logger.Logf logf logger.Logf
nc Conn nc Conn
br *bufio.Reader br *bufio.Reader
meshKey string meshKey string
canAckPings bool
wmu sync.Mutex // hold while writing to bw wmu sync.Mutex // hold while writing to bw
bw *bufio.Writer bw *bufio.Writer
@ -48,8 +49,9 @@ func (f clientOptFunc) update(o *clientOpt) { f(o) }
// clientOpt are the options passed to newClient. // clientOpt are the options passed to newClient.
type clientOpt struct { type clientOpt struct {
MeshKey string MeshKey string
ServerPub key.Public ServerPub key.Public
CanAckPings bool
} }
// MeshKey returns a ClientOpt to pass to the DERP server during connect to get // MeshKey returns a ClientOpt to pass to the DERP server during connect to get
@ -64,6 +66,12 @@ func ServerPublicKey(key key.Public) ClientOpt {
return clientOptFunc(func(o *clientOpt) { o.ServerPub = key }) return clientOptFunc(func(o *clientOpt) { o.ServerPub = key })
} }
// CanAckPings returns a ClientOpt to set whether it advertises to the
// server that it's capable of acknowledging ping requests.
func CanAckPings(v bool) ClientOpt {
return clientOptFunc(func(o *clientOpt) { o.CanAckPings = v })
}
func NewClient(privateKey key.Private, nc Conn, brw *bufio.ReadWriter, logf logger.Logf, opts ...ClientOpt) (*Client, error) { func NewClient(privateKey key.Private, nc Conn, brw *bufio.ReadWriter, logf logger.Logf, opts ...ClientOpt) (*Client, error) {
var opt clientOpt var opt clientOpt
for _, o := range opts { for _, o := range opts {
@ -77,13 +85,14 @@ func NewClient(privateKey key.Private, nc Conn, brw *bufio.ReadWriter, logf logg
func newClient(privateKey key.Private, nc Conn, brw *bufio.ReadWriter, logf logger.Logf, opt clientOpt) (*Client, error) { func newClient(privateKey key.Private, nc Conn, brw *bufio.ReadWriter, logf logger.Logf, opt clientOpt) (*Client, error) {
c := &Client{ c := &Client{
privateKey: privateKey, privateKey: privateKey,
publicKey: privateKey.Public(), publicKey: privateKey.Public(),
logf: logf, logf: logf,
nc: nc, nc: nc,
br: brw.Reader, br: brw.Reader,
bw: brw.Writer, bw: brw.Writer,
meshKey: opt.MeshKey, meshKey: opt.MeshKey,
canAckPings: opt.CanAckPings,
} }
if opt.ServerPub.IsZero() { if opt.ServerPub.IsZero() {
if err := c.recvServerKey(); err != nil { if err := c.recvServerKey(); err != nil {
@ -147,6 +156,10 @@ type clientInfo struct {
// connection list & forward packets. It's empty for regular // connection list & forward packets. It's empty for regular
// users. // users.
MeshKey string `json:"meshKey,omitempty"` MeshKey string `json:"meshKey,omitempty"`
// CanAckPings is whether the client declares it's able to ack
// pings.
CanAckPings bool
} }
func (c *Client) sendClientKey() error { func (c *Client) sendClientKey() error {
@ -155,8 +168,9 @@ func (c *Client) sendClientKey() error {
return err return err
} }
msg, err := json.Marshal(clientInfo{ msg, err := json.Marshal(clientInfo{
Version: ProtocolVersion, Version: ProtocolVersion,
MeshKey: c.meshKey, MeshKey: c.meshKey,
CanAckPings: c.canAckPings,
}) })
if err != nil { if err != nil {
return err return err

@ -63,6 +63,7 @@ type Client struct {
mu sync.Mutex mu sync.Mutex
preferred bool preferred bool
canAckPings bool
closed bool closed bool
netConn io.Closer netConn io.Closer
client *derp.Client client *derp.Client
@ -333,7 +334,11 @@ func (c *Client) connect(ctx context.Context, caller string) (client *derp.Clien
return nil, 0, fmt.Errorf("GET failed: %v: %s", err, b) return nil, 0, fmt.Errorf("GET failed: %v: %s", err, b)
} }
} }
derpClient, err = derp.NewClient(c.privateKey, httpConn, brw, c.logf, derp.MeshKey(c.MeshKey), derp.ServerPublicKey(serverPub)) derpClient, err = derp.NewClient(c.privateKey, httpConn, brw, c.logf,
derp.MeshKey(c.MeshKey),
derp.ServerPublicKey(serverPub),
derp.CanAckPings(c.canAckPings),
)
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }
@ -665,6 +670,15 @@ func (c *Client) SendPong(data [8]byte) error {
return dc.SendPong(data) return dc.SendPong(data)
} }
// SetCanAckPings sets whether this client will reply to ping requests from the server.
//
// This only affects future connections.
func (c *Client) SetCanAckPings(v bool) {
c.mu.Lock()
defer c.mu.Unlock()
c.canAckPings = v
}
// NotePreferred notes whether this Client is the caller's preferred // NotePreferred notes whether this Client is the caller's preferred
// (home) DERP node. It's only used for stats. // (home) DERP node. It's only used for stats.
func (c *Client) NotePreferred(v bool) { func (c *Client) NotePreferred(v bool) {

@ -1308,6 +1308,7 @@ func (c *Conn) derpWriteChanOfAddr(addr netaddr.IPPort, peer key.Public) chan<-
return c.derpMap.Regions[regionID] return c.derpMap.Regions[regionID]
}) })
dc.SetCanAckPings(true)
dc.NotePreferred(c.myDerp == regionID) dc.NotePreferred(c.myDerp == regionID)
dc.DNSCache = dnscache.Get() dc.DNSCache = dnscache.Get()

Loading…
Cancel
Save