net/packet, wgengine/filter: support SCTP

Add proto to flowtrack.Tuple.

Add types/ipproto leaf package to break a cycle.

Server-side ACL work remains.

Updates #1516

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick
2021-03-19 21:05:51 -07:00
committed by Brad Fitzpatrick
parent 90a6fb7ffe
commit 01b90df2fa
17 changed files with 154 additions and 62 deletions

View File

@@ -14,6 +14,7 @@ import (
"inet.af/netaddr"
"tailscale.com/net/flowtrack"
"tailscale.com/net/packet"
"tailscale.com/types/ipproto"
"tailscale.com/types/logger"
)
@@ -352,18 +353,18 @@ func (f *Filter) runIn4(q *packet.Parsed) (r Response, why string) {
if f.matches4.match(q) {
return Accept, "tcp ok"
}
case packet.UDP:
t := flowtrack.Tuple{Src: q.Src, Dst: q.Dst}
case packet.UDP, packet.SCTP:
t := flowtrack.Tuple{Proto: q.IPProto, Src: q.Src, Dst: q.Dst}
f.state.mu.Lock()
_, ok := f.state.lru.Get(t)
f.state.mu.Unlock()
if ok {
return Accept, "udp cached"
return Accept, "cached"
}
if f.matches4.match(q) {
return Accept, "udp ok"
return Accept, "ok"
}
case packet.TSMP:
return Accept, "tsmp ok"
@@ -409,18 +410,18 @@ func (f *Filter) runIn6(q *packet.Parsed) (r Response, why string) {
if f.matches6.match(q) {
return Accept, "tcp ok"
}
case packet.UDP:
t := flowtrack.Tuple{Src: q.Src, Dst: q.Dst}
case packet.UDP, packet.SCTP:
t := flowtrack.Tuple{Proto: q.IPProto, Src: q.Src, Dst: q.Dst}
f.state.mu.Lock()
_, ok := f.state.lru.Get(t)
f.state.mu.Unlock()
if ok {
return Accept, "udp cached"
return Accept, "cached"
}
if f.matches6.match(q) {
return Accept, "udp ok"
return Accept, "ok"
}
default:
return Drop, "Unknown proto"
@@ -430,15 +431,16 @@ func (f *Filter) runIn6(q *packet.Parsed) (r Response, why string) {
// runIn runs the output-specific part of the filter logic.
func (f *Filter) runOut(q *packet.Parsed) (r Response, why string) {
if q.IPProto != packet.UDP {
return Accept, "ok out"
switch q.IPProto {
case ipproto.UDP, ipproto.SCTP:
tuple := flowtrack.Tuple{
Proto: q.IPProto,
Src: q.Dst, Dst: q.Src, // src/dst reversed
}
f.state.mu.Lock()
f.state.lru.Add(tuple, nil)
f.state.mu.Unlock()
}
tuple := flowtrack.Tuple{Src: q.Dst, Dst: q.Src} // src/dst reversed
f.state.mu.Lock()
f.state.lru.Add(tuple, nil)
f.state.mu.Unlock()
return Accept, "ok out"
}

View File

@@ -18,19 +18,24 @@ import (
"tailscale.com/net/packet"
"tailscale.com/net/tsaddr"
"tailscale.com/tailcfg"
"tailscale.com/types/ipproto"
"tailscale.com/types/logger"
)
func newFilter(logf logger.Logf) *Filter {
m := func(srcs []netaddr.IPPrefix, dsts []NetPortRange) Match {
m := func(srcs []netaddr.IPPrefix, dsts []NetPortRange, protos ...ipproto.Proto) Match {
if protos == nil {
protos = defaultProtos
}
return Match{
IPProto: defaultProtos,
IPProto: protos,
Srcs: srcs,
Dsts: dsts,
}
}
matches := []Match{
m(nets("8.1.1.1", "8.2.2.2"), netports("1.2.3.4:22", "5.6.7.8:23-24")),
m(nets("9.1.1.1", "9.2.2.2"), netports("1.2.3.4:22", "5.6.7.8:23-24"), packet.SCTP),
m(nets("8.1.1.1", "8.2.2.2"), netports("5.6.7.8:27-28")),
m(nets("2.2.2.2"), netports("8.1.1.1:22")),
m(nets("0.0.0.0/0"), netports("100.122.98.50:*")),
@@ -100,7 +105,9 @@ func TestFilter(t *testing.T) {
{Drop, parsed(packet.TCP, "1::", "2602::1", 0, 443)},
// Don't allow protocols not specified by filter
{Drop, parsed(132 /* SCTP */, "8.1.1.1", "1.2.3.4", 999, 22)},
{Drop, parsed(packet.SCTP, "8.1.1.1", "1.2.3.4", 999, 22)},
// But SCTP is allowed for 9.1.1.1
{Accept, parsed(packet.SCTP, "9.1.1.1", "1.2.3.4", 999, 22)},
}
for i, test := range tests {
aclFunc := acl.runIn4
@@ -532,7 +539,7 @@ func mustIP(s string) netaddr.IP {
return ip
}
func parsed(proto packet.IPProto, src, dst string, sport, dport uint16) packet.Parsed {
func parsed(proto ipproto.Proto, src, dst string, sport, dport uint16) packet.Parsed {
sip, dip := mustIP(src), mustIP(dst)
var ret packet.Parsed
@@ -553,7 +560,7 @@ func parsed(proto packet.IPProto, src, dst string, sport, dport uint16) packet.P
return ret
}
func raw6(proto packet.IPProto, src, dst string, sport, dport uint16, trimLen int) []byte {
func raw6(proto ipproto.Proto, src, dst string, sport, dport uint16, trimLen int) []byte {
u := packet.UDP6Header{
IP6Header: packet.IP6Header{
Src: mustIP(src),
@@ -582,7 +589,7 @@ func raw6(proto packet.IPProto, src, dst string, sport, dport uint16, trimLen in
}
}
func raw4(proto packet.IPProto, src, dst string, sport, dport uint16, trimLength int) []byte {
func raw4(proto ipproto.Proto, src, dst string, sport, dport uint16, trimLength int) []byte {
u := packet.UDP4Header{
IP4Header: packet.IP4Header{
Src: mustIP(src),
@@ -622,7 +629,7 @@ func raw4(proto packet.IPProto, src, dst string, sport, dport uint16, trimLength
}
}
func raw4default(proto packet.IPProto, trimLength int) []byte {
func raw4default(proto ipproto.Proto, trimLength int) []byte {
return raw4(proto, "8.8.8.8", "8.8.8.8", 53, 53, trimLength)
}
@@ -743,7 +750,7 @@ func TestMatchesFromFilterRules(t *testing.T) {
},
want: []Match{
{
IPProto: []packet.IPProto{
IPProto: []ipproto.Proto{
packet.TCP,
packet.UDP,
packet.ICMPv4,
@@ -779,7 +786,7 @@ func TestMatchesFromFilterRules(t *testing.T) {
},
want: []Match{
{
IPProto: []packet.IPProto{
IPProto: []ipproto.Proto{
packet.TCP,
},
Dsts: []NetPortRange{

View File

@@ -10,6 +10,7 @@ import (
"inet.af/netaddr"
"tailscale.com/net/packet"
"tailscale.com/types/ipproto"
)
//go:generate go run tailscale.com/cmd/cloner --type=Match --output=match_clone.go
@@ -47,7 +48,7 @@ func (npr NetPortRange) String() string {
// Match matches packets from any IP address in Srcs to any ip:port in
// Dsts.
type Match struct {
IPProto []packet.IPProto // required set (no default value at this layer)
IPProto []ipproto.Proto // required set (no default value at this layer)
Dsts []NetPortRange
Srcs []netaddr.IPPrefix
}
@@ -123,7 +124,7 @@ func ipInList(ip netaddr.IP, netlist []netaddr.IPPrefix) bool {
return false
}
func protoInList(proto packet.IPProto, valid []packet.IPProto) bool {
func protoInList(proto ipproto.Proto, valid []ipproto.Proto) bool {
for _, v := range valid {
if proto == v {
return true

View File

@@ -8,7 +8,7 @@ package filter
import (
"inet.af/netaddr"
"tailscale.com/net/packet"
"tailscale.com/types/ipproto"
)
// Clone makes a deep copy of Match.
@@ -28,7 +28,7 @@ func (src *Match) Clone() *Match {
// A compilation failure here means this code must be regenerated, with command:
// tailscale.com/cmd/cloner -type Match
var _MatchNeedsRegeneration = Match(struct {
IPProto []packet.IPProto
IPProto []ipproto.Proto
Dsts []NetPortRange
Srcs []netaddr.IPPrefix
}{})

View File

@@ -11,9 +11,10 @@ import (
"inet.af/netaddr"
"tailscale.com/net/packet"
"tailscale.com/tailcfg"
"tailscale.com/types/ipproto"
)
var defaultProtos = []packet.IPProto{
var defaultProtos = []ipproto.Proto{
packet.TCP,
packet.UDP,
packet.ICMPv4,
@@ -31,12 +32,12 @@ func MatchesFromFilterRules(pf []tailcfg.FilterRule) ([]Match, error) {
m := Match{}
if len(r.IPProto) == 0 {
m.IPProto = append([]packet.IPProto(nil), defaultProtos...)
m.IPProto = append([]ipproto.Proto(nil), defaultProtos...)
} else {
m.IPProto = make([]packet.IPProto, 0, len(r.IPProto))
m.IPProto = make([]ipproto.Proto, 0, len(r.IPProto))
for _, n := range r.IPProto {
if n >= 0 && n <= 0xff {
m.IPProto = append(m.IPProto, packet.IPProto(n))
m.IPProto = append(m.IPProto, ipproto.Proto(n))
}
}
}

View File

@@ -90,7 +90,7 @@ func (e *userspaceEngine) trackOpenPreFilterIn(pp *packet.Parsed, t *tstun.TUN)
// Either a SYN or a RST came back. Remove it in either case.
f := flowtrack.Tuple{Dst: pp.Src, Src: pp.Dst} // src/dst reversed
f := flowtrack.Tuple{Proto: pp.IPProto, Dst: pp.Src, Src: pp.Dst} // src/dst reversed
removed := e.removeFlow(f)
if removed && pp.TCPFlags&packet.TCPRst != 0 {
e.logf("open-conn-track: flow TCP %v got RST by peer", f)
@@ -107,7 +107,7 @@ func (e *userspaceEngine) trackOpenPostFilterOut(pp *packet.Parsed, t *tstun.TUN
return
}
flow := flowtrack.Tuple{Src: pp.Src, Dst: pp.Dst}
flow := flowtrack.Tuple{Proto: pp.IPProto, Src: pp.Src, Dst: pp.Dst}
// iOS likes to probe Apple IPs on all interfaces to check for connectivity.
// Don't start timers tracking those. They won't succeed anyway. Avoids log spam

View File

@@ -16,6 +16,7 @@ import (
"github.com/tailscale/wireguard-go/tun/tuntest"
"inet.af/netaddr"
"tailscale.com/net/packet"
"tailscale.com/types/ipproto"
"tailscale.com/types/logger"
"tailscale.com/wgengine/filter"
)
@@ -106,7 +107,7 @@ func netports(netPorts ...string) (ret []filter.NetPortRange) {
}
func setfilter(logf logger.Logf, tun *TUN) {
protos := []packet.IPProto{
protos := []ipproto.Proto{
packet.TCP,
packet.UDP,
}