mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-29 04:55:31 +00:00
net/ipset, wgengine/filter/filtertype: add split-out packages
This moves NewContainsIPFunc from tsaddr to new ipset package. And wgengine/filter types gets split into wgengine/filter/filtertype, so netmap (and thus the CLI, etc) doesn't need to bring in ipset, bart, etc. Then add a test making sure the CLI deps don't regress. Updates #1278 Change-Id: Ia246d6d9502bbefbdeacc4aef1bed9c8b24f54d5 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
parent
36b1b4af2f
commit
86e0f9b912
@ -6,12 +6,10 @@ tailscale.com/cmd/derper dependencies: (generated by github.com/tailscale/depawa
|
||||
W github.com/alexbrainman/sspi/internal/common from github.com/alexbrainman/sspi/negotiate
|
||||
W 💣 github.com/alexbrainman/sspi/negotiate from tailscale.com/net/tshttpproxy
|
||||
github.com/beorn7/perks/quantile from github.com/prometheus/client_golang/prometheus
|
||||
github.com/bits-and-blooms/bitset from github.com/gaissmai/bart
|
||||
💣 github.com/cespare/xxhash/v2 from github.com/prometheus/client_golang/prometheus
|
||||
L github.com/coreos/go-iptables/iptables from tailscale.com/util/linuxfw
|
||||
W 💣 github.com/dblohm7/wingoes from tailscale.com/util/winutil
|
||||
github.com/fxamacker/cbor/v2 from tailscale.com/tka
|
||||
github.com/gaissmai/bart from tailscale.com/net/tsaddr
|
||||
github.com/golang/groupcache/lru from tailscale.com/net/dnscache
|
||||
L github.com/google/nftables from tailscale.com/util/linuxfw
|
||||
L 💣 github.com/google/nftables/alignedbuff from github.com/google/nftables/xt
|
||||
@ -48,7 +46,7 @@ tailscale.com/cmd/derper dependencies: (generated by github.com/tailscale/depawa
|
||||
L github.com/vishvananda/netns from github.com/tailscale/netlink+
|
||||
github.com/x448/float16 from github.com/fxamacker/cbor/v2
|
||||
💣 go4.org/mem from tailscale.com/client/tailscale+
|
||||
go4.org/netipx from tailscale.com/net/tsaddr+
|
||||
go4.org/netipx from tailscale.com/net/tsaddr
|
||||
W 💣 golang.zx2c4.com/wireguard/windows/tunnel/winipcfg from tailscale.com/net/netmon+
|
||||
google.golang.org/protobuf/encoding/protodelim from github.com/prometheus/common/expfmt
|
||||
google.golang.org/protobuf/encoding/prototext from github.com/prometheus/common/expfmt+
|
||||
@ -97,14 +95,12 @@ tailscale.com/cmd/derper dependencies: (generated by github.com/tailscale/depawa
|
||||
tailscale.com/ipn/ipnstate from tailscale.com/client/tailscale+
|
||||
tailscale.com/metrics from tailscale.com/cmd/derper+
|
||||
tailscale.com/net/dnscache from tailscale.com/derp/derphttp
|
||||
tailscale.com/net/flowtrack from tailscale.com/net/packet+
|
||||
tailscale.com/net/ktimeout from tailscale.com/cmd/derper
|
||||
tailscale.com/net/netaddr from tailscale.com/ipn+
|
||||
tailscale.com/net/netknob from tailscale.com/net/netns
|
||||
💣 tailscale.com/net/netmon from tailscale.com/derp/derphttp+
|
||||
tailscale.com/net/netns from tailscale.com/derp/derphttp
|
||||
tailscale.com/net/netutil from tailscale.com/client/tailscale
|
||||
tailscale.com/net/packet from tailscale.com/wgengine/filter
|
||||
tailscale.com/net/sockstats from tailscale.com/derp/derphttp
|
||||
tailscale.com/net/stun from tailscale.com/net/stunserver
|
||||
tailscale.com/net/stunserver from tailscale.com/cmd/derper
|
||||
@ -121,13 +117,13 @@ tailscale.com/cmd/derper dependencies: (generated by github.com/tailscale/depawa
|
||||
W tailscale.com/tsconst from tailscale.com/net/netmon
|
||||
tailscale.com/tstime from tailscale.com/derp+
|
||||
tailscale.com/tstime/mono from tailscale.com/tstime/rate
|
||||
tailscale.com/tstime/rate from tailscale.com/derp+
|
||||
tailscale.com/tstime/rate from tailscale.com/derp
|
||||
tailscale.com/tsweb from tailscale.com/cmd/derper
|
||||
tailscale.com/tsweb/promvarz from tailscale.com/tsweb
|
||||
tailscale.com/tsweb/varz from tailscale.com/tsweb+
|
||||
tailscale.com/types/dnstype from tailscale.com/tailcfg
|
||||
tailscale.com/types/empty from tailscale.com/ipn
|
||||
tailscale.com/types/ipproto from tailscale.com/net/flowtrack+
|
||||
tailscale.com/types/ipproto from tailscale.com/tailcfg+
|
||||
tailscale.com/types/key from tailscale.com/client/tailscale+
|
||||
tailscale.com/types/lazy from tailscale.com/version+
|
||||
tailscale.com/types/logger from tailscale.com/cmd/derper+
|
||||
@ -162,7 +158,7 @@ tailscale.com/cmd/derper dependencies: (generated by github.com/tailscale/depawa
|
||||
W 💣 tailscale.com/util/winutil/winenv from tailscale.com/hostinfo+
|
||||
tailscale.com/version from tailscale.com/derp+
|
||||
tailscale.com/version/distro from tailscale.com/envknob+
|
||||
tailscale.com/wgengine/filter from tailscale.com/types/netmap
|
||||
tailscale.com/wgengine/filter/filtertype from tailscale.com/types/netmap
|
||||
golang.org/x/crypto/acme from golang.org/x/crypto/acme/autocert
|
||||
golang.org/x/crypto/acme/autocert from tailscale.com/cmd/derper
|
||||
golang.org/x/crypto/argon2 from tailscale.com/tka
|
||||
|
@ -104,6 +104,8 @@ func TestDeps(t *testing.T) {
|
||||
"gvisor.dev/gvisor/pkg/cpuid": "https://github.com/tailscale/tailscale/issues/9756",
|
||||
"gvisor.dev/gvisor/pkg/tcpip": "https://github.com/tailscale/tailscale/issues/9756",
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header": "https://github.com/tailscale/tailscale/issues/9756",
|
||||
"tailscale.com/net/packet": "not needed in derper",
|
||||
"github.com/gaissmai/bart": "not needed in derper",
|
||||
},
|
||||
}.Check(t)
|
||||
}
|
||||
|
@ -1,9 +1,7 @@
|
||||
tailscale.com/cmd/stund dependencies: (generated by github.com/tailscale/depaware)
|
||||
|
||||
github.com/beorn7/perks/quantile from github.com/prometheus/client_golang/prometheus
|
||||
github.com/bits-and-blooms/bitset from github.com/gaissmai/bart
|
||||
💣 github.com/cespare/xxhash/v2 from github.com/prometheus/client_golang/prometheus
|
||||
github.com/gaissmai/bart from tailscale.com/net/tsaddr
|
||||
github.com/google/uuid from tailscale.com/util/fastuuid
|
||||
💣 github.com/prometheus/client_golang/prometheus from tailscale.com/tsweb/promvarz
|
||||
github.com/prometheus/client_golang/prometheus/internal from github.com/prometheus/client_golang/prometheus
|
||||
|
@ -5,12 +5,10 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
|
||||
W 💣 github.com/alexbrainman/sspi from github.com/alexbrainman/sspi/internal/common+
|
||||
W github.com/alexbrainman/sspi/internal/common from github.com/alexbrainman/sspi/negotiate
|
||||
W 💣 github.com/alexbrainman/sspi/negotiate from tailscale.com/net/tshttpproxy
|
||||
github.com/bits-and-blooms/bitset from github.com/gaissmai/bart
|
||||
L github.com/coreos/go-iptables/iptables from tailscale.com/util/linuxfw
|
||||
W 💣 github.com/dblohm7/wingoes from github.com/dblohm7/wingoes/pe+
|
||||
W 💣 github.com/dblohm7/wingoes/pe from tailscale.com/util/winutil/authenticode
|
||||
github.com/fxamacker/cbor/v2 from tailscale.com/tka
|
||||
github.com/gaissmai/bart from tailscale.com/net/tsaddr
|
||||
github.com/golang/groupcache/lru from tailscale.com/net/dnscache
|
||||
L github.com/google/nftables from tailscale.com/util/linuxfw
|
||||
L 💣 github.com/google/nftables/alignedbuff from github.com/google/nftables/xt
|
||||
@ -59,7 +57,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
|
||||
L github.com/vishvananda/netns from github.com/tailscale/netlink+
|
||||
github.com/x448/float16 from github.com/fxamacker/cbor/v2
|
||||
💣 go4.org/mem from tailscale.com/client/tailscale+
|
||||
go4.org/netipx from tailscale.com/net/tsaddr+
|
||||
go4.org/netipx from tailscale.com/net/tsaddr
|
||||
W 💣 golang.zx2c4.com/wireguard/windows/tunnel/winipcfg from tailscale.com/net/netmon+
|
||||
k8s.io/client-go/util/homedir from tailscale.com/cmd/tailscale/cli
|
||||
nhooyr.io/websocket from tailscale.com/control/controlhttp+
|
||||
@ -98,7 +96,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
|
||||
tailscale.com/net/dns/recursive from tailscale.com/net/dnsfallback
|
||||
tailscale.com/net/dnscache from tailscale.com/control/controlhttp+
|
||||
tailscale.com/net/dnsfallback from tailscale.com/control/controlhttp
|
||||
tailscale.com/net/flowtrack from tailscale.com/net/packet+
|
||||
tailscale.com/net/flowtrack from tailscale.com/net/packet
|
||||
tailscale.com/net/netaddr from tailscale.com/ipn+
|
||||
tailscale.com/net/netcheck from tailscale.com/cmd/tailscale/cli
|
||||
tailscale.com/net/neterror from tailscale.com/net/netcheck+
|
||||
@ -106,7 +104,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
|
||||
💣 tailscale.com/net/netmon from tailscale.com/cmd/tailscale/cli+
|
||||
tailscale.com/net/netns from tailscale.com/derp/derphttp+
|
||||
tailscale.com/net/netutil from tailscale.com/client/tailscale+
|
||||
tailscale.com/net/packet from tailscale.com/wgengine/capture+
|
||||
tailscale.com/net/packet from tailscale.com/wgengine/capture
|
||||
tailscale.com/net/ping from tailscale.com/net/netcheck
|
||||
tailscale.com/net/portmapper from tailscale.com/cmd/tailscale/cli+
|
||||
tailscale.com/net/sockstats from tailscale.com/control/controlhttp+
|
||||
@ -170,7 +168,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
|
||||
tailscale.com/version from tailscale.com/client/web+
|
||||
tailscale.com/version/distro from tailscale.com/client/web+
|
||||
tailscale.com/wgengine/capture from tailscale.com/cmd/tailscale/cli
|
||||
tailscale.com/wgengine/filter from tailscale.com/types/netmap
|
||||
tailscale.com/wgengine/filter/filtertype from tailscale.com/types/netmap
|
||||
golang.org/x/crypto/argon2 from tailscale.com/tka
|
||||
golang.org/x/crypto/blake2b from golang.org/x/crypto/argon2+
|
||||
golang.org/x/crypto/blake2s from tailscale.com/clientupdate/distsign+
|
||||
|
@ -17,6 +17,10 @@ func TestDeps(t *testing.T) {
|
||||
"gvisor.dev/gvisor/pkg/cpuid": "https://github.com/tailscale/tailscale/issues/9756",
|
||||
"gvisor.dev/gvisor/pkg/tcpip": "https://github.com/tailscale/tailscale/issues/9756",
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header": "https://github.com/tailscale/tailscale/issues/9756",
|
||||
"tailscale.com/wgengine/filter": "brings in bart, etc",
|
||||
"github.com/bits-and-blooms/bitset": "unneeded in CLI",
|
||||
"github.com/gaissmai/bart": "unneeded in CLI",
|
||||
"tailscale.com/net/ipset": "unneeded in CLI",
|
||||
},
|
||||
}.Check(t)
|
||||
}
|
||||
|
@ -295,6 +295,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
||||
tailscale.com/net/dnscache from tailscale.com/control/controlclient+
|
||||
tailscale.com/net/dnsfallback from tailscale.com/cmd/tailscaled+
|
||||
tailscale.com/net/flowtrack from tailscale.com/net/packet+
|
||||
tailscale.com/net/ipset from tailscale.com/ipn/ipnlocal+
|
||||
tailscale.com/net/netaddr from tailscale.com/ipn+
|
||||
tailscale.com/net/netcheck from tailscale.com/wgengine/magicsock+
|
||||
tailscale.com/net/neterror from tailscale.com/net/dns/resolver+
|
||||
@ -408,6 +409,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
||||
tailscale.com/wgengine from tailscale.com/cmd/tailscaled+
|
||||
tailscale.com/wgengine/capture from tailscale.com/ipn/ipnlocal+
|
||||
tailscale.com/wgengine/filter from tailscale.com/control/controlclient+
|
||||
tailscale.com/wgengine/filter/filtertype from tailscale.com/types/netmap+
|
||||
💣 tailscale.com/wgengine/magicsock from tailscale.com/ipn/ipnlocal+
|
||||
tailscale.com/wgengine/netlog from tailscale.com/wgengine
|
||||
tailscale.com/wgengine/netstack from tailscale.com/cmd/tailscaled
|
||||
|
@ -61,6 +61,7 @@
|
||||
"tailscale.com/net/dns"
|
||||
"tailscale.com/net/dnscache"
|
||||
"tailscale.com/net/dnsfallback"
|
||||
"tailscale.com/net/ipset"
|
||||
"tailscale.com/net/netcheck"
|
||||
"tailscale.com/net/netkernelconf"
|
||||
"tailscale.com/net/netmon"
|
||||
@ -2761,13 +2762,13 @@ func (b *LocalBackend) setAtomicValuesFromPrefsLocked(p ipn.PrefsView) {
|
||||
b.setExposeRemoteWebClientAtomicBoolLocked(p)
|
||||
|
||||
if !p.Valid() {
|
||||
b.containsViaIPFuncAtomic.Store(tsaddr.FalseContainsIPFunc())
|
||||
b.containsViaIPFuncAtomic.Store(ipset.FalseContainsIPFunc())
|
||||
b.setTCPPortsIntercepted(nil)
|
||||
b.lastServeConfJSON = mem.B(nil)
|
||||
b.serveConfig = ipn.ServeConfigView{}
|
||||
} else {
|
||||
filtered := tsaddr.FilterPrefixesCopy(p.AdvertiseRoutes(), tsaddr.IsViaPrefix)
|
||||
b.containsViaIPFuncAtomic.Store(tsaddr.NewContainsIPFunc(views.SliceOf(filtered)))
|
||||
b.containsViaIPFuncAtomic.Store(ipset.NewContainsIPFunc(views.SliceOf(filtered)))
|
||||
b.setTCPPortsInterceptedFromNetmapAndPrefsLocked(p)
|
||||
}
|
||||
}
|
||||
|
80
net/ipset/ipset.go
Normal file
80
net/ipset/ipset.go
Normal file
@ -0,0 +1,80 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Package ipset provides code for creating efficient IP-in-set lookup functions
|
||||
// with different implementations depending on the set.
|
||||
package ipset
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
|
||||
"github.com/gaissmai/bart"
|
||||
"tailscale.com/types/views"
|
||||
)
|
||||
|
||||
// FalseContainsIPFunc is shorthand for NewContainsIPFunc(views.Slice[netip.Prefix]{}).
|
||||
func FalseContainsIPFunc() func(ip netip.Addr) bool {
|
||||
return func(ip netip.Addr) bool { return false }
|
||||
}
|
||||
|
||||
// pathForTest is a test hook for NewContainsIPFunc, to test that it took the
|
||||
// right construction path.
|
||||
var pathForTest = func(string) {}
|
||||
|
||||
// NewContainsIPFunc returns a func that reports whether ip is in addrs.
|
||||
//
|
||||
// The returned func is optimized for the length of contents of addrs.
|
||||
func NewContainsIPFunc(addrs views.Slice[netip.Prefix]) func(ip netip.Addr) bool {
|
||||
// Specialize the three common cases: no address, just IPv4
|
||||
// (or just IPv6), and both IPv4 and IPv6.
|
||||
if addrs.Len() == 0 {
|
||||
pathForTest("empty")
|
||||
return func(netip.Addr) bool { return false }
|
||||
}
|
||||
// If any addr is a prefix with more than a single IP, then do either a
|
||||
// linear scan or a bart table, depending on the number of addrs.
|
||||
if addrs.ContainsFunc(func(p netip.Prefix) bool { return !p.IsSingleIP() }) {
|
||||
if addrs.Len() > 6 {
|
||||
pathForTest("bart")
|
||||
// Built a bart table.
|
||||
t := &bart.Table[struct{}]{}
|
||||
for i := range addrs.Len() {
|
||||
t.Insert(addrs.At(i), struct{}{})
|
||||
}
|
||||
return func(ip netip.Addr) bool {
|
||||
_, ok := t.Get(ip)
|
||||
return ok
|
||||
}
|
||||
} else {
|
||||
pathForTest("linear-contains")
|
||||
// Small enough to do a linear search.
|
||||
acopy := addrs.AsSlice()
|
||||
return func(ip netip.Addr) bool {
|
||||
for _, a := range acopy {
|
||||
if a.Contains(ip) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
// Fast paths for 1 and 2 IPs:
|
||||
if addrs.Len() == 1 {
|
||||
pathForTest("one-ip")
|
||||
a := addrs.At(0)
|
||||
return func(ip netip.Addr) bool { return ip == a.Addr() }
|
||||
}
|
||||
if addrs.Len() == 2 {
|
||||
pathForTest("two-ip")
|
||||
a, b := addrs.At(0), addrs.At(1)
|
||||
return func(ip netip.Addr) bool { return ip == a.Addr() || ip == b.Addr() }
|
||||
}
|
||||
// General case:
|
||||
pathForTest("ip-map")
|
||||
m := map[netip.Addr]bool{}
|
||||
for i := range addrs.Len() {
|
||||
m[addrs.At(i).Addr()] = true
|
||||
}
|
||||
return func(ip netip.Addr) bool { return m[ip] }
|
||||
}
|
149
net/ipset/ipset_test.go
Normal file
149
net/ipset/ipset_test.go
Normal file
@ -0,0 +1,149 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package ipset
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
"tailscale.com/tstest"
|
||||
"tailscale.com/types/views"
|
||||
)
|
||||
|
||||
func pp(ss ...string) (ret []netip.Prefix) {
|
||||
for _, s := range ss {
|
||||
ret = append(ret, netip.MustParsePrefix(s))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func aa(ss ...string) (ret []netip.Addr) {
|
||||
for _, s := range ss {
|
||||
ret = append(ret, netip.MustParseAddr(s))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var newContainsIPFuncTests = []struct {
|
||||
name string
|
||||
pfx []netip.Prefix
|
||||
want string
|
||||
wantIn []netip.Addr
|
||||
wantOut []netip.Addr
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
pfx: pp(),
|
||||
want: "empty",
|
||||
wantOut: aa("8.8.8.8"),
|
||||
},
|
||||
{
|
||||
name: "cidr-list-1",
|
||||
pfx: pp("10.0.0.0/8"),
|
||||
want: "linear-contains",
|
||||
wantIn: aa("10.0.0.1", "10.2.3.4"),
|
||||
wantOut: aa("8.8.8.8"),
|
||||
},
|
||||
{
|
||||
name: "cidr-list-2",
|
||||
pfx: pp("1.0.0.0/8", "3.0.0.0/8"),
|
||||
want: "linear-contains",
|
||||
wantIn: aa("1.0.0.1", "3.0.0.1"),
|
||||
wantOut: aa("2.0.0.1"),
|
||||
},
|
||||
{
|
||||
name: "cidr-list-3",
|
||||
pfx: pp("1.0.0.0/8", "3.0.0.0/8", "5.0.0.0/8"),
|
||||
want: "linear-contains",
|
||||
wantIn: aa("1.0.0.1", "5.0.0.1"),
|
||||
wantOut: aa("2.0.0.1"),
|
||||
},
|
||||
{
|
||||
name: "cidr-list-4",
|
||||
pfx: pp("1.0.0.0/8", "3.0.0.0/8", "5.0.0.0/8", "7.0.0.0/8"),
|
||||
want: "linear-contains",
|
||||
wantIn: aa("1.0.0.1", "7.0.0.1"),
|
||||
wantOut: aa("2.0.0.1"),
|
||||
},
|
||||
{
|
||||
name: "cidr-list-5",
|
||||
pfx: pp("1.0.0.0/8", "3.0.0.0/8", "5.0.0.0/8", "7.0.0.0/8", "9.0.0.0/8"),
|
||||
want: "linear-contains",
|
||||
wantIn: aa("1.0.0.1", "9.0.0.1"),
|
||||
wantOut: aa("2.0.0.1"),
|
||||
},
|
||||
{
|
||||
name: "cidr-list-10",
|
||||
pfx: pp("1.0.0.0/8", "3.0.0.0/8", "5.0.0.0/8", "7.0.0.0/8", "9.0.0.0/8",
|
||||
"11.0.0.0/8", "13.0.0.0/8", "15.0.0.0/8", "17.0.0.0/8", "19.0.0.0/8"),
|
||||
want: "bart", // big enough that bart is faster than linear-contains
|
||||
wantIn: aa("1.0.0.1", "19.0.0.1"),
|
||||
wantOut: aa("2.0.0.1"),
|
||||
},
|
||||
{
|
||||
name: "one-ip",
|
||||
pfx: pp("10.1.0.0/32"),
|
||||
want: "one-ip",
|
||||
wantIn: aa("10.1.0.0"),
|
||||
wantOut: aa("10.0.0.9"),
|
||||
},
|
||||
{
|
||||
name: "two-ip",
|
||||
pfx: pp("10.1.0.0/32", "10.2.0.0/32"),
|
||||
want: "two-ip",
|
||||
wantIn: aa("10.1.0.0", "10.2.0.0"),
|
||||
wantOut: aa("8.8.8.8"),
|
||||
},
|
||||
{
|
||||
name: "three-ip",
|
||||
pfx: pp("10.1.0.0/32", "10.2.0.0/32", "10.3.0.0/32"),
|
||||
want: "ip-map",
|
||||
wantIn: aa("10.1.0.0", "10.2.0.0"),
|
||||
wantOut: aa("8.8.8.8"),
|
||||
},
|
||||
}
|
||||
|
||||
func BenchmarkNewContainsIPFunc(b *testing.B) {
|
||||
for _, tt := range newContainsIPFuncTests {
|
||||
b.Run(tt.name, func(b *testing.B) {
|
||||
f := NewContainsIPFunc(views.SliceOf(tt.pfx))
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, ip := range tt.wantIn {
|
||||
if !f(ip) {
|
||||
b.Fatal("unexpected false")
|
||||
}
|
||||
}
|
||||
for _, ip := range tt.wantOut {
|
||||
if f(ip) {
|
||||
b.Fatal("unexpected true")
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewContainsIPFunc(t *testing.T) {
|
||||
for _, tt := range newContainsIPFuncTests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var got string
|
||||
tstest.Replace(t, &pathForTest, func(path string) { got = path })
|
||||
|
||||
f := NewContainsIPFunc(views.SliceOf(tt.pfx))
|
||||
if got != tt.want {
|
||||
t.Errorf("func type = %q; want %q", got, tt.want)
|
||||
}
|
||||
for _, ip := range tt.wantIn {
|
||||
if !f(ip) {
|
||||
t.Errorf("match(%v) = false; want true", ip)
|
||||
}
|
||||
}
|
||||
for _, ip := range tt.wantOut {
|
||||
if f(ip) {
|
||||
t.Errorf("match(%v) = true; want false", ip)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -11,7 +11,6 @@
|
||||
"slices"
|
||||
"sync"
|
||||
|
||||
"github.com/gaissmai/bart"
|
||||
"go4.org/netipx"
|
||||
"tailscale.com/net/netaddr"
|
||||
"tailscale.com/types/views"
|
||||
@ -161,77 +160,6 @@ type oncePrefix struct {
|
||||
v netip.Prefix
|
||||
}
|
||||
|
||||
// FalseContainsIPFunc is shorthand for NewContainsIPFunc(views.Slice[netip.Prefix]{}).
|
||||
func FalseContainsIPFunc() func(ip netip.Addr) bool {
|
||||
return func(ip netip.Addr) bool { return false }
|
||||
}
|
||||
|
||||
// pathForTest is a test hook for NewContainsIPFunc, to test that it took the
|
||||
// right construction path.
|
||||
var pathForTest = func(string) {}
|
||||
|
||||
// NewContainsIPFunc returns a func that reports whether ip is in addrs.
|
||||
//
|
||||
// It's optimized for the cases of addrs being empty and addrs
|
||||
// containing 1 or 2 single-IP prefixes (such as one IPv4 address and
|
||||
// one IPv6 address).
|
||||
//
|
||||
// Otherwise the implementation is somewhat slow.
|
||||
func NewContainsIPFunc(addrs views.Slice[netip.Prefix]) func(ip netip.Addr) bool {
|
||||
// Specialize the three common cases: no address, just IPv4
|
||||
// (or just IPv6), and both IPv4 and IPv6.
|
||||
if addrs.Len() == 0 {
|
||||
pathForTest("empty")
|
||||
return func(netip.Addr) bool { return false }
|
||||
}
|
||||
// If any addr is a prefix with more than a single IP, then do either a
|
||||
// linear scan or a bart table, depending on the number of addrs.
|
||||
if addrs.ContainsFunc(func(p netip.Prefix) bool { return !p.IsSingleIP() }) {
|
||||
if addrs.Len() > 6 {
|
||||
pathForTest("bart")
|
||||
// Built a bart table.
|
||||
t := &bart.Table[struct{}]{}
|
||||
for i := range addrs.Len() {
|
||||
t.Insert(addrs.At(i), struct{}{})
|
||||
}
|
||||
return func(ip netip.Addr) bool {
|
||||
_, ok := t.Get(ip)
|
||||
return ok
|
||||
}
|
||||
} else {
|
||||
pathForTest("linear-contains")
|
||||
// Small enough to do a linear search.
|
||||
acopy := addrs.AsSlice()
|
||||
return func(ip netip.Addr) bool {
|
||||
for _, a := range acopy {
|
||||
if a.Contains(ip) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
// Fast paths for 1 and 2 IPs:
|
||||
if addrs.Len() == 1 {
|
||||
pathForTest("one-ip")
|
||||
a := addrs.At(0)
|
||||
return func(ip netip.Addr) bool { return ip == a.Addr() }
|
||||
}
|
||||
if addrs.Len() == 2 {
|
||||
pathForTest("two-ip")
|
||||
a, b := addrs.At(0), addrs.At(1)
|
||||
return func(ip netip.Addr) bool { return ip == a.Addr() || ip == b.Addr() }
|
||||
}
|
||||
// General case:
|
||||
pathForTest("ip-map")
|
||||
m := map[netip.Addr]bool{}
|
||||
for i := range addrs.Len() {
|
||||
m[addrs.At(i).Addr()] = true
|
||||
}
|
||||
return func(ip netip.Addr) bool { return m[ip] }
|
||||
}
|
||||
|
||||
// PrefixesContainsIP reports whether any prefix in ipp contains ip.
|
||||
func PrefixesContainsIP(ipp []netip.Prefix, ip netip.Addr) bool {
|
||||
for _, r := range ipp {
|
||||
|
@ -8,8 +8,6 @@
|
||||
"testing"
|
||||
|
||||
"tailscale.com/net/netaddr"
|
||||
"tailscale.com/tstest"
|
||||
"tailscale.com/types/views"
|
||||
)
|
||||
|
||||
func TestInCrostiniRange(t *testing.T) {
|
||||
@ -67,143 +65,6 @@ func TestCGNATRange(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func pp(ss ...string) (ret []netip.Prefix) {
|
||||
for _, s := range ss {
|
||||
ret = append(ret, netip.MustParsePrefix(s))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func aa(ss ...string) (ret []netip.Addr) {
|
||||
for _, s := range ss {
|
||||
ret = append(ret, netip.MustParseAddr(s))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var newContainsIPFuncTests = []struct {
|
||||
name string
|
||||
pfx []netip.Prefix
|
||||
want string
|
||||
wantIn []netip.Addr
|
||||
wantOut []netip.Addr
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
pfx: pp(),
|
||||
want: "empty",
|
||||
wantOut: aa("8.8.8.8"),
|
||||
},
|
||||
{
|
||||
name: "cidr-list-1",
|
||||
pfx: pp("10.0.0.0/8"),
|
||||
want: "linear-contains",
|
||||
wantIn: aa("10.0.0.1", "10.2.3.4"),
|
||||
wantOut: aa("8.8.8.8"),
|
||||
},
|
||||
{
|
||||
name: "cidr-list-2",
|
||||
pfx: pp("1.0.0.0/8", "3.0.0.0/8"),
|
||||
want: "linear-contains",
|
||||
wantIn: aa("1.0.0.1", "3.0.0.1"),
|
||||
wantOut: aa("2.0.0.1"),
|
||||
},
|
||||
{
|
||||
name: "cidr-list-3",
|
||||
pfx: pp("1.0.0.0/8", "3.0.0.0/8", "5.0.0.0/8"),
|
||||
want: "linear-contains",
|
||||
wantIn: aa("1.0.0.1", "5.0.0.1"),
|
||||
wantOut: aa("2.0.0.1"),
|
||||
},
|
||||
{
|
||||
name: "cidr-list-4",
|
||||
pfx: pp("1.0.0.0/8", "3.0.0.0/8", "5.0.0.0/8", "7.0.0.0/8"),
|
||||
want: "linear-contains",
|
||||
wantIn: aa("1.0.0.1", "7.0.0.1"),
|
||||
wantOut: aa("2.0.0.1"),
|
||||
},
|
||||
{
|
||||
name: "cidr-list-5",
|
||||
pfx: pp("1.0.0.0/8", "3.0.0.0/8", "5.0.0.0/8", "7.0.0.0/8", "9.0.0.0/8"),
|
||||
want: "linear-contains",
|
||||
wantIn: aa("1.0.0.1", "9.0.0.1"),
|
||||
wantOut: aa("2.0.0.1"),
|
||||
},
|
||||
{
|
||||
name: "cidr-list-10",
|
||||
pfx: pp("1.0.0.0/8", "3.0.0.0/8", "5.0.0.0/8", "7.0.0.0/8", "9.0.0.0/8",
|
||||
"11.0.0.0/8", "13.0.0.0/8", "15.0.0.0/8", "17.0.0.0/8", "19.0.0.0/8"),
|
||||
want: "bart", // big enough that bart is faster than linear-contains
|
||||
wantIn: aa("1.0.0.1", "19.0.0.1"),
|
||||
wantOut: aa("2.0.0.1"),
|
||||
},
|
||||
{
|
||||
name: "one-ip",
|
||||
pfx: pp("10.1.0.0/32"),
|
||||
want: "one-ip",
|
||||
wantIn: aa("10.1.0.0"),
|
||||
wantOut: aa("10.0.0.9"),
|
||||
},
|
||||
{
|
||||
name: "two-ip",
|
||||
pfx: pp("10.1.0.0/32", "10.2.0.0/32"),
|
||||
want: "two-ip",
|
||||
wantIn: aa("10.1.0.0", "10.2.0.0"),
|
||||
wantOut: aa("8.8.8.8"),
|
||||
},
|
||||
{
|
||||
name: "three-ip",
|
||||
pfx: pp("10.1.0.0/32", "10.2.0.0/32", "10.3.0.0/32"),
|
||||
want: "ip-map",
|
||||
wantIn: aa("10.1.0.0", "10.2.0.0"),
|
||||
wantOut: aa("8.8.8.8"),
|
||||
},
|
||||
}
|
||||
|
||||
func BenchmarkNewContainsIPFunc(b *testing.B) {
|
||||
for _, tt := range newContainsIPFuncTests {
|
||||
b.Run(tt.name, func(b *testing.B) {
|
||||
f := NewContainsIPFunc(views.SliceOf(tt.pfx))
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, ip := range tt.wantIn {
|
||||
if !f(ip) {
|
||||
b.Fatal("unexpected false")
|
||||
}
|
||||
}
|
||||
for _, ip := range tt.wantOut {
|
||||
if f(ip) {
|
||||
b.Fatal("unexpected true")
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewContainsIPFunc(t *testing.T) {
|
||||
for _, tt := range newContainsIPFuncTests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var got string
|
||||
tstest.Replace(t, &pathForTest, func(path string) { got = path })
|
||||
|
||||
f := NewContainsIPFunc(views.SliceOf(tt.pfx))
|
||||
if got != tt.want {
|
||||
t.Errorf("func type = %q; want %q", got, tt.want)
|
||||
}
|
||||
for _, ip := range tt.wantIn {
|
||||
if !f(ip) {
|
||||
t.Errorf("match(%v) = false; want true", ip)
|
||||
}
|
||||
}
|
||||
for _, ip := range tt.wantOut {
|
||||
if f(ip) {
|
||||
t.Errorf("match(%v) = true; want false", ip)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var sinkIP netip.Addr
|
||||
|
||||
func BenchmarkTailscaleServiceAddr(b *testing.B) {
|
||||
|
@ -18,7 +18,7 @@
|
||||
"tailscale.com/types/key"
|
||||
"tailscale.com/types/views"
|
||||
"tailscale.com/util/set"
|
||||
"tailscale.com/wgengine/filter"
|
||||
"tailscale.com/wgengine/filter/filtertype"
|
||||
)
|
||||
|
||||
// NetworkMap is the current state of the world.
|
||||
@ -40,7 +40,7 @@ type NetworkMap struct {
|
||||
Peers []tailcfg.NodeView // sorted by Node.ID
|
||||
DNS tailcfg.DNSConfig
|
||||
|
||||
PacketFilter []filter.Match
|
||||
PacketFilter []filtertype.Match
|
||||
PacketFilterRules views.Slice[tailcfg.FilterRule]
|
||||
SSHPolicy *tailcfg.SSHPolicy // or nil, if not enabled/allowed
|
||||
|
||||
|
@ -14,9 +14,9 @@
|
||||
"go4.org/netipx"
|
||||
"tailscale.com/envknob"
|
||||
"tailscale.com/net/flowtrack"
|
||||
"tailscale.com/net/ipset"
|
||||
"tailscale.com/net/netaddr"
|
||||
"tailscale.com/net/packet"
|
||||
"tailscale.com/net/tsaddr"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/tstime/rate"
|
||||
"tailscale.com/types/ipproto"
|
||||
@ -24,6 +24,7 @@
|
||||
"tailscale.com/types/views"
|
||||
"tailscale.com/util/mak"
|
||||
"tailscale.com/util/slicesx"
|
||||
"tailscale.com/wgengine/filter/filtertype"
|
||||
)
|
||||
|
||||
// Filter is a stateful packet filter.
|
||||
@ -110,6 +111,13 @@ func (r Response) IsDrop() bool {
|
||||
HexdumpAccepts // print packet hexdump when logging accepts
|
||||
)
|
||||
|
||||
type (
|
||||
Match = filtertype.Match
|
||||
NetPortRange = filtertype.NetPortRange
|
||||
PortRange = filtertype.PortRange
|
||||
CapMatch = filtertype.CapMatch
|
||||
)
|
||||
|
||||
// NewAllowAllForTest returns a packet filter that accepts
|
||||
// everything. Use in tests only, as it permits some kinds of spoofing
|
||||
// attacks to reach the OS network stack.
|
||||
@ -192,23 +200,23 @@ func New(matches []Match, localNets, logIPs *netipx.IPSet, shareStateWith *Filte
|
||||
matches6: matchesFamily(matches, netip.Addr.Is6),
|
||||
cap4: capMatchesFunc(matches, netip.Addr.Is4),
|
||||
cap6: capMatchesFunc(matches, netip.Addr.Is6),
|
||||
local4: tsaddr.FalseContainsIPFunc(),
|
||||
local6: tsaddr.FalseContainsIPFunc(),
|
||||
logIPs4: tsaddr.FalseContainsIPFunc(),
|
||||
logIPs6: tsaddr.FalseContainsIPFunc(),
|
||||
local4: ipset.FalseContainsIPFunc(),
|
||||
local6: ipset.FalseContainsIPFunc(),
|
||||
logIPs4: ipset.FalseContainsIPFunc(),
|
||||
logIPs6: ipset.FalseContainsIPFunc(),
|
||||
state: state,
|
||||
}
|
||||
if localNets != nil {
|
||||
p := localNets.Prefixes()
|
||||
p4, p6 := slicesx.Partition(p, func(p netip.Prefix) bool { return p.Addr().Is4() })
|
||||
f.local4 = tsaddr.NewContainsIPFunc(views.SliceOf(p4))
|
||||
f.local6 = tsaddr.NewContainsIPFunc(views.SliceOf(p6))
|
||||
f.local4 = ipset.NewContainsIPFunc(views.SliceOf(p4))
|
||||
f.local6 = ipset.NewContainsIPFunc(views.SliceOf(p6))
|
||||
}
|
||||
if logIPs != nil {
|
||||
p := logIPs.Prefixes()
|
||||
p4, p6 := slicesx.Partition(p, func(p netip.Prefix) bool { return p.Addr().Is4() })
|
||||
f.logIPs4 = tsaddr.NewContainsIPFunc(views.SliceOf(p4))
|
||||
f.logIPs6 = tsaddr.NewContainsIPFunc(views.SliceOf(p6))
|
||||
f.logIPs4 = ipset.NewContainsIPFunc(views.SliceOf(p4))
|
||||
f.logIPs6 = ipset.NewContainsIPFunc(views.SliceOf(p6))
|
||||
}
|
||||
|
||||
return f
|
||||
@ -233,7 +241,7 @@ func matchesFamily(ms matches, keep func(netip.Addr) bool) matches {
|
||||
}
|
||||
}
|
||||
if len(retm.Srcs) > 0 && len(retm.Dsts) > 0 {
|
||||
retm.SrcsContains = tsaddr.NewContainsIPFunc(views.SliceOf(retm.Srcs))
|
||||
retm.SrcsContains = ipset.NewContainsIPFunc(views.SliceOf(retm.Srcs))
|
||||
ret = append(ret, retm)
|
||||
}
|
||||
}
|
||||
@ -255,7 +263,7 @@ func capMatchesFunc(ms matches, keep func(netip.Addr) bool) matches {
|
||||
}
|
||||
}
|
||||
if len(retm.Srcs) > 0 {
|
||||
retm.SrcsContains = tsaddr.NewContainsIPFunc(views.SliceOf(retm.Srcs))
|
||||
retm.SrcsContains = ipset.NewContainsIPFunc(views.SliceOf(retm.Srcs))
|
||||
ret = append(ret, retm)
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"go4.org/netipx"
|
||||
xmaps "golang.org/x/exp/maps"
|
||||
"tailscale.com/net/ipset"
|
||||
"tailscale.com/net/packet"
|
||||
"tailscale.com/net/tsaddr"
|
||||
"tailscale.com/tailcfg"
|
||||
@ -28,6 +29,7 @@
|
||||
"tailscale.com/types/logger"
|
||||
"tailscale.com/types/views"
|
||||
"tailscale.com/util/must"
|
||||
"tailscale.com/wgengine/filter/filtertype"
|
||||
)
|
||||
|
||||
// testAllowedProto is an IP protocol number we treat as allowed for
|
||||
@ -44,7 +46,7 @@ func m(srcs []netip.Prefix, dsts []NetPortRange, protos ...ipproto.Proto) Match
|
||||
return Match{
|
||||
IPProto: protos,
|
||||
Srcs: srcs,
|
||||
SrcsContains: tsaddr.NewContainsIPFunc(views.SliceOf(srcs)),
|
||||
SrcsContains: ipset.NewContainsIPFunc(views.SliceOf(srcs)),
|
||||
Dsts: dsts,
|
||||
}
|
||||
}
|
||||
@ -440,7 +442,7 @@ func TestLoggingPrivacy(t *testing.T) {
|
||||
}
|
||||
|
||||
f := newFilter(logf)
|
||||
f.logIPs4 = tsaddr.NewContainsIPFunc(views.SliceOf([]netip.Prefix{
|
||||
f.logIPs4 = ipset.NewContainsIPFunc(views.SliceOf([]netip.Prefix{
|
||||
tsaddr.CGNATRange(),
|
||||
tsaddr.TailscaleULARange(),
|
||||
}))
|
||||
@ -702,7 +704,7 @@ func nets(nets ...string) (ret []netip.Prefix) {
|
||||
|
||||
func ports(s string) PortRange {
|
||||
if s == "*" {
|
||||
return allPorts
|
||||
return filtertype.AllPorts
|
||||
}
|
||||
|
||||
var fs, ls string
|
||||
|
98
wgengine/filter/filtertype/filtertype.go
Normal file
98
wgengine/filter/filtertype/filtertype.go
Normal file
@ -0,0 +1,98 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Package filtertype defines the types used by wgengine/filter.
|
||||
package filtertype
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"strings"
|
||||
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/ipproto"
|
||||
)
|
||||
|
||||
//go:generate go run tailscale.com/cmd/cloner --type=Match,CapMatch
|
||||
|
||||
// PortRange is a range of TCP and UDP ports.
|
||||
type PortRange struct {
|
||||
First, Last uint16 // inclusive
|
||||
}
|
||||
|
||||
var AllPorts = PortRange{0, 0xffff}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// contains returns whether port is in pr.
|
||||
func (pr PortRange) Contains(port uint16) bool {
|
||||
return port >= pr.First && port <= pr.Last
|
||||
}
|
||||
|
||||
// NetPortRange combines an IP address prefix and PortRange.
|
||||
type NetPortRange struct {
|
||||
Net netip.Prefix
|
||||
Ports PortRange
|
||||
}
|
||||
|
||||
func (npr NetPortRange) String() string {
|
||||
return fmt.Sprintf("%v:%v", npr.Net, npr.Ports)
|
||||
}
|
||||
|
||||
// CapMatch is a capability grant match predicate.
|
||||
type CapMatch struct {
|
||||
// Dst is the IP prefix that the destination IP address matches against
|
||||
// to get the capability.
|
||||
Dst netip.Prefix
|
||||
|
||||
// Cap is the capability that's granted if the destination IP addresses
|
||||
// matches Dst.
|
||||
Cap tailcfg.PeerCapability
|
||||
|
||||
// Values are the raw JSON values of the capability.
|
||||
// See tailcfg.PeerCapability and tailcfg.PeerCapMap for details.
|
||||
Values []tailcfg.RawMessage
|
||||
}
|
||||
|
||||
// Match matches packets from any IP address in Srcs to any ip:port in
|
||||
// Dsts.
|
||||
type Match struct {
|
||||
IPProto []ipproto.Proto // required set (no default value at this layer)
|
||||
Srcs []netip.Prefix
|
||||
SrcsContains func(netip.Addr) bool `json:"-"` // report whether Addr is in Srcs
|
||||
Dsts []NetPortRange // optional, if Srcs match
|
||||
Caps []CapMatch // optional, if Srcs match
|
||||
}
|
||||
|
||||
func (m Match) String() string {
|
||||
// TODO(bradfitz): use strings.Builder, add String tests
|
||||
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=>%v", m.IPProto, ss, ds)
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
|
||||
// Code generated by tailscale.com/cmd/cloner; DO NOT EDIT.
|
||||
|
||||
package filter
|
||||
package filtertype
|
||||
|
||||
import (
|
||||
"net/netip"
|
@ -4,101 +4,13 @@
|
||||
package filter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"tailscale.com/net/packet"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/ipproto"
|
||||
"tailscale.com/wgengine/filter/filtertype"
|
||||
)
|
||||
|
||||
//go:generate go run tailscale.com/cmd/cloner --type=Match,CapMatch
|
||||
|
||||
// PortRange is a range of TCP and UDP ports.
|
||||
type PortRange struct {
|
||||
First, Last uint16 // inclusive
|
||||
}
|
||||
|
||||
var allPorts = PortRange{0, 0xffff}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// contains returns whether port is in pr.
|
||||
func (pr PortRange) contains(port uint16) bool {
|
||||
return port >= pr.First && port <= pr.Last
|
||||
}
|
||||
|
||||
// NetPortRange combines an IP address prefix and PortRange.
|
||||
type NetPortRange struct {
|
||||
Net netip.Prefix
|
||||
Ports PortRange
|
||||
}
|
||||
|
||||
func (npr NetPortRange) String() string {
|
||||
return fmt.Sprintf("%v:%v", npr.Net, npr.Ports)
|
||||
}
|
||||
|
||||
// CapMatch is a capability grant match predicate.
|
||||
type CapMatch struct {
|
||||
// Dst is the IP prefix that the destination IP address matches against
|
||||
// to get the capability.
|
||||
Dst netip.Prefix
|
||||
|
||||
// Cap is the capability that's granted if the destination IP addresses
|
||||
// matches Dst.
|
||||
Cap tailcfg.PeerCapability
|
||||
|
||||
// Values are the raw JSON values of the capability.
|
||||
// See tailcfg.PeerCapability and tailcfg.PeerCapMap for details.
|
||||
Values []tailcfg.RawMessage
|
||||
}
|
||||
|
||||
// Match matches packets from any IP address in Srcs to any ip:port in
|
||||
// Dsts.
|
||||
type Match struct {
|
||||
IPProto []ipproto.Proto // required set (no default value at this layer)
|
||||
Srcs []netip.Prefix
|
||||
SrcsContains func(netip.Addr) bool `json:"-"` // report whether Addr is in Srcs
|
||||
Dsts []NetPortRange // optional, if Srcs match
|
||||
Caps []CapMatch // optional, if Srcs match
|
||||
}
|
||||
|
||||
func (m Match) String() string {
|
||||
// TODO(bradfitz): use strings.Builder, add String tests
|
||||
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=>%v", m.IPProto, ss, ds)
|
||||
}
|
||||
|
||||
type matches []Match
|
||||
type matches []filtertype.Match
|
||||
|
||||
func (ms matches) match(q *packet.Parsed) bool {
|
||||
for _, m := range ms {
|
||||
@ -112,7 +24,7 @@ func (ms matches) match(q *packet.Parsed) bool {
|
||||
if !dst.Net.Contains(q.Dst.Addr()) {
|
||||
continue
|
||||
}
|
||||
if !dst.Ports.contains(q.Dst.Port()) {
|
||||
if !dst.Ports.Contains(q.Dst.Port()) {
|
||||
continue
|
||||
}
|
||||
return true
|
||||
@ -147,7 +59,7 @@ func (ms matches) matchProtoAndIPsOnlyIfAllPorts(q *packet.Parsed) bool {
|
||||
continue
|
||||
}
|
||||
for _, dst := range m.Dsts {
|
||||
if dst.Ports != allPorts {
|
||||
if dst.Ports != filtertype.AllPorts {
|
||||
continue
|
||||
}
|
||||
if dst.Net.Contains(q.Dst.Addr()) {
|
||||
|
@ -9,8 +9,8 @@
|
||||
"strings"
|
||||
|
||||
"go4.org/netipx"
|
||||
"tailscale.com/net/ipset"
|
||||
"tailscale.com/net/netaddr"
|
||||
"tailscale.com/net/tsaddr"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/ipproto"
|
||||
"tailscale.com/types/views"
|
||||
@ -63,7 +63,7 @@ func MatchesFromFilterRules(pf []tailcfg.FilterRule) ([]Match, error) {
|
||||
}
|
||||
m.Srcs = append(m.Srcs, nets...)
|
||||
}
|
||||
m.SrcsContains = tsaddr.NewContainsIPFunc(views.SliceOf(m.Srcs))
|
||||
m.SrcsContains = ipset.NewContainsIPFunc(views.SliceOf(m.Srcs))
|
||||
|
||||
for _, d := range r.DstPorts {
|
||||
nets, err := parseIPSet(d.IP, d.Bits)
|
||||
|
@ -39,6 +39,7 @@
|
||||
"tailscale.com/ipn/ipnlocal"
|
||||
"tailscale.com/metrics"
|
||||
"tailscale.com/net/dns"
|
||||
"tailscale.com/net/ipset"
|
||||
"tailscale.com/net/netaddr"
|
||||
"tailscale.com/net/packet"
|
||||
"tailscale.com/net/tsaddr"
|
||||
@ -330,7 +331,7 @@ func Create(logf logger.Logf, tundev *tstun.Wrapper, e wgengine.Engine, mc *magi
|
||||
driveForLocal: driveForLocal,
|
||||
}
|
||||
ns.ctx, ns.ctxCancel = context.WithCancel(context.Background())
|
||||
ns.atomicIsLocalIPFunc.Store(tsaddr.FalseContainsIPFunc())
|
||||
ns.atomicIsLocalIPFunc.Store(ipset.FalseContainsIPFunc())
|
||||
ns.tundev.PostFilterPacketInboundFromWireGuard = ns.injectInbound
|
||||
ns.tundev.PreFilterPacketOutboundToWireGuardNetstackIntercept = ns.handleLocalPackets
|
||||
stacksForMetrics.Store(ns, struct{}{})
|
||||
@ -568,10 +569,10 @@ func ipPrefixToAddressWithPrefix(ipp netip.Prefix) tcpip.AddressWithPrefix {
|
||||
func (ns *Impl) UpdateNetstackIPs(nm *netmap.NetworkMap) {
|
||||
var selfNode tailcfg.NodeView
|
||||
if nm != nil {
|
||||
ns.atomicIsLocalIPFunc.Store(tsaddr.NewContainsIPFunc(nm.GetAddresses()))
|
||||
ns.atomicIsLocalIPFunc.Store(ipset.NewContainsIPFunc(nm.GetAddresses()))
|
||||
selfNode = nm.SelfNode
|
||||
} else {
|
||||
ns.atomicIsLocalIPFunc.Store(tsaddr.FalseContainsIPFunc())
|
||||
ns.atomicIsLocalIPFunc.Store(ipset.FalseContainsIPFunc())
|
||||
}
|
||||
|
||||
oldPfx := make(map[netip.Prefix]bool)
|
||||
|
@ -27,6 +27,7 @@
|
||||
"tailscale.com/ipn/ipnstate"
|
||||
"tailscale.com/net/dns"
|
||||
"tailscale.com/net/flowtrack"
|
||||
"tailscale.com/net/ipset"
|
||||
"tailscale.com/net/netmon"
|
||||
"tailscale.com/net/packet"
|
||||
"tailscale.com/net/sockstats"
|
||||
@ -330,8 +331,8 @@ func NewUserspaceEngine(logf logger.Logf, conf Config) (_ Engine, reterr error)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
e.isLocalAddr.Store(tsaddr.FalseContainsIPFunc())
|
||||
e.isDNSIPOverTailscale.Store(tsaddr.FalseContainsIPFunc())
|
||||
e.isLocalAddr.Store(ipset.FalseContainsIPFunc())
|
||||
e.isDNSIPOverTailscale.Store(ipset.FalseContainsIPFunc())
|
||||
|
||||
if conf.NetMon != nil {
|
||||
e.netMon = conf.NetMon
|
||||
@ -854,7 +855,7 @@ func (e *userspaceEngine) Reconfig(cfg *wgcfg.Config, routerCfg *router.Config,
|
||||
panic("dnsCfg must not be nil")
|
||||
}
|
||||
|
||||
e.isLocalAddr.Store(tsaddr.NewContainsIPFunc(views.SliceOf(routerCfg.LocalAddrs)))
|
||||
e.isLocalAddr.Store(ipset.NewContainsIPFunc(views.SliceOf(routerCfg.LocalAddrs)))
|
||||
|
||||
e.wgLock.Lock()
|
||||
defer e.wgLock.Unlock()
|
||||
@ -912,7 +913,7 @@ func (e *userspaceEngine) Reconfig(cfg *wgcfg.Config, routerCfg *router.Config,
|
||||
// instead have ipnlocal populate a map of DNS IP => linkName and
|
||||
// put that in the *dns.Config instead, and plumb it down to the
|
||||
// dns.Manager. Maybe also with isLocalAddr above.
|
||||
e.isDNSIPOverTailscale.Store(tsaddr.NewContainsIPFunc(views.SliceOf(dnsIPsOverTailscale(dnsCfg, routerCfg))))
|
||||
e.isDNSIPOverTailscale.Store(ipset.NewContainsIPFunc(views.SliceOf(dnsIPsOverTailscale(dnsCfg, routerCfg))))
|
||||
|
||||
// See if any peers have changed disco keys, which means they've restarted.
|
||||
// If so, we need to update the wireguard-go/device.Device in two phases:
|
||||
|
Loading…
Reference in New Issue
Block a user