mirror of
https://github.com/tailscale/tailscale.git
synced 2024-12-01 14:05:39 +00:00
d417be6a4b
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>
140 lines
2.6 KiB
Go
140 lines
2.6 KiB
Go
// 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"
|
|
"strings"
|
|
|
|
"tailscale.com/wgengine/packet"
|
|
)
|
|
|
|
type IP = packet.IP
|
|
|
|
const IPAny = IP(0)
|
|
|
|
var NewIP = packet.NewIP
|
|
|
|
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 IPPortRange struct {
|
|
IP IP
|
|
Ports PortRange
|
|
}
|
|
|
|
var IPPortRangeAny = IPPortRange{IPAny, PortRangeAny}
|
|
|
|
func (ipr IPPortRange) String() string {
|
|
return fmt.Sprintf("%v:%v", ipr.IP, ipr.Ports)
|
|
}
|
|
|
|
type Match struct {
|
|
DstPorts []IPPortRange
|
|
SrcIPs []IP
|
|
}
|
|
|
|
func (m Match) Clone() (res Match) {
|
|
if m.DstPorts != nil {
|
|
res.DstPorts = append([]IPPortRange{}, m.DstPorts...)
|
|
}
|
|
if m.SrcIPs != nil {
|
|
res.SrcIPs = append([]IP{}, m.SrcIPs...)
|
|
}
|
|
return res
|
|
}
|
|
|
|
func (m Match) String() string {
|
|
srcs := []string{}
|
|
for _, srcip := range m.SrcIPs {
|
|
srcs = append(srcs, srcip.String())
|
|
}
|
|
dsts := []string{}
|
|
for _, dst := range m.DstPorts {
|
|
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
|
|
|
|
func (m Matches) Clone() (res Matches) {
|
|
for _, match := range m {
|
|
res = append(res, match.Clone())
|
|
}
|
|
return res
|
|
}
|
|
|
|
func ipInList(ip IP, iplist []IP) bool {
|
|
for _, ipp := range iplist {
|
|
if ipp == IPAny || ipp == ip {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func matchIPPorts(mm Matches, q *packet.QDecode) bool {
|
|
for _, acl := range mm {
|
|
for _, dst := range acl.DstPorts {
|
|
if dst.IP != IPAny && dst.IP != q.DstIP {
|
|
continue
|
|
}
|
|
if q.DstPort < dst.Ports.First || q.DstPort > dst.Ports.Last {
|
|
continue
|
|
}
|
|
if !ipInList(q.SrcIP, acl.SrcIPs) {
|
|
// 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.DstPorts {
|
|
if dst.IP != IPAny && dst.IP != q.DstIP {
|
|
continue
|
|
}
|
|
if !ipInList(q.SrcIP, acl.SrcIPs) {
|
|
// Skip other dests in this acl, since
|
|
// the src will never match.
|
|
break
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|