From 01b90df2fa4f9101e4f0ae8334b00dd9c3ccc148 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Fri, 19 Mar 2021 21:05:51 -0700 Subject: [PATCH] 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 --- cmd/tailscale/depaware.txt | 1 + cmd/tailscaled/depaware.txt | 1 + net/flowtrack/flowtrack.go | 10 +++--- net/packet/header.go | 1 + net/packet/ip4.go | 3 +- net/packet/ip6.go | 3 +- net/packet/packet.go | 35 +++++++++++++++++-- net/packet/packet_test.go | 35 +++++++++++++++++++ net/packet/tsmp.go | 7 ++-- net/packet/ip.go => types/ipproto/ipproto.go | 36 ++++++++++++-------- wgengine/filter/filter.go | 34 +++++++++--------- wgengine/filter/filter_test.go | 25 +++++++++----- wgengine/filter/match.go | 5 +-- wgengine/filter/match_clone.go | 4 +-- wgengine/filter/tailcfg.go | 9 ++--- wgengine/pendopen.go | 4 +-- wgengine/tstun/tun_test.go | 3 +- 17 files changed, 154 insertions(+), 62 deletions(-) rename net/packet/ip.go => types/ipproto/ipproto.go (72%) diff --git a/cmd/tailscale/depaware.txt b/cmd/tailscale/depaware.txt index 16746622c..74ae1d54e 100644 --- a/cmd/tailscale/depaware.txt +++ b/cmd/tailscale/depaware.txt @@ -39,6 +39,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep tailscale.com/tailcfg from tailscale.com/cmd/tailscale/cli+ W tailscale.com/tsconst from tailscale.com/net/interfaces 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/logger from tailscale.com/cmd/tailscale/cli+ tailscale.com/types/netmap from tailscale.com/ipn diff --git a/cmd/tailscaled/depaware.txt b/cmd/tailscaled/depaware.txt index 7fc2c90fc..0d9df8cd8 100644 --- a/cmd/tailscaled/depaware.txt +++ b/cmd/tailscaled/depaware.txt @@ -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/types/empty from tailscale.com/control/controlclient+ 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/logger from tailscale.com/cmd/tailscaled+ tailscale.com/types/netmap from tailscale.com/control/controlclient+ diff --git a/net/flowtrack/flowtrack.go b/net/flowtrack/flowtrack.go index 5fcd4ab20..8387145d3 100644 --- a/net/flowtrack/flowtrack.go +++ b/net/flowtrack/flowtrack.go @@ -15,16 +15,18 @@ import ( "fmt" "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 { - Src netaddr.IPPort - Dst netaddr.IPPort + Proto ipproto.Proto + Src netaddr.IPPort + Dst netaddr.IPPort } 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. diff --git a/net/packet/header.go b/net/packet/header.go index 86680a5a7..5cf4ef650 100644 --- a/net/packet/header.go +++ b/net/packet/header.go @@ -10,6 +10,7 @@ import ( ) const tcpHeaderLength = 20 +const sctpHeaderLength = 12 // maxPacketLength is the largest length that all headers support. // IPv4 headers using uint16 for this forces an upper bound of 64KB. diff --git a/net/packet/ip4.go b/net/packet/ip4.go index 0240abaa1..2c090d9f1 100644 --- a/net/packet/ip4.go +++ b/net/packet/ip4.go @@ -9,6 +9,7 @@ import ( "errors" "inet.af/netaddr" + "tailscale.com/types/ipproto" ) // 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. type IP4Header struct { - IPProto IPProto + IPProto ipproto.Proto IPID uint16 Src netaddr.IP Dst netaddr.IP diff --git a/net/packet/ip6.go b/net/packet/ip6.go index 59f605b32..e181f1dde 100644 --- a/net/packet/ip6.go +++ b/net/packet/ip6.go @@ -8,6 +8,7 @@ import ( "encoding/binary" "inet.af/netaddr" + "tailscale.com/types/ipproto" ) // 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. type IP6Header struct { - IPProto IPProto + IPProto ipproto.Proto IPID uint32 // only lower 20 bits used Src netaddr.IP Dst netaddr.IP diff --git a/net/packet/packet.go b/net/packet/packet.go index a88a1af7a..60276e034 100644 --- a/net/packet/packet.go +++ b/net/packet/packet.go @@ -11,9 +11,22 @@ import ( "strings" "inet.af/netaddr" + "tailscale.com/types/ipproto" "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. 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. IPVersion uint8 // 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 // valid iff IPProto == TCP || IPProto == UDP. Src netaddr.IPPort @@ -130,7 +143,7 @@ func (q *Parsed) decode4(b []byte) { } // 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])) if len(b) < q.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.dataofs = q.subofs + udpHeaderLength 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: // Inter-tailscale messages. q.dataofs = q.subofs @@ -244,7 +265,7 @@ func (q *Parsed) decode6(b []byte) { return } - q.IPProto = IPProto(b[6]) + q.IPProto = ipproto.Proto(b[6]) q.length = int(binary.BigEndian.Uint16(b[4:6])) + ip6HeaderLength if len(b) < q.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.Dst.Port = binary.BigEndian.Uint16(sub[2:4]) 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: // Inter-tailscale messages. q.dataofs = q.subofs diff --git a/net/packet/packet_test.go b/net/packet/packet_test.go index 8bac5db4a..6d567b7fd 100644 --- a/net/packet/packet_test.go +++ b/net/packet/packet_test.go @@ -305,6 +305,39 @@ var ipv4TSMPDecode = Parsed{ 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) { tests := []struct { name string @@ -320,6 +353,7 @@ func TestParsedString(t *testing.T) { {"igmp", igmpPacketDecode, "IGMP{192.168.1.82:0 > 224.0.0.251:0}"}, {"unknown", unknownPacketDecode, "Unknown{???}"}, {"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 { @@ -357,6 +391,7 @@ func TestDecode(t *testing.T) { {"unknown", unknownPacketBuffer, unknownPacketDecode}, {"invalid4", invalid4RequestBuffer, invalid4RequestDecode}, {"ipv4_tsmp", ipv4TSMPBuffer, ipv4TSMPDecode}, + {"ipv4_sctp", sctpBuffer, sctpDecode}, } for _, tt := range tests { diff --git a/net/packet/tsmp.go b/net/packet/tsmp.go index 2346c9419..4da3c20e1 100644 --- a/net/packet/tsmp.go +++ b/net/packet/tsmp.go @@ -17,6 +17,7 @@ import ( "inet.af/netaddr" "tailscale.com/net/flowtrack" + "tailscale.com/types/ipproto" ) // 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 Src netaddr.IPPort // rejected flow's src 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 // MaybeBroken is whether the rejection is non-terminal (the @@ -57,7 +58,7 @@ type TailscaleRejectedHeader struct { const rejectFlagBitMaybeBroken = 0x1 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 { @@ -181,7 +182,7 @@ func (pp *Parsed) AsTailscaleRejectedHeader() (h TailscaleRejectedHeader, ok boo return } h = TailscaleRejectedHeader{ - Proto: IPProto(p[1]), + Proto: ipproto.Proto(p[1]), Reason: TailscaleRejectReason(p[2]), IPSrc: pp.Src.IP, IPDst: pp.Dst.IP, diff --git a/net/packet/ip.go b/types/ipproto/ipproto.go similarity index 72% rename from net/packet/ip.go rename to types/ipproto/ipproto.go index 34194f344..5c9d101f6 100644 --- a/net/packet/ip.go +++ b/types/ipproto/ipproto.go @@ -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 // 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 // (https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml), // or the special values Unknown or Fragment. -type IPProto uint8 +type Proto uint8 const ( // Unknown represents an unknown or unsupported protocol; it's // deliberately the zero value. Strictly speaking the zero // value is IPv6 hop-by-hop extensions, but we don't support // those, so this is still technically correct. - Unknown IPProto = 0x00 + Unknown Proto = 0x00 // Values from the IANA registry. - ICMPv4 IPProto = 0x01 - IGMP IPProto = 0x02 - ICMPv6 IPProto = 0x3a - TCP IPProto = 0x06 - UDP IPProto = 0x11 + ICMPv4 Proto = 0x01 + IGMP Proto = 0x02 + ICMPv6 Proto = 0x3a + TCP Proto = 0x06 + UDP Proto = 0x11 + SCTP Proto = 0x84 // TSMP is the Tailscale Message Protocol (our ICMP-ish // 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 // send them to the host network stack. It's only used between // nodes. - TSMP IPProto = 99 + TSMP Proto = 99 // Fragment represents any non-first IP fragment, for which we // 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 // internal use. - Fragment IPProto = 0xFF + Fragment Proto = 0xFF ) -func (p IPProto) String() string { +func (p Proto) String() string { switch p { + case Unknown: + return "Unknown" case Fragment: return "Frag" case ICMPv4: @@ -58,9 +64,11 @@ func (p IPProto) String() string { return "UDP" case TCP: return "TCP" + case SCTP: + return "SCTP" case TSMP: return "TSMP" default: - return "Unknown" + return fmt.Sprintf("IPProto-%d", int(p)) } } diff --git a/wgengine/filter/filter.go b/wgengine/filter/filter.go index ee11bd44d..876eb5369 100644 --- a/wgengine/filter/filter.go +++ b/wgengine/filter/filter.go @@ -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" } diff --git a/wgengine/filter/filter_test.go b/wgengine/filter/filter_test.go index 3b0748a8c..a067e7374 100644 --- a/wgengine/filter/filter_test.go +++ b/wgengine/filter/filter_test.go @@ -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{ diff --git a/wgengine/filter/match.go b/wgengine/filter/match.go index 799614104..a1b356113 100644 --- a/wgengine/filter/match.go +++ b/wgengine/filter/match.go @@ -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 diff --git a/wgengine/filter/match_clone.go b/wgengine/filter/match_clone.go index b031e1e3a..04874ddec 100644 --- a/wgengine/filter/match_clone.go +++ b/wgengine/filter/match_clone.go @@ -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 }{}) diff --git a/wgengine/filter/tailcfg.go b/wgengine/filter/tailcfg.go index 26b7ed3da..6fcf0e52c 100644 --- a/wgengine/filter/tailcfg.go +++ b/wgengine/filter/tailcfg.go @@ -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)) } } } diff --git a/wgengine/pendopen.go b/wgengine/pendopen.go index be1fa1468..12902c174 100644 --- a/wgengine/pendopen.go +++ b/wgengine/pendopen.go @@ -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 diff --git a/wgengine/tstun/tun_test.go b/wgengine/tstun/tun_test.go index 555324d19..062b63f81 100644 --- a/wgengine/tstun/tun_test.go +++ b/wgengine/tstun/tun_test.go @@ -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, }