tailscale/wgengine/filter/match.go

175 lines
3.1 KiB
Go
Raw Normal View History

// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package filter
import (
"fmt"
"math/bits"
"net"
"strings"
controlclinet: clone filter.MatchAllowAll This avoids a non-obvious data race, where the JSON decoder ends up creating do-nothing writes into global variables. ================== WARNING: DATA RACE Write at 0x0000011e1860 by goroutine 201: tailscale.com/wgengine/packet.(*IP).UnmarshalJSON() /home/crawshaw/repo/corp/oss/wgengine/packet/packet.go:83 +0x2d9 encoding/json.(*decodeState).literalStore() /home/crawshaw/go/go/src/encoding/json/decode.go:877 +0x445e ... encoding/json.Unmarshal() /home/crawshaw/go/go/src/encoding/json/decode.go:107 +0x1de tailscale.com/control/controlclient.(*Direct).decodeMsg() /home/crawshaw/repo/corp/oss/control/controlclient/direct.go:615 +0x1ab tailscale.com/control/controlclient.(*Direct).PollNetMap() /home/crawshaw/repo/corp/oss/control/controlclient/direct.go:525 +0x1053 tailscale.com/control/controlclient.(*Client).mapRoutine() /home/crawshaw/repo/corp/oss/control/controlclient/auto.go:428 +0x3a6 Previous read at 0x0000011e1860 by goroutine 86: tailscale.com/wgengine/filter.matchIPWithoutPorts() /home/crawshaw/repo/corp/oss/wgengine/filter/match.go:108 +0x91 tailscale.com/wgengine/filter.(*Filter).runIn() /home/crawshaw/repo/corp/oss/wgengine/filter/filter.go:147 +0x3c6 tailscale.com/wgengine/filter.(*Filter).RunIn() /home/crawshaw/repo/corp/oss/wgengine/filter/filter.go:127 +0xb0 tailscale.com/wgengine.(*userspaceEngine).SetFilter.func1() /home/crawshaw/repo/corp/oss/wgengine/userspace.go:390 +0xfc github.com/tailscale/wireguard-go/device.(*Device).RoutineDecryption() /home/crawshaw/repo/corp/wireguard-go/device/receive.go:295 +0xa1f For #112 Signed-off-by: David Crawshaw <crawshaw@tailscale.com>
2020-02-29 03:27:17 +00:00
"tailscale.com/wgengine/packet"
)
type IP = packet.IP
func NewIP(ip net.IP) IP {
return packet.NewIP(ip)
}
type Net struct {
IP IP
Mask IP
}
func (n Net) Includes(ip IP) bool {
return (n.IP & n.Mask) == (ip & n.Mask)
}
func (n Net) Bits() int {
return 32 - bits.TrailingZeros32(uint32(n.Mask))
}
func (n Net) String() string {
b := n.Bits()
if b == 32 {
return n.IP.String()
} else if b == 0 {
return "*"
} else {
return fmt.Sprintf("%s/%d", n.IP, b)
}
}
var NetAny = Net{0, 0}
var NetNone = Net{^IP(0), ^IP(0)}
func Netmask(bits int) IP {
var b uint32
b = ^uint32((1 << (32 - bits)) - 1)
return IP(b)
}
type PortRange struct {
First, Last uint16
}
var PortRangeAny = PortRange{0, 65535}
func (pr PortRange) String() string {
if pr.First == 0 && pr.Last == 65535 {
return "*"
} else if pr.First == pr.Last {
return fmt.Sprintf("%d", pr.First)
} else {
return fmt.Sprintf("%d-%d", pr.First, pr.Last)
}
}
type NetPortRange struct {
Net Net
Ports PortRange
}
var NetPortRangeAny = NetPortRange{NetAny, PortRangeAny}
func (ipr NetPortRange) String() string {
return fmt.Sprintf("%v:%v", ipr.Net, ipr.Ports)
}
type Match struct {
Dsts []NetPortRange
Srcs []Net
}
controlclinet: clone filter.MatchAllowAll This avoids a non-obvious data race, where the JSON decoder ends up creating do-nothing writes into global variables. ================== WARNING: DATA RACE Write at 0x0000011e1860 by goroutine 201: tailscale.com/wgengine/packet.(*IP).UnmarshalJSON() /home/crawshaw/repo/corp/oss/wgengine/packet/packet.go:83 +0x2d9 encoding/json.(*decodeState).literalStore() /home/crawshaw/go/go/src/encoding/json/decode.go:877 +0x445e ... encoding/json.Unmarshal() /home/crawshaw/go/go/src/encoding/json/decode.go:107 +0x1de tailscale.com/control/controlclient.(*Direct).decodeMsg() /home/crawshaw/repo/corp/oss/control/controlclient/direct.go:615 +0x1ab tailscale.com/control/controlclient.(*Direct).PollNetMap() /home/crawshaw/repo/corp/oss/control/controlclient/direct.go:525 +0x1053 tailscale.com/control/controlclient.(*Client).mapRoutine() /home/crawshaw/repo/corp/oss/control/controlclient/auto.go:428 +0x3a6 Previous read at 0x0000011e1860 by goroutine 86: tailscale.com/wgengine/filter.matchIPWithoutPorts() /home/crawshaw/repo/corp/oss/wgengine/filter/match.go:108 +0x91 tailscale.com/wgengine/filter.(*Filter).runIn() /home/crawshaw/repo/corp/oss/wgengine/filter/filter.go:147 +0x3c6 tailscale.com/wgengine/filter.(*Filter).RunIn() /home/crawshaw/repo/corp/oss/wgengine/filter/filter.go:127 +0xb0 tailscale.com/wgengine.(*userspaceEngine).SetFilter.func1() /home/crawshaw/repo/corp/oss/wgengine/userspace.go:390 +0xfc github.com/tailscale/wireguard-go/device.(*Device).RoutineDecryption() /home/crawshaw/repo/corp/wireguard-go/device/receive.go:295 +0xa1f For #112 Signed-off-by: David Crawshaw <crawshaw@tailscale.com>
2020-02-29 03:27:17 +00:00
func (m Match) Clone() (res Match) {
if m.Dsts != nil {
res.Dsts = append([]NetPortRange{}, m.Dsts...)
controlclinet: clone filter.MatchAllowAll This avoids a non-obvious data race, where the JSON decoder ends up creating do-nothing writes into global variables. ================== WARNING: DATA RACE Write at 0x0000011e1860 by goroutine 201: tailscale.com/wgengine/packet.(*IP).UnmarshalJSON() /home/crawshaw/repo/corp/oss/wgengine/packet/packet.go:83 +0x2d9 encoding/json.(*decodeState).literalStore() /home/crawshaw/go/go/src/encoding/json/decode.go:877 +0x445e ... encoding/json.Unmarshal() /home/crawshaw/go/go/src/encoding/json/decode.go:107 +0x1de tailscale.com/control/controlclient.(*Direct).decodeMsg() /home/crawshaw/repo/corp/oss/control/controlclient/direct.go:615 +0x1ab tailscale.com/control/controlclient.(*Direct).PollNetMap() /home/crawshaw/repo/corp/oss/control/controlclient/direct.go:525 +0x1053 tailscale.com/control/controlclient.(*Client).mapRoutine() /home/crawshaw/repo/corp/oss/control/controlclient/auto.go:428 +0x3a6 Previous read at 0x0000011e1860 by goroutine 86: tailscale.com/wgengine/filter.matchIPWithoutPorts() /home/crawshaw/repo/corp/oss/wgengine/filter/match.go:108 +0x91 tailscale.com/wgengine/filter.(*Filter).runIn() /home/crawshaw/repo/corp/oss/wgengine/filter/filter.go:147 +0x3c6 tailscale.com/wgengine/filter.(*Filter).RunIn() /home/crawshaw/repo/corp/oss/wgengine/filter/filter.go:127 +0xb0 tailscale.com/wgengine.(*userspaceEngine).SetFilter.func1() /home/crawshaw/repo/corp/oss/wgengine/userspace.go:390 +0xfc github.com/tailscale/wireguard-go/device.(*Device).RoutineDecryption() /home/crawshaw/repo/corp/wireguard-go/device/receive.go:295 +0xa1f For #112 Signed-off-by: David Crawshaw <crawshaw@tailscale.com>
2020-02-29 03:27:17 +00:00
}
if m.Srcs != nil {
res.Srcs = append([]Net{}, m.Srcs...)
controlclinet: clone filter.MatchAllowAll This avoids a non-obvious data race, where the JSON decoder ends up creating do-nothing writes into global variables. ================== WARNING: DATA RACE Write at 0x0000011e1860 by goroutine 201: tailscale.com/wgengine/packet.(*IP).UnmarshalJSON() /home/crawshaw/repo/corp/oss/wgengine/packet/packet.go:83 +0x2d9 encoding/json.(*decodeState).literalStore() /home/crawshaw/go/go/src/encoding/json/decode.go:877 +0x445e ... encoding/json.Unmarshal() /home/crawshaw/go/go/src/encoding/json/decode.go:107 +0x1de tailscale.com/control/controlclient.(*Direct).decodeMsg() /home/crawshaw/repo/corp/oss/control/controlclient/direct.go:615 +0x1ab tailscale.com/control/controlclient.(*Direct).PollNetMap() /home/crawshaw/repo/corp/oss/control/controlclient/direct.go:525 +0x1053 tailscale.com/control/controlclient.(*Client).mapRoutine() /home/crawshaw/repo/corp/oss/control/controlclient/auto.go:428 +0x3a6 Previous read at 0x0000011e1860 by goroutine 86: tailscale.com/wgengine/filter.matchIPWithoutPorts() /home/crawshaw/repo/corp/oss/wgengine/filter/match.go:108 +0x91 tailscale.com/wgengine/filter.(*Filter).runIn() /home/crawshaw/repo/corp/oss/wgengine/filter/filter.go:147 +0x3c6 tailscale.com/wgengine/filter.(*Filter).RunIn() /home/crawshaw/repo/corp/oss/wgengine/filter/filter.go:127 +0xb0 tailscale.com/wgengine.(*userspaceEngine).SetFilter.func1() /home/crawshaw/repo/corp/oss/wgengine/userspace.go:390 +0xfc github.com/tailscale/wireguard-go/device.(*Device).RoutineDecryption() /home/crawshaw/repo/corp/wireguard-go/device/receive.go:295 +0xa1f For #112 Signed-off-by: David Crawshaw <crawshaw@tailscale.com>
2020-02-29 03:27:17 +00:00
}
return res
}
func (m Match) String() string {
srcs := []string{}
for _, src := range m.Srcs {
srcs = append(srcs, src.String())
}
dsts := []string{}
for _, dst := range m.Dsts {
dsts = append(dsts, dst.String())
}
var ss, ds string
if len(srcs) == 1 {
ss = srcs[0]
} else {
ss = "[" + strings.Join(srcs, ",") + "]"
}
if len(dsts) == 1 {
ds = dsts[0]
} else {
ds = "[" + strings.Join(dsts, ",") + "]"
}
return fmt.Sprintf("%v=>%v", ss, ds)
}
type Matches []Match
controlclinet: clone filter.MatchAllowAll This avoids a non-obvious data race, where the JSON decoder ends up creating do-nothing writes into global variables. ================== WARNING: DATA RACE Write at 0x0000011e1860 by goroutine 201: tailscale.com/wgengine/packet.(*IP).UnmarshalJSON() /home/crawshaw/repo/corp/oss/wgengine/packet/packet.go:83 +0x2d9 encoding/json.(*decodeState).literalStore() /home/crawshaw/go/go/src/encoding/json/decode.go:877 +0x445e ... encoding/json.Unmarshal() /home/crawshaw/go/go/src/encoding/json/decode.go:107 +0x1de tailscale.com/control/controlclient.(*Direct).decodeMsg() /home/crawshaw/repo/corp/oss/control/controlclient/direct.go:615 +0x1ab tailscale.com/control/controlclient.(*Direct).PollNetMap() /home/crawshaw/repo/corp/oss/control/controlclient/direct.go:525 +0x1053 tailscale.com/control/controlclient.(*Client).mapRoutine() /home/crawshaw/repo/corp/oss/control/controlclient/auto.go:428 +0x3a6 Previous read at 0x0000011e1860 by goroutine 86: tailscale.com/wgengine/filter.matchIPWithoutPorts() /home/crawshaw/repo/corp/oss/wgengine/filter/match.go:108 +0x91 tailscale.com/wgengine/filter.(*Filter).runIn() /home/crawshaw/repo/corp/oss/wgengine/filter/filter.go:147 +0x3c6 tailscale.com/wgengine/filter.(*Filter).RunIn() /home/crawshaw/repo/corp/oss/wgengine/filter/filter.go:127 +0xb0 tailscale.com/wgengine.(*userspaceEngine).SetFilter.func1() /home/crawshaw/repo/corp/oss/wgengine/userspace.go:390 +0xfc github.com/tailscale/wireguard-go/device.(*Device).RoutineDecryption() /home/crawshaw/repo/corp/wireguard-go/device/receive.go:295 +0xa1f For #112 Signed-off-by: David Crawshaw <crawshaw@tailscale.com>
2020-02-29 03:27:17 +00:00
func (m Matches) Clone() (res Matches) {
for _, match := range m {
res = append(res, match.Clone())
}
return res
}
func ipInList(ip IP, netlist []Net) bool {
for _, net := range netlist {
if net.Includes(ip) {
return true
}
}
return false
}
func matchIPPorts(mm Matches, q *packet.QDecode) bool {
for _, acl := range mm {
for _, dst := range acl.Dsts {
if !dst.Net.Includes(q.DstIP) {
continue
}
if q.DstPort < dst.Ports.First || q.DstPort > dst.Ports.Last {
continue
}
if !ipInList(q.SrcIP, acl.Srcs) {
// Skip other dests in this acl, since
// the src will never match.
break
}
return true
}
}
return false
}
func matchIPWithoutPorts(mm Matches, q *packet.QDecode) bool {
for _, acl := range mm {
for _, dst := range acl.Dsts {
if !dst.Net.Includes(q.DstIP) {
continue
}
if !ipInList(q.SrcIP, acl.Srcs) {
// Skip other dests in this acl, since
// the src will never match.
break
}
return true
}
}
return false
}