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>
pull/1560/head
Brad Fitzpatrick 4 years ago committed by Brad Fitzpatrick
parent 90a6fb7ffe
commit 01b90df2fa

@ -39,6 +39,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
tailscale.com/tailcfg from tailscale.com/cmd/tailscale/cli+ tailscale.com/tailcfg from tailscale.com/cmd/tailscale/cli+
W tailscale.com/tsconst from tailscale.com/net/interfaces W tailscale.com/tsconst from tailscale.com/net/interfaces
tailscale.com/types/empty from tailscale.com/ipn tailscale.com/types/empty from tailscale.com/ipn
tailscale.com/types/ipproto from tailscale.com/net/flowtrack+
tailscale.com/types/key from tailscale.com/derp+ tailscale.com/types/key from tailscale.com/derp+
tailscale.com/types/logger from tailscale.com/cmd/tailscale/cli+ tailscale.com/types/logger from tailscale.com/cmd/tailscale/cli+
tailscale.com/types/netmap from tailscale.com/ipn tailscale.com/types/netmap from tailscale.com/ipn

@ -113,6 +113,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
tailscale.com/tstime from tailscale.com/wgengine/magicsock tailscale.com/tstime from tailscale.com/wgengine/magicsock
tailscale.com/types/empty from tailscale.com/control/controlclient+ tailscale.com/types/empty from tailscale.com/control/controlclient+
tailscale.com/types/flagtype from tailscale.com/cmd/tailscaled tailscale.com/types/flagtype from tailscale.com/cmd/tailscaled
tailscale.com/types/ipproto from tailscale.com/net/flowtrack+
tailscale.com/types/key from tailscale.com/derp+ tailscale.com/types/key from tailscale.com/derp+
tailscale.com/types/logger from tailscale.com/cmd/tailscaled+ tailscale.com/types/logger from tailscale.com/cmd/tailscaled+
tailscale.com/types/netmap from tailscale.com/control/controlclient+ tailscale.com/types/netmap from tailscale.com/control/controlclient+

@ -15,16 +15,18 @@ import (
"fmt" "fmt"
"inet.af/netaddr" "inet.af/netaddr"
"tailscale.com/types/ipproto"
) )
// Tuple is a 4-tuple of source and destination IP and port. // Tuple is a 5-tuple of proto, source and destination IP and port.
type Tuple struct { type Tuple struct {
Proto ipproto.Proto
Src netaddr.IPPort Src netaddr.IPPort
Dst netaddr.IPPort Dst netaddr.IPPort
} }
func (t Tuple) String() string { func (t Tuple) String() string {
return fmt.Sprintf("(%v => %v)", t.Src, t.Dst) return fmt.Sprintf("(%v %v => %v)", t.Proto, t.Src, t.Dst)
} }
// Cache is an LRU cache keyed by Tuple. // Cache is an LRU cache keyed by Tuple.

@ -10,6 +10,7 @@ import (
) )
const tcpHeaderLength = 20 const tcpHeaderLength = 20
const sctpHeaderLength = 12
// maxPacketLength is the largest length that all headers support. // maxPacketLength is the largest length that all headers support.
// IPv4 headers using uint16 for this forces an upper bound of 64KB. // IPv4 headers using uint16 for this forces an upper bound of 64KB.

