mirror of
https://github.com/tailscale/tailscale.git
synced 2025-08-12 13:48:01 +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:

committed by
Brad Fitzpatrick

parent
36b1b4af2f
commit
86e0f9b912
@@ -14,9 +14,9 @@ import (
|
||||
"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 @@ import (
|
||||
"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 @@ const (
|
||||
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 @@ import (
|
||||
"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 @@ import (
|
||||
"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 @@ import (
|
||||
"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)
|
||||
|
Reference in New Issue
Block a user