From 1eb95c7e32e6fb8e66ddd41ae28a5f109f62227e Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Sat, 20 Mar 2021 21:45:47 -0700 Subject: [PATCH] net/packet, wgengine{,/filter}: remove net/packet IPProto forwarding consts Only use the ones in types/ipproto now. Signed-off-by: Brad Fitzpatrick --- net/packet/icmp4.go | 8 +- net/packet/packet.go | 86 +++++++++----------- net/packet/packet_test.go | 13 +++ net/packet/tsmp.go | 4 +- net/packet/udp4.go | 8 +- net/packet/udp6.go | 8 +- wgengine/filter/filter.go | 24 +++--- wgengine/filter/filter_test.go | 144 ++++++++++++++++----------------- wgengine/filter/tailcfg.go | 9 +-- wgengine/pendopen.go | 7 +- wgengine/tstun/tun.go | 3 +- wgengine/tstun/tun_test.go | 4 +- wgengine/userspace.go | 3 +- 13 files changed, 169 insertions(+), 152 deletions(-) diff --git a/net/packet/icmp4.go b/net/packet/icmp4.go index 8a1568114..da4774887 100644 --- a/net/packet/icmp4.go +++ b/net/packet/icmp4.go @@ -4,7 +4,11 @@ package packet -import "encoding/binary" +import ( + "encoding/binary" + + "tailscale.com/types/ipproto" +) // icmp4HeaderLength is the size of the ICMPv4 packet header, not // including the outer IP layer or the variable "response data" @@ -66,7 +70,7 @@ func (h ICMP4Header) Marshal(buf []byte) error { return errLargePacket } // The caller does not need to set this. - h.IPProto = ICMPv4 + h.IPProto = ipproto.ICMPv4 buf[20] = uint8(h.Type) buf[21] = uint8(h.Code) diff --git a/net/packet/packet.go b/net/packet/packet.go index 60276e034..91c92f641 100644 --- a/net/packet/packet.go +++ b/net/packet/packet.go @@ -15,17 +15,7 @@ import ( "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 -) +const unknown = ipproto.Unknown // RFC1858: prevent overlapping fragment attacks. const minFrag = 60 + 20 // max IPv4 header + basic TCP header @@ -113,7 +103,7 @@ func (q *Parsed) Decode(b []byte) { if len(b) < 1 { q.IPVersion = 0 - q.IPProto = Unknown + q.IPProto = unknown return } @@ -125,7 +115,7 @@ func (q *Parsed) Decode(b []byte) { q.decode6(b) default: q.IPVersion = 0 - q.IPProto = Unknown + q.IPProto = unknown } } @@ -138,7 +128,7 @@ func (q *Parsed) StuffForTesting(len int) { func (q *Parsed) decode4(b []byte) { if len(b) < ip4HeaderLength { q.IPVersion = 0 - q.IPProto = Unknown + q.IPProto = unknown return } @@ -147,7 +137,7 @@ func (q *Parsed) decode4(b []byte) { q.length = int(binary.BigEndian.Uint16(b[2:4])) if len(b) < q.length { // Packet was cut off before full IPv4 length. - q.IPProto = Unknown + q.IPProto = unknown return } @@ -158,7 +148,7 @@ func (q *Parsed) decode4(b []byte) { q.subofs = int((b[0] & 0x0F) << 2) if q.subofs > q.length { // next-proto starts beyond end of packet. - q.IPProto = Unknown + q.IPProto = unknown return } sub := b[q.subofs:] @@ -183,29 +173,29 @@ func (q *Parsed) decode4(b []byte) { // This is the first fragment if moreFrags && len(sub) < minFrag { // Suspiciously short first fragment, dump it. - q.IPProto = Unknown + q.IPProto = unknown return } // otherwise, this is either non-fragmented (the usual case) // or a big enough initial fragment that we can read the // whole subprotocol header. switch q.IPProto { - case ICMPv4: + case ipproto.ICMPv4: if len(sub) < icmp4HeaderLength { - q.IPProto = Unknown + q.IPProto = unknown return } q.Src.Port = 0 q.Dst.Port = 0 q.dataofs = q.subofs + icmp4HeaderLength return - case IGMP: + case ipproto.IGMP: // Keep IPProto, but don't parse anything else // out. return - case TCP: + case ipproto.TCP: if len(sub) < tcpHeaderLength { - q.IPProto = Unknown + q.IPProto = unknown return } q.Src.Port = binary.BigEndian.Uint16(sub[0:2]) @@ -214,29 +204,29 @@ func (q *Parsed) decode4(b []byte) { headerLength := (sub[12] & 0xF0) >> 2 q.dataofs = q.subofs + int(headerLength) return - case UDP: + case ipproto.UDP: if len(sub) < udpHeaderLength { - q.IPProto = Unknown + q.IPProto = unknown return } q.Src.Port = binary.BigEndian.Uint16(sub[0:2]) q.Dst.Port = binary.BigEndian.Uint16(sub[2:4]) q.dataofs = q.subofs + udpHeaderLength return - case SCTP: + case ipproto.SCTP: if len(sub) < sctpHeaderLength { - q.IPProto = Unknown + 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 ipproto.TSMP: // Inter-tailscale messages. q.dataofs = q.subofs return default: - q.IPProto = Unknown + q.IPProto = unknown return } } else { @@ -244,7 +234,7 @@ func (q *Parsed) decode4(b []byte) { if fragOfs < minFrag { // First frag was suspiciously short, so we can't // trust the followup either. - q.IPProto = Unknown + q.IPProto = unknown return } // otherwise, we have to permit the fragment to slide through. @@ -253,7 +243,7 @@ func (q *Parsed) decode4(b []byte) { // but that would require statefulness. Anyway, receivers' // kernels know to drop fragments where the initial fragment // doesn't arrive. - q.IPProto = Fragment + q.IPProto = ipproto.Fragment return } } @@ -261,7 +251,7 @@ func (q *Parsed) decode4(b []byte) { func (q *Parsed) decode6(b []byte) { if len(b) < ip6HeaderLength { q.IPVersion = 0 - q.IPProto = Unknown + q.IPProto = unknown return } @@ -269,7 +259,7 @@ func (q *Parsed) decode6(b []byte) { q.length = int(binary.BigEndian.Uint16(b[4:6])) + ip6HeaderLength if len(b) < q.length { // Packet was cut off before the full IPv6 length. - q.IPProto = Unknown + q.IPProto = unknown return } @@ -295,17 +285,17 @@ func (q *Parsed) decode6(b []byte) { sub = sub[:len(sub):len(sub)] // help the compiler do bounds check elimination switch q.IPProto { - case ICMPv6: + case ipproto.ICMPv6: if len(sub) < icmp6HeaderLength { - q.IPProto = Unknown + q.IPProto = unknown return } q.Src.Port = 0 q.Dst.Port = 0 q.dataofs = q.subofs + icmp6HeaderLength - case TCP: + case ipproto.TCP: if len(sub) < tcpHeaderLength { - q.IPProto = Unknown + q.IPProto = unknown return } q.Src.Port = binary.BigEndian.Uint16(sub[0:2]) @@ -314,28 +304,28 @@ func (q *Parsed) decode6(b []byte) { headerLength := (sub[12] & 0xF0) >> 2 q.dataofs = q.subofs + int(headerLength) return - case UDP: + case ipproto.UDP: if len(sub) < udpHeaderLength { - q.IPProto = Unknown + q.IPProto = unknown return } 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: + case ipproto.SCTP: if len(sub) < sctpHeaderLength { - q.IPProto = Unknown + 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 ipproto.TSMP: // Inter-tailscale messages. q.dataofs = q.subofs return default: - q.IPProto = Unknown + q.IPProto = unknown return } } @@ -396,13 +386,13 @@ func (q *Parsed) IsTCPSyn() bool { // IsError reports whether q is an ICMP "Error" packet. func (q *Parsed) IsError() bool { switch q.IPProto { - case ICMPv4: + case ipproto.ICMPv4: if len(q.b) < q.subofs+8 { return false } t := ICMP4Type(q.b[q.subofs]) return t == ICMP4Unreachable || t == ICMP4TimeExceeded - case ICMPv6: + case ipproto.ICMPv6: if len(q.b) < q.subofs+8 { return false } @@ -416,9 +406,9 @@ func (q *Parsed) IsError() bool { // IsEchoRequest reports whether q is an ICMP Echo Request. func (q *Parsed) IsEchoRequest() bool { switch q.IPProto { - case ICMPv4: + case ipproto.ICMPv4: return len(q.b) >= q.subofs+8 && ICMP4Type(q.b[q.subofs]) == ICMP4EchoRequest && ICMP4Code(q.b[q.subofs+1]) == ICMP4NoCode - case ICMPv6: + case ipproto.ICMPv6: return len(q.b) >= q.subofs+8 && ICMP6Type(q.b[q.subofs]) == ICMP6EchoRequest && ICMP6Code(q.b[q.subofs+1]) == ICMP6NoCode default: return false @@ -428,9 +418,9 @@ func (q *Parsed) IsEchoRequest() bool { // IsEchoRequest reports whether q is an IPv4 ICMP Echo Response. func (q *Parsed) IsEchoResponse() bool { switch q.IPProto { - case ICMPv4: + case ipproto.ICMPv4: return len(q.b) >= q.subofs+8 && ICMP4Type(q.b[q.subofs]) == ICMP4EchoReply && ICMP4Code(q.b[q.subofs+1]) == ICMP4NoCode - case ICMPv6: + case ipproto.ICMPv6: return len(q.b) >= q.subofs+8 && ICMP6Type(q.b[q.subofs]) == ICMP6EchoReply && ICMP6Code(q.b[q.subofs+1]) == ICMP6NoCode default: return false diff --git a/net/packet/packet_test.go b/net/packet/packet_test.go index 6d567b7fd..ac4fa33f3 100644 --- a/net/packet/packet_test.go +++ b/net/packet/packet_test.go @@ -10,6 +10,19 @@ import ( "testing" "inet.af/netaddr" + "tailscale.com/types/ipproto" +) + +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 ) func mustIPPort(s string) netaddr.IPPort { diff --git a/net/packet/tsmp.go b/net/packet/tsmp.go index 4da3c20e1..8d3c65d56 100644 --- a/net/packet/tsmp.go +++ b/net/packet/tsmp.go @@ -139,7 +139,7 @@ func (h TailscaleRejectedHeader) Marshal(buf []byte) error { } if h.Src.IP.Is4() { iph := IP4Header{ - IPProto: TSMP, + IPProto: ipproto.TSMP, Src: h.IPSrc, Dst: h.IPDst, } @@ -147,7 +147,7 @@ func (h TailscaleRejectedHeader) Marshal(buf []byte) error { buf = buf[ip4HeaderLength:] } else if h.Src.IP.Is6() { iph := IP6Header{ - IPProto: TSMP, + IPProto: ipproto.TSMP, Src: h.IPSrc, Dst: h.IPDst, } diff --git a/net/packet/udp4.go b/net/packet/udp4.go index 82aa30179..ce179f89d 100644 --- a/net/packet/udp4.go +++ b/net/packet/udp4.go @@ -4,7 +4,11 @@ package packet -import "encoding/binary" +import ( + "encoding/binary" + + "tailscale.com/types/ipproto" +) // udpHeaderLength is the size of the UDP packet header, not including // the outer IP header. @@ -31,7 +35,7 @@ func (h UDP4Header) Marshal(buf []byte) error { return errLargePacket } // The caller does not need to set this. - h.IPProto = UDP + h.IPProto = ipproto.UDP length := len(buf) - h.IP4Header.Len() binary.BigEndian.PutUint16(buf[20:22], h.SrcPort) diff --git a/net/packet/udp6.go b/net/packet/udp6.go index 0450eae9e..18213c1fb 100644 --- a/net/packet/udp6.go +++ b/net/packet/udp6.go @@ -4,7 +4,11 @@ package packet -import "encoding/binary" +import ( + "encoding/binary" + + "tailscale.com/types/ipproto" +) // UDP6Header is an IPv6+UDP header. type UDP6Header struct { @@ -27,7 +31,7 @@ func (h UDP6Header) Marshal(buf []byte) error { return errLargePacket } // The caller does not need to set this. - h.IPProto = UDP + h.IPProto = ipproto.UDP length := len(buf) - h.IP6Header.Len() binary.BigEndian.PutUint16(buf[40:42], h.SrcPort) diff --git a/wgengine/filter/filter.go b/wgengine/filter/filter.go index 876eb5369..e904b3905 100644 --- a/wgengine/filter/filter.go +++ b/wgengine/filter/filter.go @@ -268,7 +268,7 @@ func (f *Filter) CheckTCP(srcIP, dstIP netaddr.IP, dstPort uint16) Response { } pkt.Src.IP = srcIP pkt.Dst.IP = dstIP - pkt.IPProto = packet.TCP + pkt.IPProto = ipproto.TCP pkt.TCPFlags = packet.TCPSyn pkt.Src.Port = 0 pkt.Dst.Port = dstPort @@ -326,7 +326,7 @@ func (f *Filter) runIn4(q *packet.Parsed) (r Response, why string) { } switch q.IPProto { - case packet.ICMPv4: + case ipproto.ICMPv4: if q.IsEchoResponse() || q.IsError() { // ICMP responses are allowed. // TODO(apenwarr): consider using conntrack state. @@ -338,7 +338,7 @@ func (f *Filter) runIn4(q *packet.Parsed) (r Response, why string) { // If any port is open to an IP, allow ICMP to it. return Accept, "icmp ok" } - case packet.TCP: + case ipproto.TCP: // For TCP, we want to allow *outgoing* connections, // which means we want to allow return packets on those // connections. To make this restriction work, we need to @@ -353,7 +353,7 @@ func (f *Filter) runIn4(q *packet.Parsed) (r Response, why string) { if f.matches4.match(q) { return Accept, "tcp ok" } - case packet.UDP, packet.SCTP: + case ipproto.UDP, ipproto.SCTP: t := flowtrack.Tuple{Proto: q.IPProto, Src: q.Src, Dst: q.Dst} f.state.mu.Lock() @@ -366,7 +366,7 @@ func (f *Filter) runIn4(q *packet.Parsed) (r Response, why string) { if f.matches4.match(q) { return Accept, "ok" } - case packet.TSMP: + case ipproto.TSMP: return Accept, "tsmp ok" default: return Drop, "Unknown proto" @@ -383,7 +383,7 @@ func (f *Filter) runIn6(q *packet.Parsed) (r Response, why string) { } switch q.IPProto { - case packet.ICMPv6: + case ipproto.ICMPv6: if q.IsEchoResponse() || q.IsError() { // ICMP responses are allowed. // TODO(apenwarr): consider using conntrack state. @@ -395,7 +395,7 @@ func (f *Filter) runIn6(q *packet.Parsed) (r Response, why string) { // If any port is open to an IP, allow ICMP to it. return Accept, "icmp ok" } - case packet.TCP: + case ipproto.TCP: // For TCP, we want to allow *outgoing* connections, // which means we want to allow return packets on those // connections. To make this restriction work, we need to @@ -404,13 +404,13 @@ func (f *Filter) runIn6(q *packet.Parsed) (r Response, why string) { // can't be initiated without first sending a SYN. // It happens to also be much faster. // TODO(apenwarr): Skip the rest of decoding in this path? - if q.IPProto == packet.TCP && !q.IsTCPSyn() { + if q.IPProto == ipproto.TCP && !q.IsTCPSyn() { return Accept, "tcp non-syn" } if f.matches6.match(q) { return Accept, "tcp ok" } - case packet.UDP, packet.SCTP: + case ipproto.UDP, ipproto.SCTP: t := flowtrack.Tuple{Proto: q.IPProto, Src: q.Src, Dst: q.Dst} f.state.mu.Lock() @@ -488,11 +488,11 @@ func (f *Filter) pre(q *packet.Parsed, rf RunFlags, dir direction) Response { } switch q.IPProto { - case packet.Unknown: + case ipproto.Unknown: // Unknown packets are dangerous; always drop them. f.logRateLimit(rf, q, dir, Drop, "unknown") return Drop - case packet.Fragment: + case ipproto.Fragment: // Fragments after the first always need to be passed through. // Very small fragments are considered Junk by Parsed. f.logRateLimit(rf, q, dir, Accept, "fragment") @@ -516,5 +516,5 @@ func omitDropLogging(p *packet.Parsed, dir direction) bool { return false } - return p.Dst.IP.IsMulticast() || (p.Dst.IP.IsLinkLocalUnicast() && p.Dst.IP != gcpDNSAddr) || p.IPProto == packet.IGMP + return p.Dst.IP.IsMulticast() || (p.Dst.IP.IsLinkLocalUnicast() && p.Dst.IP != gcpDNSAddr) || p.IPProto == ipproto.IGMP } diff --git a/wgengine/filter/filter_test.go b/wgengine/filter/filter_test.go index a067e7374..ca807a5fd 100644 --- a/wgengine/filter/filter_test.go +++ b/wgengine/filter/filter_test.go @@ -35,7 +35,7 @@ func newFilter(logf logger.Logf) *Filter { } 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("9.1.1.1", "9.2.2.2"), netports("1.2.3.4:22", "5.6.7.8:23-24"), ipproto.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:*")), @@ -66,48 +66,48 @@ func TestFilter(t *testing.T) { } tests := []InOut{ // allow 8.1.1.1 => 1.2.3.4:22 - {Accept, parsed(packet.TCP, "8.1.1.1", "1.2.3.4", 999, 22)}, - {Accept, parsed(packet.ICMPv4, "8.1.1.1", "1.2.3.4", 0, 0)}, - {Drop, parsed(packet.TCP, "8.1.1.1", "1.2.3.4", 0, 0)}, - {Accept, parsed(packet.TCP, "8.1.1.1", "1.2.3.4", 0, 22)}, - {Drop, parsed(packet.TCP, "8.1.1.1", "1.2.3.4", 0, 21)}, + {Accept, parsed(ipproto.TCP, "8.1.1.1", "1.2.3.4", 999, 22)}, + {Accept, parsed(ipproto.ICMPv4, "8.1.1.1", "1.2.3.4", 0, 0)}, + {Drop, parsed(ipproto.TCP, "8.1.1.1", "1.2.3.4", 0, 0)}, + {Accept, parsed(ipproto.TCP, "8.1.1.1", "1.2.3.4", 0, 22)}, + {Drop, parsed(ipproto.TCP, "8.1.1.1", "1.2.3.4", 0, 21)}, // allow 8.2.2.2. => 1.2.3.4:22 - {Accept, parsed(packet.TCP, "8.2.2.2", "1.2.3.4", 0, 22)}, - {Drop, parsed(packet.TCP, "8.2.2.2", "1.2.3.4", 0, 23)}, - {Drop, parsed(packet.TCP, "8.3.3.3", "1.2.3.4", 0, 22)}, + {Accept, parsed(ipproto.TCP, "8.2.2.2", "1.2.3.4", 0, 22)}, + {Drop, parsed(ipproto.TCP, "8.2.2.2", "1.2.3.4", 0, 23)}, + {Drop, parsed(ipproto.TCP, "8.3.3.3", "1.2.3.4", 0, 22)}, // allow 8.1.1.1 => 5.6.7.8:23-24 - {Accept, parsed(packet.TCP, "8.1.1.1", "5.6.7.8", 0, 23)}, - {Accept, parsed(packet.TCP, "8.1.1.1", "5.6.7.8", 0, 24)}, - {Drop, parsed(packet.TCP, "8.1.1.3", "5.6.7.8", 0, 24)}, - {Drop, parsed(packet.TCP, "8.1.1.1", "5.6.7.8", 0, 22)}, + {Accept, parsed(ipproto.TCP, "8.1.1.1", "5.6.7.8", 0, 23)}, + {Accept, parsed(ipproto.TCP, "8.1.1.1", "5.6.7.8", 0, 24)}, + {Drop, parsed(ipproto.TCP, "8.1.1.3", "5.6.7.8", 0, 24)}, + {Drop, parsed(ipproto.TCP, "8.1.1.1", "5.6.7.8", 0, 22)}, // allow * => *:443 - {Accept, parsed(packet.TCP, "17.34.51.68", "8.1.34.51", 0, 443)}, - {Drop, parsed(packet.TCP, "17.34.51.68", "8.1.34.51", 0, 444)}, + {Accept, parsed(ipproto.TCP, "17.34.51.68", "8.1.34.51", 0, 443)}, + {Drop, parsed(ipproto.TCP, "17.34.51.68", "8.1.34.51", 0, 444)}, // allow * => 100.122.98.50:* - {Accept, parsed(packet.TCP, "17.34.51.68", "100.122.98.50", 0, 999)}, - {Accept, parsed(packet.TCP, "17.34.51.68", "100.122.98.50", 0, 0)}, + {Accept, parsed(ipproto.TCP, "17.34.51.68", "100.122.98.50", 0, 999)}, + {Accept, parsed(ipproto.TCP, "17.34.51.68", "100.122.98.50", 0, 0)}, // allow ::1, ::2 => [2001::1]:22 - {Accept, parsed(packet.TCP, "::1", "2001::1", 0, 22)}, - {Accept, parsed(packet.ICMPv6, "::1", "2001::1", 0, 0)}, - {Accept, parsed(packet.TCP, "::2", "2001::1", 0, 22)}, - {Accept, parsed(packet.TCP, "::2", "2001::2", 0, 22)}, - {Drop, parsed(packet.TCP, "::1", "2001::1", 0, 23)}, - {Drop, parsed(packet.TCP, "::1", "2001::3", 0, 22)}, - {Drop, parsed(packet.TCP, "::3", "2001::1", 0, 22)}, + {Accept, parsed(ipproto.TCP, "::1", "2001::1", 0, 22)}, + {Accept, parsed(ipproto.ICMPv6, "::1", "2001::1", 0, 0)}, + {Accept, parsed(ipproto.TCP, "::2", "2001::1", 0, 22)}, + {Accept, parsed(ipproto.TCP, "::2", "2001::2", 0, 22)}, + {Drop, parsed(ipproto.TCP, "::1", "2001::1", 0, 23)}, + {Drop, parsed(ipproto.TCP, "::1", "2001::3", 0, 22)}, + {Drop, parsed(ipproto.TCP, "::3", "2001::1", 0, 22)}, // allow * => *:443 - {Accept, parsed(packet.TCP, "::1", "2001::1", 0, 443)}, - {Drop, parsed(packet.TCP, "::1", "2001::1", 0, 444)}, + {Accept, parsed(ipproto.TCP, "::1", "2001::1", 0, 443)}, + {Drop, parsed(ipproto.TCP, "::1", "2001::1", 0, 444)}, // localNets prefilter - accepted by policy filter, but // unexpected dst IP. - {Drop, parsed(packet.TCP, "8.1.1.1", "16.32.48.64", 0, 443)}, - {Drop, parsed(packet.TCP, "1::", "2602::1", 0, 443)}, + {Drop, parsed(ipproto.TCP, "8.1.1.1", "16.32.48.64", 0, 443)}, + {Drop, parsed(ipproto.TCP, "1::", "2602::1", 0, 443)}, // Don't allow protocols not specified by filter - {Drop, parsed(packet.SCTP, "8.1.1.1", "1.2.3.4", 999, 22)}, + {Drop, parsed(ipproto.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)}, + {Accept, parsed(ipproto.SCTP, "9.1.1.1", "1.2.3.4", 999, 22)}, } for i, test := range tests { aclFunc := acl.runIn4 @@ -117,7 +117,7 @@ func TestFilter(t *testing.T) { if got, why := aclFunc(&test.p); test.want != got { t.Errorf("#%d runIn got=%v want=%v why=%q packet:%v", i, got, test.want, why, test.p) } - if test.p.IPProto == packet.TCP { + if test.p.IPProto == ipproto.TCP { var got Response if test.p.IPVersion == 4 { got = acl.CheckTCP(test.p.Src.IP, test.p.Dst.IP, test.p.Dst.Port) @@ -128,7 +128,7 @@ func TestFilter(t *testing.T) { t.Errorf("#%d CheckTCP got=%v want=%v packet:%v", i, got, test.want, test.p) } // TCP and UDP are treated equivalently in the filter - verify that. - test.p.IPProto = packet.UDP + test.p.IPProto = ipproto.UDP if got, why := aclFunc(&test.p); test.want != got { t.Errorf("#%d runIn (UDP) got=%v want=%v why=%q packet:%v", i, got, test.want, why, test.p) } @@ -142,8 +142,8 @@ func TestUDPState(t *testing.T) { acl := newFilter(t.Logf) flags := LogDrops | LogAccepts - a4 := parsed(packet.UDP, "119.119.119.119", "102.102.102.102", 4242, 4343) - b4 := parsed(packet.UDP, "102.102.102.102", "119.119.119.119", 4343, 4242) + a4 := parsed(ipproto.UDP, "119.119.119.119", "102.102.102.102", 4242, 4343) + b4 := parsed(ipproto.UDP, "102.102.102.102", "119.119.119.119", 4343, 4242) // Unsollicited UDP traffic gets dropped if got := acl.RunIn(&a4, flags); got != Drop { @@ -158,8 +158,8 @@ func TestUDPState(t *testing.T) { t.Fatalf("incoming response packet not accepted, got=%v: %v", got, a4) } - a6 := parsed(packet.UDP, "2001::2", "2001::1", 4242, 4343) - b6 := parsed(packet.UDP, "2001::1", "2001::2", 4343, 4242) + a6 := parsed(ipproto.UDP, "2001::2", "2001::1", 4242, 4343) + b6 := parsed(ipproto.UDP, "2001::1", "2001::2", 4343, 4242) // Unsollicited UDP traffic gets dropped if got := acl.RunIn(&a6, flags); got != Drop { @@ -178,10 +178,10 @@ func TestUDPState(t *testing.T) { func TestNoAllocs(t *testing.T) { acl := newFilter(t.Logf) - tcp4Packet := raw4(packet.TCP, "8.1.1.1", "1.2.3.4", 999, 22, 0) - udp4Packet := raw4(packet.UDP, "8.1.1.1", "1.2.3.4", 999, 22, 0) - tcp6Packet := raw6(packet.TCP, "2001::1", "2001::2", 999, 22, 0) - udp6Packet := raw6(packet.UDP, "2001::1", "2001::2", 999, 22, 0) + tcp4Packet := raw4(ipproto.TCP, "8.1.1.1", "1.2.3.4", 999, 22, 0) + udp4Packet := raw4(ipproto.UDP, "8.1.1.1", "1.2.3.4", 999, 22, 0) + tcp6Packet := raw6(ipproto.TCP, "2001::1", "2001::2", 999, 22, 0) + udp6Packet := raw6(ipproto.UDP, "2001::1", "2001::2", 999, 22, 0) tests := []struct { name string @@ -262,13 +262,13 @@ func TestParseIPSet(t *testing.T) { } func BenchmarkFilter(b *testing.B) { - tcp4Packet := raw4(packet.TCP, "8.1.1.1", "1.2.3.4", 999, 22, 0) - udp4Packet := raw4(packet.UDP, "8.1.1.1", "1.2.3.4", 999, 22, 0) - icmp4Packet := raw4(packet.ICMPv4, "8.1.1.1", "1.2.3.4", 0, 0, 0) + tcp4Packet := raw4(ipproto.TCP, "8.1.1.1", "1.2.3.4", 999, 22, 0) + udp4Packet := raw4(ipproto.UDP, "8.1.1.1", "1.2.3.4", 999, 22, 0) + icmp4Packet := raw4(ipproto.ICMPv4, "8.1.1.1", "1.2.3.4", 0, 0, 0) - tcp6Packet := raw6(packet.TCP, "::1", "2001::1", 999, 22, 0) - udp6Packet := raw6(packet.UDP, "::1", "2001::1", 999, 22, 0) - icmp6Packet := raw6(packet.ICMPv6, "::1", "2001::1", 0, 0, 0) + tcp6Packet := raw6(ipproto.TCP, "::1", "2001::1", 999, 22, 0) + udp6Packet := raw6(ipproto.UDP, "::1", "2001::1", 999, 22, 0) + icmp6Packet := raw6(ipproto.ICMPv6, "::1", "2001::1", 0, 0, 0) benches := []struct { name string @@ -315,11 +315,11 @@ func TestPreFilter(t *testing.T) { }{ {"empty", Accept, []byte{}}, {"short", Drop, []byte("short")}, - {"junk", Drop, raw4default(packet.Unknown, 10)}, - {"fragment", Accept, raw4default(packet.Fragment, 40)}, - {"tcp", noVerdict, raw4default(packet.TCP, 0)}, - {"udp", noVerdict, raw4default(packet.UDP, 0)}, - {"icmp", noVerdict, raw4default(packet.ICMPv4, 0)}, + {"junk", Drop, raw4default(ipproto.Unknown, 10)}, + {"fragment", Accept, raw4default(ipproto.Fragment, 40)}, + {"tcp", noVerdict, raw4default(ipproto.TCP, 0)}, + {"udp", noVerdict, raw4default(ipproto.UDP, 0)}, + {"icmp", noVerdict, raw4default(ipproto.ICMPv4, 0)}, } f := NewAllowNone(t.Logf, &netaddr.IPSet{}) for _, testPacket := range packets { @@ -341,7 +341,7 @@ func TestOmitDropLogging(t *testing.T) { }{ { name: "v4_tcp_out", - pkt: &packet.Parsed{IPVersion: 4, IPProto: packet.TCP}, + pkt: &packet.Parsed{IPVersion: 4, IPProto: ipproto.TCP}, dir: out, want: false, }, @@ -439,73 +439,73 @@ func TestLoggingPrivacy(t *testing.T) { }{ { name: "ts_to_ts_v4_out", - pkt: &packet.Parsed{IPVersion: 4, IPProto: packet.TCP, Src: ts4, Dst: ts4}, + pkt: &packet.Parsed{IPVersion: 4, IPProto: ipproto.TCP, Src: ts4, Dst: ts4}, dir: out, logged: true, }, { name: "ts_to_internet_v4_out", - pkt: &packet.Parsed{IPVersion: 4, IPProto: packet.TCP, Src: ts4, Dst: internet4}, + pkt: &packet.Parsed{IPVersion: 4, IPProto: ipproto.TCP, Src: ts4, Dst: internet4}, dir: out, logged: false, }, { name: "internet_to_ts_v4_out", - pkt: &packet.Parsed{IPVersion: 4, IPProto: packet.TCP, Src: internet4, Dst: ts4}, + pkt: &packet.Parsed{IPVersion: 4, IPProto: ipproto.TCP, Src: internet4, Dst: ts4}, dir: out, logged: false, }, { name: "ts_to_ts_v4_in", - pkt: &packet.Parsed{IPVersion: 4, IPProto: packet.TCP, Src: ts4, Dst: ts4}, + pkt: &packet.Parsed{IPVersion: 4, IPProto: ipproto.TCP, Src: ts4, Dst: ts4}, dir: in, logged: true, }, { name: "ts_to_internet_v4_in", - pkt: &packet.Parsed{IPVersion: 4, IPProto: packet.TCP, Src: ts4, Dst: internet4}, + pkt: &packet.Parsed{IPVersion: 4, IPProto: ipproto.TCP, Src: ts4, Dst: internet4}, dir: in, logged: false, }, { name: "internet_to_ts_v4_in", - pkt: &packet.Parsed{IPVersion: 4, IPProto: packet.TCP, Src: internet4, Dst: ts4}, + pkt: &packet.Parsed{IPVersion: 4, IPProto: ipproto.TCP, Src: internet4, Dst: ts4}, dir: in, logged: false, }, { name: "ts_to_ts_v6_out", - pkt: &packet.Parsed{IPVersion: 6, IPProto: packet.TCP, Src: ts6, Dst: ts6}, + pkt: &packet.Parsed{IPVersion: 6, IPProto: ipproto.TCP, Src: ts6, Dst: ts6}, dir: out, logged: true, }, { name: "ts_to_internet_v6_out", - pkt: &packet.Parsed{IPVersion: 6, IPProto: packet.TCP, Src: ts6, Dst: internet6}, + pkt: &packet.Parsed{IPVersion: 6, IPProto: ipproto.TCP, Src: ts6, Dst: internet6}, dir: out, logged: false, }, { name: "internet_to_ts_v6_out", - pkt: &packet.Parsed{IPVersion: 6, IPProto: packet.TCP, Src: internet6, Dst: ts6}, + pkt: &packet.Parsed{IPVersion: 6, IPProto: ipproto.TCP, Src: internet6, Dst: ts6}, dir: out, logged: false, }, { name: "ts_to_ts_v6_in", - pkt: &packet.Parsed{IPVersion: 6, IPProto: packet.TCP, Src: ts6, Dst: ts6}, + pkt: &packet.Parsed{IPVersion: 6, IPProto: ipproto.TCP, Src: ts6, Dst: ts6}, dir: in, logged: true, }, { name: "ts_to_internet_v6_in", - pkt: &packet.Parsed{IPVersion: 6, IPProto: packet.TCP, Src: ts6, Dst: internet6}, + pkt: &packet.Parsed{IPVersion: 6, IPProto: ipproto.TCP, Src: ts6, Dst: internet6}, dir: in, logged: false, }, { name: "internet_to_ts_v6_in", - pkt: &packet.Parsed{IPVersion: 6, IPProto: packet.TCP, Src: internet6, Dst: ts6}, + pkt: &packet.Parsed{IPVersion: 6, IPProto: ipproto.TCP, Src: internet6, Dst: ts6}, dir: in, logged: false, }, @@ -607,7 +607,7 @@ func raw4(proto ipproto.Proto, src, dst string, sport, dport uint16, trimLength // UDP marshaling clobbers IPProto, so override it here. switch proto { - case packet.Unknown, packet.Fragment: + case ipproto.Unknown, ipproto.Fragment: default: u.IP4Header.IPProto = proto } @@ -615,7 +615,7 @@ func raw4(proto ipproto.Proto, src, dst string, sport, dport uint16, trimLength panic(err) } - if proto == packet.Fragment { + if proto == ipproto.Fragment { // Set some fragment offset. This makes the IP // checksum wrong, but we don't validate the checksum // when parsing. @@ -751,10 +751,10 @@ func TestMatchesFromFilterRules(t *testing.T) { want: []Match{ { IPProto: []ipproto.Proto{ - packet.TCP, - packet.UDP, - packet.ICMPv4, - packet.ICMPv6, + ipproto.TCP, + ipproto.UDP, + ipproto.ICMPv4, + ipproto.ICMPv6, }, Dsts: []NetPortRange{ { @@ -776,7 +776,7 @@ func TestMatchesFromFilterRules(t *testing.T) { name: "explicit_protos", in: []tailcfg.FilterRule{ { - IPProto: []int{int(packet.TCP)}, + IPProto: []int{int(ipproto.TCP)}, SrcIPs: []string{"100.64.1.1"}, DstPorts: []tailcfg.NetPortRange{{ IP: "1.2.0.0/16", @@ -787,7 +787,7 @@ func TestMatchesFromFilterRules(t *testing.T) { want: []Match{ { IPProto: []ipproto.Proto{ - packet.TCP, + ipproto.TCP, }, Dsts: []NetPortRange{ { diff --git a/wgengine/filter/tailcfg.go b/wgengine/filter/tailcfg.go index 6fcf0e52c..1338a75b4 100644 --- a/wgengine/filter/tailcfg.go +++ b/wgengine/filter/tailcfg.go @@ -9,16 +9,15 @@ import ( "strings" "inet.af/netaddr" - "tailscale.com/net/packet" "tailscale.com/tailcfg" "tailscale.com/types/ipproto" ) var defaultProtos = []ipproto.Proto{ - packet.TCP, - packet.UDP, - packet.ICMPv4, - packet.ICMPv6, + ipproto.TCP, + ipproto.UDP, + ipproto.ICMPv4, + ipproto.ICMPv6, } // MatchesFromFilterRules converts tailcfg FilterRules into Matches. diff --git a/wgengine/pendopen.go b/wgengine/pendopen.go index 12902c174..785f71259 100644 --- a/wgengine/pendopen.go +++ b/wgengine/pendopen.go @@ -14,6 +14,7 @@ import ( "tailscale.com/net/flowtrack" "tailscale.com/net/packet" "tailscale.com/net/tsaddr" + "tailscale.com/types/ipproto" "tailscale.com/wgengine/filter" "tailscale.com/wgengine/tstun" ) @@ -68,7 +69,7 @@ func (e *userspaceEngine) noteFlowProblemFromPeer(f flowtrack.Tuple, problem pac func (e *userspaceEngine) trackOpenPreFilterIn(pp *packet.Parsed, t *tstun.TUN) (res filter.Response) { res = filter.Accept // always - if pp.IPProto == packet.TSMP { + if pp.IPProto == ipproto.TSMP { res = filter.DropSilently rh, ok := pp.AsTailscaleRejectedHeader() if !ok { @@ -83,7 +84,7 @@ func (e *userspaceEngine) trackOpenPreFilterIn(pp *packet.Parsed, t *tstun.TUN) } if pp.IPVersion == 0 || - pp.IPProto != packet.TCP || + pp.IPProto != ipproto.TCP || pp.TCPFlags&(packet.TCPSyn|packet.TCPRst) == 0 { return } @@ -102,7 +103,7 @@ func (e *userspaceEngine) trackOpenPostFilterOut(pp *packet.Parsed, t *tstun.TUN res = filter.Accept // always if pp.IPVersion == 0 || - pp.IPProto != packet.TCP || + pp.IPProto != ipproto.TCP || pp.TCPFlags&packet.TCPSyn == 0 { return } diff --git a/wgengine/tstun/tun.go b/wgengine/tstun/tun.go index 92af1b8b0..367c85829 100644 --- a/wgengine/tstun/tun.go +++ b/wgengine/tstun/tun.go @@ -18,6 +18,7 @@ import ( "github.com/tailscale/wireguard-go/tun" "inet.af/netaddr" "tailscale.com/net/packet" + "tailscale.com/types/ipproto" "tailscale.com/types/logger" "tailscale.com/wgengine/filter" ) @@ -340,7 +341,7 @@ func (t *TUN) filterIn(buf []byte) filter.Response { // Their host networking stack can translate this into ICMP // or whatnot as required. But notably, their GUI or tailscale CLI // can show them a rejection history with reasons. - if p.IPVersion == 4 && p.IPProto == packet.TCP && p.TCPFlags&packet.TCPSyn != 0 { + if p.IPVersion == 4 && p.IPProto == ipproto.TCP && p.TCPFlags&packet.TCPSyn != 0 { rj := packet.TailscaleRejectedHeader{ IPSrc: p.Dst.IP, IPDst: p.Src.IP, diff --git a/wgengine/tstun/tun_test.go b/wgengine/tstun/tun_test.go index 062b63f81..2ba798222 100644 --- a/wgengine/tstun/tun_test.go +++ b/wgengine/tstun/tun_test.go @@ -108,8 +108,8 @@ func netports(netPorts ...string) (ret []filter.NetPortRange) { func setfilter(logf logger.Logf, tun *TUN) { protos := []ipproto.Proto{ - packet.TCP, - packet.UDP, + ipproto.TCP, + ipproto.UDP, } matches := []filter.Match{ {IPProto: protos, Srcs: nets("5.6.7.8"), Dsts: netports("1.2.3.4:89-90")}, diff --git a/wgengine/userspace.go b/wgengine/userspace.go index fccca8c8b..8505b800f 100644 --- a/wgengine/userspace.go +++ b/wgengine/userspace.go @@ -35,6 +35,7 @@ import ( "tailscale.com/net/tsaddr" "tailscale.com/net/tshttpproxy" "tailscale.com/tailcfg" + "tailscale.com/types/ipproto" "tailscale.com/types/key" "tailscale.com/types/logger" "tailscale.com/types/netmap" @@ -462,7 +463,7 @@ func (e *userspaceEngine) isLocalAddr(ip netaddr.IP) bool { // handleDNS is an outbound pre-filter resolving Tailscale domains. func (e *userspaceEngine) handleDNS(p *packet.Parsed, t *tstun.TUN) filter.Response { - if p.Dst.IP == magicDNSIP && p.Dst.Port == magicDNSPort && p.IPProto == packet.UDP { + if p.Dst.IP == magicDNSIP && p.Dst.Port == magicDNSPort && p.IPProto == ipproto.UDP { request := tsdns.Packet{ Payload: append([]byte(nil), p.Payload()...), Addr: netaddr.IPPort{IP: p.Src.IP, Port: p.Src.Port},