wgengine/packet: add IPVersion field, don't use IPProto to note version

As prep for IPv6 log spam fixes in a future change.

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
reviewable/pr613/r1
Brad Fitzpatrick 4 years ago committed by Brad Fitzpatrick
parent 91d95dafd2
commit 3e3c24b8f6

@ -188,6 +188,11 @@ func (f *Filter) runIn(q *packet.ParsedPacket) (r Response, why string) {
return Drop, "destination not allowed" return Drop, "destination not allowed"
} }
if q.IPVersion == 6 {
// TODO: support IPv6.
return Drop, "no rules matched"
}
switch q.IPProto { switch q.IPProto {
case packet.ICMP: case packet.ICMP:
if q.IsEchoResponse() || q.IsError() { if q.IsEchoResponse() || q.IsError() {
@ -257,14 +262,17 @@ func (f *Filter) pre(q *packet.ParsedPacket, rf RunFlags) Response {
return Drop 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 { switch q.IPProto {
case packet.Unknown: case packet.Unknown:
// Unknown packets are dangerous; always drop them. // Unknown packets are dangerous; always drop them.
f.logRateLimit(rf, q, Drop, "unknown") f.logRateLimit(rf, q, Drop, "unknown")
return Drop return Drop
case packet.IPv6:
f.logRateLimit(rf, q, Drop, "ipv6")
return Drop
case packet.Fragment: case packet.Fragment:
// Fragments after the first always need to be passed through. // Fragments after the first always need to be passed through.
// Very small fragments are considered Junk by ParsedPacket. // Very small fragments are considered Junk by ParsedPacket.

@ -47,12 +47,12 @@ const (
// Unknown represents an unknown or unsupported protocol; it's deliberately the zero value. // Unknown represents an unknown or unsupported protocol; it's deliberately the zero value.
Unknown IPProto = 0x00 Unknown IPProto = 0x00
ICMP IPProto = 0x01 ICMP IPProto = 0x01
ICMPv6 IPProto = 0x3a
TCP IPProto = 0x06 TCP IPProto = 0x06
UDP IPProto = 0x11 UDP IPProto = 0x11
// IPv6 and Fragment are special values. They're not really IPProto values // Fragment is a special value. It's not really an IPProto value
// so we're using the unassigned 0xFE and 0xFF values for them. // so we're using the unassigned 0xFF value.
// TODO(dmytro): special values should be taken out of here. // TODO(dmytro): special values should be taken out of here.
IPv6 IPProto = 0xFE
Fragment IPProto = 0xFF Fragment IPProto = 0xFF
) )
@ -66,8 +66,6 @@ func (p IPProto) String() string {
return "UDP" return "UDP"
case TCP: case TCP:
return "TCP" return "TCP"
case IPv6:
return "IPv6"
default: default:
return "Unknown" return "Unknown"
} }

@ -30,6 +30,8 @@ var (
) )
// ParsedPacket is a minimal decoding of a packet suitable for use in filters. // 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 { type ParsedPacket struct {
// b is the byte buffer that this decodes. // b is the byte buffer that this decodes.
b []byte b []byte
@ -41,27 +43,32 @@ type ParsedPacket struct {
// This is not the same as len(b) because b can have trailing zeros. // This is not the same as len(b) because b can have trailing zeros.
length int length int
IPProto IPProto // IP subprotocol (UDP, TCP, etc) IPVersion uint8 // 4, 6, or 0
SrcIP IP // IP source address IPProto IPProto // IP subprotocol (UDP, TCP, etc); the NextHeader field for IPv6
DstIP IP // IP destination address SrcIP IP // IP source address (not used for IPv6)
SrcPort uint16 // TCP/UDP source port DstIP IP // IP destination address (not used for IPv6)
DstPort uint16 // TCP/UDP destination port SrcPort uint16 // TCP/UDP source port
TCPFlags uint8 // TCP flags (SYN, ACK, etc) DstPort uint16 // TCP/UDP destination port
TCPFlags uint8 // TCP flags (SYN, ACK, etc)
} }
func (q *ParsedPacket) String() string { // NextHeader
switch q.IPProto { type NextHeader uint8
case IPv6:
func (p *ParsedPacket) String() string {
if p.IPVersion == 6 {
return "IPv6{???}" return "IPv6{???}"
}
switch p.IPProto {
case Unknown: case Unknown:
return "Unknown{???}" return "Unknown{???}"
} }
sb := strbuilder.Get() sb := strbuilder.Get()
sb.WriteString(q.IPProto.String()) sb.WriteString(p.IPProto.String())
sb.WriteByte('{') sb.WriteByte('{')
writeIPPort(sb, q.SrcIP, q.SrcPort) writeIPPort(sb, p.SrcIP, p.SrcPort)
sb.WriteString(" > ") sb.WriteString(" > ")
writeIPPort(sb, q.DstIP, q.DstPort) writeIPPort(sb, p.DstIP, p.DstPort)
sb.WriteByte('}') sb.WriteByte('}')
return sb.String() return sb.String()
} }
@ -105,20 +112,22 @@ func (q *ParsedPacket) Decode(b []byte) {
q.b = b q.b = b
if len(b) < ipHeaderLength { if len(b) < ipHeaderLength {
q.IPVersion = 0
q.IPProto = Unknown q.IPProto = Unknown
return return
} }
// Check that it's IPv4. // Check that it's IPv4.
// TODO(apenwarr): consider IPv6 support // TODO(apenwarr): consider IPv6 support
switch (b[0] & 0xF0) >> 4 { q.IPVersion = (b[0] & 0xF0) >> 4
switch q.IPVersion {
case 4: case 4:
q.IPProto = IPProto(b[9]) q.IPProto = IPProto(b[9])
// continue
case 6: case 6:
q.IPProto = IPv6 q.IPProto = IPProto(b[6]) // "Next Header" field
return return
default: default:
q.IPVersion = 0
q.IPProto = Unknown q.IPProto = Unknown
return return
} }

@ -47,11 +47,12 @@ var icmpRequestDecode = ParsedPacket{
dataofs: 24, dataofs: 24,
length: len(icmpRequestBuffer), length: len(icmpRequestBuffer),
IPProto: ICMP, IPVersion: 4,
SrcIP: NewIP(net.ParseIP("1.2.3.4")), IPProto: ICMP,
DstIP: NewIP(net.ParseIP("5.6.7.8")), SrcIP: NewIP(net.ParseIP("1.2.3.4")),
SrcPort: 0, DstIP: NewIP(net.ParseIP("5.6.7.8")),
DstPort: 0, SrcPort: 0,
DstPort: 0,
} }
var icmpReplyBuffer = []byte{ var icmpReplyBuffer = []byte{
@ -72,11 +73,12 @@ var icmpReplyDecode = ParsedPacket{
dataofs: 24, dataofs: 24,
length: len(icmpReplyBuffer), length: len(icmpReplyBuffer),
IPProto: ICMP, IPVersion: 4,
SrcIP: NewIP(net.ParseIP("1.2.3.4")), IPProto: ICMP,
DstIP: NewIP(net.ParseIP("5.6.7.8")), SrcIP: NewIP(net.ParseIP("1.2.3.4")),
SrcPort: 0, DstIP: NewIP(net.ParseIP("5.6.7.8")),
DstPort: 0, SrcPort: 0,
DstPort: 0,
} }
// IPv6 Router Solicitation // IPv6 Router Solicitation
@ -90,8 +92,9 @@ var ipv6PacketBuffer = []byte{
} }
var ipv6PacketDecode = ParsedPacket{ var ipv6PacketDecode = ParsedPacket{
b: ipv6PacketBuffer, b: ipv6PacketBuffer,
IPProto: IPv6, IPVersion: 6,
IPProto: ICMPv6,
} }
// This is a malformed IPv4 packet. // This is a malformed IPv4 packet.
@ -101,8 +104,9 @@ var unknownPacketBuffer = []byte{
} }
var unknownPacketDecode = ParsedPacket{ var unknownPacketDecode = ParsedPacket{
b: unknownPacketBuffer, b: unknownPacketBuffer,
IPProto: Unknown, IPVersion: 0,
IPProto: Unknown,
} }
var tcpPacketBuffer = []byte{ var tcpPacketBuffer = []byte{
@ -125,12 +129,13 @@ var tcpPacketDecode = ParsedPacket{
dataofs: 40, dataofs: 40,
length: len(tcpPacketBuffer), length: len(tcpPacketBuffer),
IPProto: TCP, IPVersion: 4,
SrcIP: NewIP(net.ParseIP("1.2.3.4")), IPProto: TCP,
DstIP: NewIP(net.ParseIP("5.6.7.8")), SrcIP: NewIP(net.ParseIP("1.2.3.4")),
SrcPort: 123, DstIP: NewIP(net.ParseIP("5.6.7.8")),
DstPort: 567, SrcPort: 123,
TCPFlags: TCPSynAck, DstPort: 567,
TCPFlags: TCPSynAck,
} }
var udpRequestBuffer = []byte{ var udpRequestBuffer = []byte{
@ -152,11 +157,12 @@ var udpRequestDecode = ParsedPacket{
dataofs: 28, dataofs: 28,
length: len(udpRequestBuffer), length: len(udpRequestBuffer),
IPProto: UDP, IPVersion: 4,
SrcIP: NewIP(net.ParseIP("1.2.3.4")), IPProto: UDP,
DstIP: NewIP(net.ParseIP("5.6.7.8")), SrcIP: NewIP(net.ParseIP("1.2.3.4")),
SrcPort: 123, DstIP: NewIP(net.ParseIP("5.6.7.8")),
DstPort: 567, SrcPort: 123,
DstPort: 567,
} }
var udpReplyBuffer = []byte{ var udpReplyBuffer = []byte{
@ -234,7 +240,7 @@ func TestDecode(t *testing.T) {
var got ParsedPacket var got ParsedPacket
got.Decode(tt.buf) got.Decode(tt.buf)
if !reflect.DeepEqual(got, tt.want) { 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)
} }
}) })
} }

Loading…
Cancel
Save