wgengine: configure wireguard peers lazily, as needed

wireguard-go uses 3 goroutines per peer (with reasonably large stacks
& buffers).

Rather than tell wireguard-go about all our peers, only tell it about
peers we're actively communicating with. That means we need hooks into
magicsock's packet receiving path and tstun's packet sending path to
lazily create a wireguard peer on demand from the network map.

This frees up lots of memory for iOS (where we have almost nothing
left for larger domains with many users).

We should ideally do this in wireguard-go itself one day, but that'd
be a pretty big change.

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick
2020-07-23 15:15:28 -07:00
committed by Brad Fitzpatrick
parent 5066b824a6
commit 16a9cfe2f4
4 changed files with 410 additions and 78 deletions

View File

@@ -66,6 +66,8 @@ type TUN struct {
_ [4]byte // force 64-bit alignment of following field on 32-bit
lastActivityAtomic int64 // unix seconds of last send or receive
destIPActivity atomic.Value // of map[packet.IP]func()
// buffer stores the oldest unconsumed packet from tdev.
// It is made a static buffer in order to avoid allocations.
buffer [maxBufferSize]byte
@@ -129,6 +131,14 @@ func WrapTUN(logf logger.Logf, tdev tun.Device) *TUN {
return tun
}
// SetDestIPActivityFuncs sets a map of funcs to run per packet
// destination (the map keys).
//
// The map ownership passes to the TUN. It must be non-nil.
func (t *TUN) SetDestIPActivityFuncs(m map[packet.IP]func()) {
t.destIPActivity.Store(m)
}
func (t *TUN) Close() error {
select {
case <-t.closed:
@@ -204,10 +214,7 @@ func (t *TUN) poll() {
}
}
func (t *TUN) filterOut(buf []byte) filter.Response {
p := parsedPacketPool.Get().(*packet.ParsedPacket)
defer parsedPacketPool.Put(p)
p.Decode(buf)
func (t *TUN) filterOut(p *packet.ParsedPacket) filter.Response {
if t.PreFilterOut != nil {
if t.PreFilterOut(p, t) == filter.Drop {
@@ -271,8 +278,18 @@ func (t *TUN) Read(buf []byte, offset int) (int, error) {
}
}
p := parsedPacketPool.Get().(*packet.ParsedPacket)
defer parsedPacketPool.Put(p)
p.Decode(buf[offset : offset+n])
if m, ok := t.destIPActivity.Load().(map[packet.IP]func()); ok {
if fn := m[p.DstIP]; fn != nil {
fn()
}
}
if !t.disableFilter {
response := t.filterOut(buf[offset : offset+n])
response := t.filterOut(p)
if response != filter.Accept {
// Wireguard considers read errors fatal; pretend nothing was read
return 0, nil