@ -9,6 +9,7 @@ import (
"errors" "errors"
"inet.af/netaddr" "inet.af/netaddr"
"tailscale.com/types/ipproto"
) )
// ip4HeaderLength is the length of an IPv4 header with no IP options. // ip4HeaderLength is the length of an IPv4 header with no IP options.
@ -16,7 +17,7 @@ const ip4HeaderLength = 20
// IP4Header represents an IPv4 packet header. // IP4Header represents an IPv4 packet header.
type IP4Header struct { type IP4Header struct {
IPProto IPProto IPProto ipproto.Proto
IPID uint16 IPID uint16
Src netaddr.IP Src netaddr.IP
Dst netaddr.IP Dst netaddr.IP

@ -8,6 +8,7 @@ import (
"encoding/binary" "encoding/binary"
"inet.af/netaddr" "inet.af/netaddr"
"tailscale.com/types/ipproto"
) )
// ip6HeaderLength is the length of an IPv6 header with no IP options. // ip6HeaderLength is the length of an IPv6 header with no IP options.
@ -15,7 +16,7 @@ const ip6HeaderLength = 40
// IP6Header represents an IPv6 packet header. // IP6Header represents an IPv6 packet header.
type IP6Header struct { type IP6Header struct {
IPProto IPProto IPProto ipproto.Proto
IPID uint32 // only lower 20 bits used IPID uint32 // only lower 20 bits used
Src netaddr.IP Src netaddr.IP
Dst netaddr.IP Dst netaddr.IP

@ -11,9 +11,22 @@ import (
"strings" "strings"
"inet.af/netaddr" "inet.af/netaddr"
"tailscale.com/types/ipproto"
"tailscale.com/types/strbuilder" "tailscale.com/types/strbuilder"
) )
const (
Unknown = ipproto.Unknown
TCP = ipproto.TCP
UDP = ipproto.UDP
SCTP = ipproto.SCTP
IGMP = ipproto.IGMP
ICMPv4 = ipproto.ICMPv4
ICMPv6 = ipproto.ICMPv6
TSMP = ipproto.TSMP
Fragment = ipproto.Fragment
)
// RFC1858: prevent overlapping fragment attacks. // RFC1858: prevent overlapping fragment attacks.
const minFrag = 60 + 20 // max IPv4 header + basic TCP header const minFrag = 60 + 20 // max IPv4 header + basic TCP header
@ -44,7 +57,7 @@ type Parsed struct {
// 6), or 0 if the packet doesn't look like IPv4 or IPv6. // 6), or 0 if the packet doesn't look like IPv4 or IPv6.
IPVersion uint8 IPVersion uint8
// IPProto is the IP subprotocol (UDP, TCP, etc.). Valid iff IPVersion != 0. // IPProto is the IP subprotocol (UDP, TCP, etc.). Valid iff IPVersion != 0.
IPProto IPProto IPProto ipproto.Proto
// SrcIP4 is the source address. Family matches IPVersion. Port is // SrcIP4 is the source address. Family matches IPVersion. Port is
// valid iff IPProto == TCP || IPProto == UDP. // valid iff IPProto == TCP || IPProto == UDP.
Src netaddr.IPPort Src netaddr.IPPort
@ -130,7 +143,7 @@ func (q *Parsed) decode4(b []byte) {
} }
// Check that it's IPv4. // Check that it's IPv4.
q.IPProto = IPProto(b[9]) q.IPProto = ipproto.Proto(b[9])
q.length = int(binary.BigEndian.Uint16(b[2:4])) q.length = int(binary.BigEndian.Uint16(b[2:4]))
if len(b) < q.length { if len(b) < q.length {
// Packet was cut off before full IPv4 length. // Packet was cut off before full IPv4 length.
@ -210,6 +223,14 @@ func (q *Parsed) decode4(b []byte) {
q.Dst.Port = binary.BigEndian.Uint16(sub[2:4]) q.Dst.Port = binary.BigEndian.Uint16(sub[2:4])
q.dataofs = q.subofs + udpHeaderLength q.dataofs = q.subofs + udpHeaderLength
return return
case SCTP:
if len(sub) < sctpHeaderLength {
q.IPProto = Unknown
return
}
q.Src.Port = binary.BigEndian.Uint16(sub[0:2])
q.Dst.Port = binary.BigEndian.Uint16(sub[2:4])
return
case TSMP: case TSMP:
// Inter-tailscale messages. // Inter-tailscale messages.
q.dataofs = q.subofs q.dataofs = q.subofs
@ -244,7 +265,7 @@ func (q *Parsed) decode6(b []byte) {
return return
} }
q.IPProto = IPProto(b[6]) q.IPProto = ipproto.Proto(b[6])
q.length = int(binary.BigEndian.Uint16(b[4:6])) + ip6HeaderLength q.length = int(binary.BigEndian.Uint16(b[4:6])) + ip6HeaderLength
if len(b) < q.length { if len(b) < q.length {
// Packet was cut off before the full IPv6 length. // Packet was cut off before the full IPv6 length.
@ -301,6 +322,14 @@ func (q *Parsed) decode6(b []byte) {
q.Src.Port = binary.BigEndian.Uint16(sub[0:2]) q.Src.Port = binary.BigEndian.Uint16(sub[0:2])
q.Dst.Port = binary.BigEndian.Uint16(sub[2:4]) q.Dst.Port = binary.BigEndian.Uint16(sub[2:4])
q.dataofs = q.subofs + udpHeaderLength q.dataofs = q.subofs + udpHeaderLength
case SCTP:
if len(sub) < sctpHeaderLength {
q.IPProto = Unknown
return
}
q.Src.Port = binary.BigEndian.Uint16(sub[0:2])
q.Dst.Port = binary.BigEndian.Uint16(sub[2:4])
return
case TSMP: case TSMP:
// Inter-tailscale messages. // Inter-tailscale messages.
q.dataofs = q.subofs q.dataofs = q.subofs

@ -305,6 +305,39 @@ var ipv4TSMPDecode = Parsed{
Dst: mustIPPort("100.74.70.3:0"), Dst: mustIPPort("100.74.70.3:0"),
} }
// IPv4 SCTP
var sctpBuffer = []byte{
// IPv4 header:
0x45, 0x00,
0x00, 0x20, // 20 + 12 bytes total
0x00, 0x00, // ID
0x00, 0x00, // Fragment
0x40, // TTL
byte(SCTP),
// Checksum, unchecked:
1, 2,
// source IP:
0x64, 0x5e, 0x0c, 0x0e,
// dest IP:
0x64, 0x4a, 0x46, 0x03,
// Src Port, Dest Port:
0x00, 0x7b, 0x01, 0xc8,
// Verification tag:
1, 2, 3, 4,
// Checksum: (unchecked)
5, 6, 7, 8,
}
var sctpDecode = Parsed{
b: sctpBuffer,
subofs: 20,
length: 20 + 12,
IPVersion: 4,
IPProto: SCTP,
Src: mustIPPort("100.94.12.14:123"),
Dst: mustIPPort("100.74.70.3:456"),
}
func TestParsedString(t *testing.T) { func TestParsedString(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
@ -320,6 +353,7 @@ func TestParsedString(t *testing.T) {
{"igmp", igmpPacketDecode, "IGMP{192.168.1.82:0 > 224.0.0.251:0}"}, {"igmp", igmpPacketDecode, "IGMP{192.168.1.82:0 > 224.0.0.251:0}"},
{"unknown", unknownPacketDecode, "Unknown{???}"}, {"unknown", unknownPacketDecode, "Unknown{???}"},
{"ipv4_tsmp", ipv4TSMPDecode, "TSMP{100.94.12.14:0 > 100.74.70.3:0}"}, {"ipv4_tsmp", ipv4TSMPDecode, "TSMP{100.94.12.14:0 > 100.74.70.3:0}"},
{"sctp", sctpDecode, "SCTP{100.94.12.14:123 > 100.74.70.3:456}"},
} }
for _, tt := range tests { for _, tt := range tests {
@ -357,6 +391,7 @@ func TestDecode(t *testing.T) {
{"unknown", unknownPacketBuffer, unknownPacketDecode}, {"unknown", unknownPacketBuffer, unknownPacketDecode},
{"invalid4", invalid4RequestBuffer, invalid4RequestDecode}, {"invalid4", invalid4RequestBuffer, invalid4RequestDecode},
{"ipv4_tsmp", ipv4TSMPBuffer, ipv4TSMPDecode}, {"ipv4_tsmp", ipv4TSMPBuffer, ipv4TSMPDecode},
{"ipv4_sctp", sctpBuffer, sctpDecode},
} }
for _, tt := range tests { for _, tt := range tests {

@ -17,6 +17,7 @@ import (
"inet.af/netaddr" "inet.af/netaddr"
"tailscale.com/net/flowtrack" "tailscale.com/net/flowtrack"
"tailscale.com/types/ipproto"
) )
// TailscaleRejectedHeader is a TSMP message that says that one // TailscaleRejectedHeader is a TSMP message that says that one
@ -39,7 +40,7 @@ type TailscaleRejectedHeader struct {
IPDst netaddr.IP // IPv4 or IPv6 header's dst IP IPDst netaddr.IP // IPv4 or IPv6 header's dst IP
Src netaddr.IPPort // rejected flow's src Src netaddr.IPPort // rejected flow's src
Dst netaddr.IPPort // rejected flow's dst Dst netaddr.IPPort // rejected flow's dst
Proto IPProto // proto that was rejected (TCP or UDP) Proto ipproto.Proto // proto that was rejected (TCP or UDP)
Reason TailscaleRejectReason // why the connection was rejected Reason TailscaleRejectReason // why the connection was rejected
// MaybeBroken is whether the rejection is non-terminal (the // MaybeBroken is whether the rejection is non-terminal (the
@ -57,7 +58,7 @@ type TailscaleRejectedHeader struct {
const rejectFlagBitMaybeBroken = 0x1 const rejectFlagBitMaybeBroken = 0x1
func (rh TailscaleRejectedHeader) Flow() flowtrack.Tuple { func (rh TailscaleRejectedHeader) Flow() flowtrack.Tuple {
return flowtrack.Tuple{Src: rh.Src, Dst: rh.Dst} return flowtrack.Tuple{Proto: rh.Proto, Src: rh.Src, Dst: rh.Dst}
} }
func (rh TailscaleRejectedHeader) String() string { func (rh TailscaleRejectedHeader) String() string {
@ -181,7 +182,7 @@ func (pp *Parsed) AsTailscaleRejectedHeader() (h TailscaleRejectedHeader, ok boo
return return
} }
h = TailscaleRejectedHeader{ h = TailscaleRejectedHeader{
Proto: IPProto(p[1]), Proto: ipproto.Proto(p[1]),
Reason: TailscaleRejectReason(p[2]), Reason: TailscaleRejectReason(p[2]),
IPSrc: pp.Src.IP, IPSrc: pp.Src.IP,
IPDst: pp.Dst.IP, IPDst: pp.Dst.IP,

@ -1,28 +1,32 @@
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved. // Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package packet // Package ipproto contains IP Protocol constants.
package ipproto
// IPProto is an IP subprotocol as defined by the IANA protocol import "fmt"
// Proto is an IP subprotocol as defined by the IANA protocol
// numbers list // numbers list
// (https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml), // (https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml),
// or the special values Unknown or Fragment. // or the special values Unknown or Fragment.
type IPProto uint8 type Proto uint8
const ( const (
// Unknown represents an unknown or unsupported protocol; it's // Unknown represents an unknown or unsupported protocol; it's
// deliberately the zero value. Strictly speaking the zero // deliberately the zero value. Strictly speaking the zero
// value is IPv6 hop-by-hop extensions, but we don't support // value is IPv6 hop-by-hop extensions, but we don't support
// those, so this is still technically correct. // those, so this is still technically correct.
Unknown IPProto = 0x00 Unknown Proto = 0x00
// Values from the IANA registry. // Values from the IANA registry.
ICMPv4 IPProto = 0x01 ICMPv4 Proto = 0x01
IGMP IPProto = 0x02 IGMP Proto = 0x02
ICMPv6 IPProto = 0x3a ICMPv6 Proto = 0x3a
TCP IPProto = 0x06 TCP Proto = 0x06
UDP IPProto = 0x11 UDP Proto = 0x11
SCTP Proto = 0x84
// TSMP is the Tailscale Message Protocol (our ICMP-ish // TSMP is the Tailscale Message Protocol (our ICMP-ish
// thing), an IP protocol used only between Tailscale nodes // thing), an IP protocol used only between Tailscale nodes
@ -33,7 +37,7 @@ const (
// scheme". We never accept these from the host OS stack nor // scheme". We never accept these from the host OS stack nor
// send them to the host network stack. It's only used between // send them to the host network stack. It's only used between
// nodes. // nodes.
TSMP IPProto = 99 TSMP Proto = 99
// Fragment represents any non-first IP fragment, for which we // Fragment represents any non-first IP fragment, for which we
// don't have the sub-protocol header (and therefore can't // don't have the sub-protocol header (and therefore can't
@ -41,11 +45,13 @@ const (
// //
// 0xFF is reserved in the IANA registry, so we steal it for // 0xFF is reserved in the IANA registry, so we steal it for
// internal use. // internal use.
Fragment IPProto = 0xFF Fragment Proto = 0xFF
) )
func (p IPProto) String() string { func (p Proto) String() string {
switch p { switch p {
case Unknown:
return "Unknown"
case Fragment: case Fragment:
return "Frag" return "Frag"
case ICMPv4: case ICMPv4:
@ -58,9 +64,11 @@ func (p IPProto) String() string {
return "UDP" return "UDP"
case TCP: case TCP:
return "TCP" return "TCP"
case SCTP:
return "SCTP"
case TSMP: case TSMP:
return "TSMP" return "TSMP"
default: default:
return "Unknown" return fmt.Sprintf("IPProto-%d", int(p))
} }
} }

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

@ -18,19 +18,24 @@ import (
"tailscale.com/net/packet" "tailscale.com/net/packet"
"tailscale.com/net/tsaddr" "tailscale.com/net/tsaddr"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/types/ipproto"
"tailscale.com/types/logger" "tailscale.com/types/logger"
) )
func newFilter(logf logger.Logf) *Filter { 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{ return Match{
IPProto: defaultProtos, IPProto: protos,
Srcs: srcs, Srcs: srcs,
Dsts: dsts, Dsts: dsts,
} }
} }
matches := []Match{ 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("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("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("2.2.2.2"), netports("8.1.1.1:22")),
m(nets("0.0.0.0/0"), netports("100.122.98.50:*")), 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)}, {Drop, parsed(packet.TCP, "1::", "2602::1", 0, 443)},
// Don't allow protocols not specified by filter // 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 { for i, test := range tests {
aclFunc := acl.runIn4 aclFunc := acl.runIn4
@ -532,7 +539,7 @@ func mustIP(s string) netaddr.IP {
return 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) sip, dip := mustIP(src), mustIP(dst)
var ret packet.Parsed var ret packet.Parsed
@ -553,7 +560,7 @@ func parsed(proto packet.IPProto, src, dst string, sport, dport uint16) packet.P
return ret 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{ u := packet.UDP6Header{
IP6Header: packet.IP6Header{ IP6Header: packet.IP6Header{
Src: mustIP(src), 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{ u := packet.UDP4Header{
IP4Header: packet.IP4Header{ IP4Header: packet.IP4Header{
Src: mustIP(src), 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) 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{ want: []Match{
{ {
IPProto: []packet.IPProto{ IPProto: []ipproto.Proto{
packet.TCP, packet.TCP,
packet.UDP, packet.UDP,
packet.ICMPv4, packet.ICMPv4,
@ -779,7 +786,7 @@ func TestMatchesFromFilterRules(t *testing.T) {
}, },
want: []Match{ want: []Match{
{ {
IPProto: []packet.IPProto{ IPProto: []ipproto.Proto{
packet.TCP, packet.TCP,
}, },
Dsts: []NetPortRange{ Dsts: []NetPortRange{

@ -10,6 +10,7 @@ import (
"inet.af/netaddr" "inet.af/netaddr"
"tailscale.com/net/packet" "tailscale.com/net/packet"
"tailscale.com/types/ipproto"
) )
//go:generate go run tailscale.com/cmd/cloner --type=Match --output=match_clone.go //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 // Match matches packets from any IP address in Srcs to any ip:port in
// Dsts. // Dsts.
type Match struct { 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 Dsts []NetPortRange
Srcs []netaddr.IPPrefix Srcs []netaddr.IPPrefix
} }
@ -123,7 +124,7 @@ func ipInList(ip netaddr.IP, netlist []netaddr.IPPrefix) bool {
return false return false
} }
func protoInList(proto packet.IPProto, valid []packet.IPProto) bool { func protoInList(proto ipproto.Proto, valid []ipproto.Proto) bool {
for _, v := range valid { for _, v := range valid {
if proto == v { if proto == v {
return true return true

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

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

@ -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. // 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) removed := e.removeFlow(f)
if removed && pp.TCPFlags&packet.TCPRst != 0 { if removed && pp.TCPFlags&packet.TCPRst != 0 {
e.logf("open-conn-track: flow TCP %v got RST by peer", f) 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 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. // 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 // Don't start timers tracking those. They won't succeed anyway. Avoids log spam

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

Loading…
Cancel
Save