diff --git a/wgengine/filter/filter.go b/wgengine/filter/filter.go index 007960777..970728bd7 100644 --- a/wgengine/filter/filter.go +++ b/wgengine/filter/filter.go @@ -188,6 +188,11 @@ func (f *Filter) runIn(q *packet.ParsedPacket) (r Response, why string) { return Drop, "destination not allowed" } + if q.IPVersion == 6 { + // TODO: support IPv6. + return Drop, "no rules matched" + } + switch q.IPProto { case packet.ICMP: if q.IsEchoResponse() || q.IsError() { @@ -257,14 +262,17 @@ func (f *Filter) pre(q *packet.ParsedPacket, rf RunFlags) Response { return Drop } + if q.IPVersion == 6 { + // TODO(bradfitz): don't log about normal broadcast + // IPv6 traffic like route announcements. + f.logRateLimit(rf, q, Drop, "ipv6") + return Drop + } switch q.IPProto { case packet.Unknown: // Unknown packets are dangerous; always drop them. f.logRateLimit(rf, q, Drop, "unknown") return Drop - case packet.IPv6: - f.logRateLimit(rf, q, Drop, "ipv6") - return Drop case packet.Fragment: // Fragments after the first always need to be passed through. // Very small fragments are considered Junk by ParsedPacket. diff --git a/wgengine/packet/ip.go b/wgengine/packet/ip.go index 487f0fb2b..39dfb66c7 100644 --- a/wgengine/packet/ip.go +++ b/wgengine/packet/ip.go @@ -47,12 +47,12 @@ const ( // Unknown represents an unknown or unsupported protocol; it's deliberately the zero value. Unknown IPProto = 0x00 ICMP IPProto = 0x01 + ICMPv6 IPProto = 0x3a TCP IPProto = 0x06 UDP IPProto = 0x11 - // IPv6 and Fragment are special values. They're not really IPProto values - // so we're using the unassigned 0xFE and 0xFF values for them. + // Fragment is a special value. It's not really an IPProto value + // so we're using the unassigned 0xFF value. // TODO(dmytro): special values should be taken out of here. - IPv6 IPProto = 0xFE Fragment IPProto = 0xFF ) @@ -66,8 +66,6 @@ func (p IPProto) String() string { return "UDP" case TCP: return "TCP" - case IPv6: - return "IPv6" default: return "Unknown" } diff --git a/wgengine/packet/packet.go b/wgengine/packet/packet.go index 38d6f9e2b..75bf0bad1 100644 --- a/wgengine/packet/packet.go +++ b/wgengine/packet/packet.go @@ -30,6 +30,8 @@ var ( ) // ParsedPacket is a minimal decoding of a packet suitable for use in filters. +// +// In general, it only supports IPv4. The IPv6 parsing is very minimal. type ParsedPacket struct { // b is the byte buffer that this decodes. b []byte @@ -41,27 +43,32 @@ type ParsedPacket struct { // This is not the same as len(b) because b can have trailing zeros. length int - IPProto IPProto // IP subprotocol (UDP, TCP, etc) - SrcIP IP // IP source address - DstIP IP // IP destination address - SrcPort uint16 // TCP/UDP source port - DstPort uint16 // TCP/UDP destination port - TCPFlags uint8 // TCP flags (SYN, ACK, etc) + IPVersion uint8 // 4, 6, or 0 + IPProto IPProto // IP subprotocol (UDP, TCP, etc); the NextHeader field for IPv6 + SrcIP IP // IP source address (not used for IPv6) + DstIP IP // IP destination address (not used for IPv6) + SrcPort uint16 // TCP/UDP source port + DstPort uint16 // TCP/UDP destination port + TCPFlags uint8 // TCP flags (SYN, ACK, etc) } -func (q *ParsedPacket) String() string { - switch q.IPProto { - case IPv6: +// NextHeader +type NextHeader uint8 + +func (p *ParsedPacket) String() string { + if p.IPVersion == 6 { return "IPv6{???}" + } + switch p.IPProto { case Unknown: return "Unknown{???}" } sb := strbuilder.Get() - sb.WriteString(q.IPProto.String()) + sb.WriteString(p.IPProto.String()) sb.WriteByte('{') - writeIPPort(sb, q.SrcIP, q.SrcPort) + writeIPPort(sb, p.SrcIP, p.SrcPort) sb.WriteString(" > ") - writeIPPort(sb, q.DstIP, q.DstPort) + writeIPPort(sb, p.DstIP, p.DstPort) sb.WriteByte('}') return sb.String() } @@ -105,20 +112,22 @@ func (q *ParsedPacket) Decode(b []byte) { q.b = b if len(b) < ipHeaderLength { + q.IPVersion = 0 q.IPProto = Unknown return } // Check that it's IPv4. // TODO(apenwarr): consider IPv6 support - switch (b[0] & 0xF0) >> 4 { + q.IPVersion = (b[0] & 0xF0) >> 4 + switch q.IPVersion { case 4: q.IPProto = IPProto(b[9]) - // continue case 6: - q.IPProto = IPv6 + q.IPProto = IPProto(b[6]) // "Next Header" field return default: + q.IPVersion = 0 q.IPProto = Unknown return } diff --git a/wgengine/packet/packet_test.go b/wgengine/packet/packet_test.go index 3810e2f31..7669a2e90 100644 --- a/wgengine/packet/packet_test.go +++ b/wgengine/packet/packet_test.go @@ -47,11 +47,12 @@ var icmpRequestDecode = ParsedPacket{ dataofs: 24, length: len(icmpRequestBuffer), - IPProto: ICMP, - SrcIP: NewIP(net.ParseIP("1.2.3.4")), - DstIP: NewIP(net.ParseIP("5.6.7.8")), - SrcPort: 0, - DstPort: 0, + IPVersion: 4, + IPProto: ICMP, + SrcIP: NewIP(net.ParseIP("1.2.3.4")), + DstIP: NewIP(net.ParseIP("5.6.7.8")), + SrcPort: 0, + DstPort: 0, } var icmpReplyBuffer = []byte{ @@ -72,11 +73,12 @@ var icmpReplyDecode = ParsedPacket{ dataofs: 24, length: len(icmpReplyBuffer), - IPProto: ICMP, - SrcIP: NewIP(net.ParseIP("1.2.3.4")), - DstIP: NewIP(net.ParseIP("5.6.7.8")), - SrcPort: 0, - DstPort: 0, + IPVersion: 4, + IPProto: ICMP, + SrcIP: NewIP(net.ParseIP("1.2.3.4")), + DstIP: NewIP(net.ParseIP("5.6.7.8")), + SrcPort: 0, + DstPort: 0, } // IPv6 Router Solicitation @@ -90,8 +92,9 @@ var ipv6PacketBuffer = []byte{ } var ipv6PacketDecode = ParsedPacket{ - b: ipv6PacketBuffer, - IPProto: IPv6, + b: ipv6PacketBuffer, + IPVersion: 6, + IPProto: ICMPv6, } // This is a malformed IPv4 packet. @@ -101,8 +104,9 @@ var unknownPacketBuffer = []byte{ } var unknownPacketDecode = ParsedPacket{ - b: unknownPacketBuffer, - IPProto: Unknown, + b: unknownPacketBuffer, + IPVersion: 0, + IPProto: Unknown, } var tcpPacketBuffer = []byte{ @@ -125,12 +129,13 @@ var tcpPacketDecode = ParsedPacket{ dataofs: 40, length: len(tcpPacketBuffer), - IPProto: TCP, - SrcIP: NewIP(net.ParseIP("1.2.3.4")), - DstIP: NewIP(net.ParseIP("5.6.7.8")), - SrcPort: 123, - DstPort: 567, - TCPFlags: TCPSynAck, + IPVersion: 4, + IPProto: TCP, + SrcIP: NewIP(net.ParseIP("1.2.3.4")), + DstIP: NewIP(net.ParseIP("5.6.7.8")), + SrcPort: 123, + DstPort: 567, + TCPFlags: TCPSynAck, } var udpRequestBuffer = []byte{ @@ -152,11 +157,12 @@ var udpRequestDecode = ParsedPacket{ dataofs: 28, length: len(udpRequestBuffer), - IPProto: UDP, - SrcIP: NewIP(net.ParseIP("1.2.3.4")), - DstIP: NewIP(net.ParseIP("5.6.7.8")), - SrcPort: 123, - DstPort: 567, + IPVersion: 4, + IPProto: UDP, + SrcIP: NewIP(net.ParseIP("1.2.3.4")), + DstIP: NewIP(net.ParseIP("5.6.7.8")), + SrcPort: 123, + DstPort: 567, } var udpReplyBuffer = []byte{ @@ -234,7 +240,7 @@ func TestDecode(t *testing.T) { var got ParsedPacket got.Decode(tt.buf) if !reflect.DeepEqual(got, tt.want) { - t.Errorf("got %v; want %v", got, tt.want) + t.Errorf("mismatch\n got: %#v\nwant: %#v", got, tt.want) } }) }