wgengine, wgengine/filter: minor doc, style, performance, locking changes

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick 2020-03-25 08:40:36 -07:00
parent 6284454ae5
commit 2e420ad8b6
3 changed files with 29 additions and 10 deletions

View File

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Package filter contains a stateful packet filter.
package filter package filter
import ( import (
@ -17,14 +18,17 @@ import (
type filterState struct { type filterState struct {
mu sync.Mutex mu sync.Mutex
lru *lru.Cache lru *lru.Cache // of tuple
} }
// Filter is a stateful packet filter.
type Filter struct { type Filter struct {
matches Matches matches Matches
state *filterState state *filterState
} }
// Response is a verdict: either a Drop, Accept, or noVerdict skip to
// continue processing.
type Response int type Response int
const ( const (
@ -46,6 +50,7 @@ func (r Response) String() string {
} }
} }
// RunFlags controls the filter's debug log verbosity at runtime.
type RunFlags int type RunFlags int
const ( const (
@ -62,27 +67,34 @@ type tuple struct {
DstPort uint16 DstPort uint16
} }
const LRU_MAX = 512 // max entries in UDP LRU cache const lruMax = 512 // max entries in UDP LRU cache
// MatchAllowAll matches all packets.
var MatchAllowAll = Matches{ var MatchAllowAll = Matches{
Match{[]IPPortRange{IPPortRangeAny}, []IP{IPAny}}, Match{[]IPPortRange{IPPortRangeAny}, []IP{IPAny}},
} }
// NewAllowAll returns a packet filter that accepts everything.
func NewAllowAll() *Filter { func NewAllowAll() *Filter {
return New(MatchAllowAll, nil) return New(MatchAllowAll, nil)
} }
// NewAllowNone returns a packet filter that rejects everything.
func NewAllowNone() *Filter { func NewAllowNone() *Filter {
return New(nil, nil) return New(nil, nil)
} }
// New creates a new packet Filter with the given Matches rules.
// If shareStateWith is non-nil, the returned filter shares state
// with the previous one, to enable rules to be changed at runtime
// without breaking existing flows.
func New(matches Matches, shareStateWith *Filter) *Filter { func New(matches Matches, shareStateWith *Filter) *Filter {
var state *filterState var state *filterState
if shareStateWith != nil { if shareStateWith != nil {
state = shareStateWith.state state = shareStateWith.state
} else { } else {
state = &filterState{ state = &filterState{
lru: lru.New(LRU_MAX), lru: lru.New(lruMax),
} }
} }
f := &Filter{ f := &Filter{
@ -200,9 +212,10 @@ func (f *Filter) runOut(q *packet.QDecode) (r Response, why string) {
// TODO(apenwarr): create sessions on ICMP Echo Request too. // TODO(apenwarr): create sessions on ICMP Echo Request too.
if q.IPProto == packet.UDP { if q.IPProto == packet.UDP {
t := tuple{q.DstIP, q.SrcIP, q.DstPort, q.SrcPort} t := tuple{q.DstIP, q.SrcIP, q.DstPort, q.SrcPort}
var ti interface{} = t // allocate once, rather than twice inside mutex
f.state.mu.Lock() f.state.mu.Lock()
f.state.lru.Add(t, t) f.state.lru.Add(ti, ti)
f.state.mu.Unlock() f.state.mu.Unlock()
} }
return Accept, "ok out" return Accept, "ok out"

View File

@ -36,19 +36,21 @@ type userspaceEngine struct {
router Router router Router
magicConn *magicsock.Conn magicConn *magicsock.Conn
linkMon *monitor.Mon linkMon *monitor.Mon
filt *filter.Filter
wgLock sync.Mutex // serializes all wgdev operations wgLock sync.Mutex // serializes all wgdev operations; see lock order comment below
lastReconfig string lastReconfig string
lastCfg wgcfg.Config lastCfg wgcfg.Config
lastRoutes string lastRoutes string
mu sync.Mutex mu sync.Mutex // guards following; see lock order comment below
filt *filter.Filter
statusCallback StatusCallback statusCallback StatusCallback
peerSequence []wgcfg.Key peerSequence []wgcfg.Key
endpoints []string endpoints []string
pingers map[wgcfg.Key]context.CancelFunc // mu must be held to call CancelFunc pingers map[wgcfg.Key]context.CancelFunc // mu must be held to call CancelFunc
linkState *interfaces.State linkState *interfaces.State
// Lock ordering: wgLock, then mu.
} }
type Loggify struct { type Loggify struct {
@ -382,12 +384,12 @@ func (e *userspaceEngine) Reconfig(cfg *wgcfg.Config, dnsDomains []string) error
} }
func (e *userspaceEngine) GetFilter() *filter.Filter { func (e *userspaceEngine) GetFilter() *filter.Filter {
e.mu.Lock()
defer e.mu.Unlock()
return e.filt return e.filt
} }
func (e *userspaceEngine) SetFilter(filt *filter.Filter) { func (e *userspaceEngine) SetFilter(filt *filter.Filter) {
e.filt = filt
var filtin, filtout func(b []byte) device.FilterResult var filtin, filtout func(b []byte) device.FilterResult
if filt == nil { if filt == nil {
e.logf("wgengine: nil filter provided; no access restrictions.\n") e.logf("wgengine: nil filter provided; no access restrictions.\n")
@ -429,6 +431,10 @@ func (e *userspaceEngine) SetFilter(filt *filter.Filter) {
defer e.wgLock.Unlock() defer e.wgLock.Unlock()
e.wgdev.SetFilterInOut(filtin, filtout) e.wgdev.SetFilterInOut(filtin, filtout)
e.mu.Lock()
e.filt = filt
e.mu.Unlock()
} }
func (e *userspaceEngine) SetStatusCallback(cb StatusCallback) { func (e *userspaceEngine) SetStatusCallback(cb StatusCallback) {

View File

@ -101,7 +101,7 @@ type Engine interface {
// sends an updated network map. // sends an updated network map.
Reconfig(cfg *wgcfg.Config, dnsDomains []string) error Reconfig(cfg *wgcfg.Config, dnsDomains []string) error
// GetFilter returns the current packet filter, if any // GetFilter returns the current packet filter, if any.
GetFilter() *filter.Filter GetFilter() *filter.Filter
// SetFilter updates the packet filter. // SetFilter updates the packet filter.