wgengine/packet: rename types to reflect their v4-only-ness, document.

Signed-off-by: David Anderson <danderson@tailscale.com>
This commit is contained in:
David Anderson
2020-11-09 15:34:03 -08:00
parent ebd96bf4a9
commit 19df6a2ee2
12 changed files with 196 additions and 181 deletions
+16
View File
@@ -0,0 +1,16 @@
// Copyright (c) 2020 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 contains packet parsing and marshaling utilities.
//
// ParsedPacket provides allocation-free minimal packet header
// decoding, for use in packet filtering. The other types in the
// package are for constructing and marshaling packets into []bytes.
//
// To support allocation-free parsing, this package defines IPv4 and
// IPv6 address types. You should prefer to use netaddr's types,
// except where you absolutely need allocation-free IP handling
// (i.e. in the tunnel datapath) and are willing to implement all
// codepaths and data structures twice, once per IP family.
package packet
@@ -4,41 +4,41 @@
package packet
type ICMPType uint8
type ICMP4Type uint8
const (
ICMPEchoReply ICMPType = 0x00
ICMPEchoRequest ICMPType = 0x08
ICMPUnreachable ICMPType = 0x03
ICMPTimeExceeded ICMPType = 0x0b
ICMP4EchoReply ICMP4Type = 0x00
ICMP4EchoRequest ICMP4Type = 0x08
ICMP4Unreachable ICMP4Type = 0x03
ICMP4TimeExceeded ICMP4Type = 0x0b
)
func (t ICMPType) String() string {
func (t ICMP4Type) String() string {
switch t {
case ICMPEchoReply:
case ICMP4EchoReply:
return "EchoReply"
case ICMPEchoRequest:
case ICMP4EchoRequest:
return "EchoRequest"
case ICMPUnreachable:
case ICMP4Unreachable:
return "Unreachable"
case ICMPTimeExceeded:
case ICMP4TimeExceeded:
return "TimeExceeded"
default:
return "Unknown"
}
}
type ICMPCode uint8
type ICMP4Code uint8
const (
ICMPNoCode ICMPCode = 0
ICMP4NoCode ICMP4Code = 0
)
// ICMPHeader represents an ICMP packet header.
type ICMPHeader struct {
IPHeader
Type ICMPType
Code ICMPCode
type ICMP4Header struct {
IP4Header
Type ICMP4Type
Code ICMP4Code
}
const (
@@ -47,11 +47,11 @@ const (
icmpAllHeadersLength = ipHeaderLength + icmpHeaderLength
)
func (ICMPHeader) Len() int {
func (ICMP4Header) Len() int {
return icmpAllHeadersLength
}
func (h ICMPHeader) Marshal(buf []byte) error {
func (h ICMP4Header) Marshal(buf []byte) error {
if len(buf) < icmpAllHeadersLength {
return errSmallBuffer
}
@@ -64,15 +64,17 @@ func (h ICMPHeader) Marshal(buf []byte) error {
buf[20] = uint8(h.Type)
buf[21] = uint8(h.Code)
h.IPHeader.Marshal(buf)
h.IP4Header.Marshal(buf)
put16(buf[22:24], ipChecksum(buf))
return nil
}
func (h *ICMPHeader) ToResponse() {
h.Type = ICMPEchoReply
h.Code = ICMPNoCode
h.IPHeader.ToResponse()
func (h *ICMP4Header) ToResponse() {
// TODO: this doesn't implement ToResponse correctly, as it
// assumes the ICMP request type.
h.Type = ICMP4EchoReply
h.Code = ICMP4NoCode
h.IP4Header.ToResponse()
}
@@ -11,61 +11,62 @@ import (
"inet.af/netaddr"
)
// IP is an IPv4 address.
type IP uint32
// IP4 is an IPv4 address.
type IP4 uint32
// NewIP converts a standard library IP address into an IP.
// It panics if b is not an IPv4 address.
func NewIP(b net.IP) IP {
func NewIP4(b net.IP) IP4 {
b4 := b.To4()
if b4 == nil {
panic(fmt.Sprintf("To4(%v) failed", b))
}
return IP(get32(b4))
return IP4(get32(b4))
}
// IPFromNetaddr converts a netaddr.IP to an IP.
func IPFromNetaddr(ip netaddr.IP) IP {
func IP4FromNetaddr(ip netaddr.IP) IP4 {
ipbytes := ip.As4()
return IP(get32(ipbytes[:]))
return IP4(get32(ipbytes[:]))
}
// Netaddr converts an IP to a netaddr.IP.
func (ip IP) Netaddr() netaddr.IP {
func (ip IP4) Netaddr() netaddr.IP {
return netaddr.IPv4(byte(ip>>24), byte(ip>>16), byte(ip>>8), byte(ip))
}
func (ip IP) String() string {
func (ip IP4) String() string {
return fmt.Sprintf("%d.%d.%d.%d", byte(ip>>24), byte(ip>>16), byte(ip>>8), byte(ip))
}
func (ip IP) IsMulticast() bool {
func (ip IP4) IsMulticast() bool {
return byte(ip>>24)&0xf0 == 0xe0
}
func (ip IP) IsLinkLocalUnicast() bool {
func (ip IP4) IsLinkLocalUnicast() bool {
return byte(ip>>24) == 169 && byte(ip>>16) == 254
}
// IPProto is either a real IP protocol (ITCP, UDP, ...) or an special value like Unknown.
// If it is a real IP protocol, its value corresponds to its IP protocol number.
type IPProto uint8
// IP4Proto is either a real IP protocol (TCP, UDP, ...) or an special
// value like Unknown. If it is a real IP protocol, its value
// corresponds to its IP protocol number.
type IP4Proto uint8
const (
// Unknown represents an unknown or unsupported protocol; it's deliberately the zero value.
Unknown IPProto = 0x00
ICMP IPProto = 0x01
IGMP IPProto = 0x02
ICMPv6 IPProto = 0x3a
TCP IPProto = 0x06
UDP IPProto = 0x11
Unknown IP4Proto = 0x00
ICMP IP4Proto = 0x01
IGMP IP4Proto = 0x02
ICMPv6 IP4Proto = 0x3a
TCP IP4Proto = 0x06
UDP IP4Proto = 0x11
// 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.
Fragment IPProto = 0xFF
Fragment IP4Proto = 0xFF
)
func (p IPProto) String() string {
func (p IP4Proto) String() string {
switch p {
case Fragment:
return "Frag"
@@ -81,20 +82,20 @@ func (p IPProto) String() string {
}
// IPHeader represents an IP packet header.
type IPHeader struct {
IPProto IPProto
type IP4Header struct {
IPProto IP4Proto
IPID uint16
SrcIP IP
DstIP IP
SrcIP IP4
DstIP IP4
}
const ipHeaderLength = 20
func (IPHeader) Len() int {
func (IP4Header) Len() int {
return ipHeaderLength
}
func (h IPHeader) Marshal(buf []byte) error {
func (h IP4Header) Marshal(buf []byte) error {
if len(buf) < ipHeaderLength {
return errSmallBuffer
}
@@ -118,11 +119,10 @@ func (h IPHeader) Marshal(buf []byte) error {
return nil
}
// MarshalPseudo serializes the header into buf in pseudo format.
// It clobbers the header region, which is the first h.Length() bytes of buf.
// It explicitly initializes every byte of the header region,
// so pre-zeroing it on reuse is not required. It does not allocate memory.
func (h IPHeader) MarshalPseudo(buf []byte) error {
// MarshalPseudo serializes the header into buf in the "pseudo-header"
// form required when calculating UDP checksums. Overwrites the first
// h.Length() bytes of buf.
func (h IP4Header) MarshalPseudo(buf []byte) error {
if len(buf) < ipHeaderLength {
return errSmallBuffer
}
@@ -140,7 +140,8 @@ func (h IPHeader) MarshalPseudo(buf []byte) error {
return nil
}
func (h *IPHeader) ToResponse() {
// ToResponse implements Header.
func (h *IP4Header) ToResponse() {
h.SrcIP, h.DstIP = h.DstIP, h.SrcIP
// Flip the bits in the IPID. If incoming IPIDs are distinct, so are these.
h.IPID = ^h.IPID
+30 -30
View File
@@ -43,13 +43,13 @@ type ParsedPacket struct {
// This is not the same as len(b) because b can have trailing zeros.
length int
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)
IPVersion uint8 // 4, 6, or 0
IPProto IP4Proto // IP subprotocol (UDP, TCP, etc); the NextHeader field for IPv6
SrcIP IP4 // IP source address (not used for IPv6)
DstIP IP4 // 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)
}
// NextHeader
@@ -73,7 +73,7 @@ func (p *ParsedPacket) String() string {
return sb.String()
}
func writeIPPort(sb *strbuilder.Builder, ip IP, port uint16) {
func writeIPPort(sb *strbuilder.Builder, ip IP4, port uint16) {
sb.WriteUint(uint64(byte(ip >> 24)))
sb.WriteByte('.')
sb.WriteUint(uint64(byte(ip >> 16)))
@@ -122,9 +122,9 @@ func (q *ParsedPacket) Decode(b []byte) {
q.IPVersion = (b[0] & 0xF0) >> 4
switch q.IPVersion {
case 4:
q.IPProto = IPProto(b[9])
q.IPProto = IP4Proto(b[9])
case 6:
q.IPProto = IPProto(b[6]) // "Next Header" field
q.IPProto = IP4Proto(b[6]) // "Next Header" field
return
default:
q.IPVersion = 0
@@ -140,8 +140,8 @@ func (q *ParsedPacket) Decode(b []byte) {
}
// If it's valid IPv4, then the IP addresses are valid
q.SrcIP = IP(get32(b[12:16]))
q.DstIP = IP(get32(b[16:20]))
q.SrcIP = IP4(get32(b[12:16]))
q.DstIP = IP4(get32(b[16:20]))
q.subofs = int((b[0] & 0x0F) << 2)
sub := b[q.subofs:]
@@ -224,9 +224,9 @@ func (q *ParsedPacket) Decode(b []byte) {
}
}
func (q *ParsedPacket) IPHeader() IPHeader {
func (q *ParsedPacket) IPHeader() IP4Header {
ipid := get16(q.b[4:6])
return IPHeader{
return IP4Header{
IPID: ipid,
IPProto: q.IPProto,
SrcIP: q.SrcIP,
@@ -234,19 +234,19 @@ func (q *ParsedPacket) IPHeader() IPHeader {
}
}
func (q *ParsedPacket) ICMPHeader() ICMPHeader {
return ICMPHeader{
IPHeader: q.IPHeader(),
Type: ICMPType(q.b[q.subofs+0]),
Code: ICMPCode(q.b[q.subofs+1]),
func (q *ParsedPacket) ICMPHeader() ICMP4Header {
return ICMP4Header{
IP4Header: q.IPHeader(),
Type: ICMP4Type(q.b[q.subofs+0]),
Code: ICMP4Code(q.b[q.subofs+1]),
}
}
func (q *ParsedPacket) UDPHeader() UDPHeader {
return UDPHeader{
IPHeader: q.IPHeader(),
SrcPort: q.SrcPort,
DstPort: q.DstPort,
func (q *ParsedPacket) UDPHeader() UDP4Header {
return UDP4Header{
IP4Header: q.IPHeader(),
SrcPort: q.SrcPort,
DstPort: q.DstPort,
}
}
@@ -284,8 +284,8 @@ func (q *ParsedPacket) IsTCPSyn() bool {
// IsError reports whether q is an IPv4 ICMP "Error" packet.
func (q *ParsedPacket) IsError() bool {
if q.IPProto == ICMP && len(q.b) >= q.subofs+8 {
switch ICMPType(q.b[q.subofs]) {
case ICMPUnreachable, ICMPTimeExceeded:
switch ICMP4Type(q.b[q.subofs]) {
case ICMP4Unreachable, ICMP4TimeExceeded:
return true
}
}
@@ -295,8 +295,8 @@ func (q *ParsedPacket) IsError() bool {
// IsEchoRequest reports whether q is an IPv4 ICMP Echo Request.
func (q *ParsedPacket) IsEchoRequest() bool {
if q.IPProto == ICMP && len(q.b) >= q.subofs+8 {
return ICMPType(q.b[q.subofs]) == ICMPEchoRequest &&
ICMPCode(q.b[q.subofs+1]) == ICMPNoCode
return ICMP4Type(q.b[q.subofs]) == ICMP4EchoRequest &&
ICMP4Code(q.b[q.subofs+1]) == ICMP4NoCode
}
return false
}
@@ -304,8 +304,8 @@ func (q *ParsedPacket) IsEchoRequest() bool {
// IsEchoRequest reports whether q is an IPv4 ICMP Echo Response.
func (q *ParsedPacket) IsEchoResponse() bool {
if q.IPProto == ICMP && len(q.b) >= q.subofs+8 {
return ICMPType(q.b[q.subofs]) == ICMPEchoReply &&
ICMPCode(q.b[q.subofs+1]) == ICMPNoCode
return ICMP4Type(q.b[q.subofs]) == ICMP4EchoReply &&
ICMP4Code(q.b[q.subofs+1]) == ICMP4NoCode
}
return false
}
+12 -12
View File
@@ -11,9 +11,9 @@ import (
"testing"
)
func TestIPString(t *testing.T) {
func TestIP4String(t *testing.T) {
const str = "1.2.3.4"
ip := NewIP(net.ParseIP(str))
ip := NewIP4(net.ParseIP(str))
var got string
allocs := testing.AllocsPerRun(1000, func() {
@@ -49,8 +49,8 @@ var icmpRequestDecode = ParsedPacket{
IPVersion: 4,
IPProto: ICMP,
SrcIP: NewIP(net.ParseIP("1.2.3.4")),
DstIP: NewIP(net.ParseIP("5.6.7.8")),
SrcIP: NewIP4(net.ParseIP("1.2.3.4")),
DstIP: NewIP4(net.ParseIP("5.6.7.8")),
SrcPort: 0,
DstPort: 0,
}
@@ -75,8 +75,8 @@ var icmpReplyDecode = ParsedPacket{
IPVersion: 4,
IPProto: ICMP,
SrcIP: NewIP(net.ParseIP("1.2.3.4")),
DstIP: NewIP(net.ParseIP("5.6.7.8")),
SrcIP: NewIP4(net.ParseIP("1.2.3.4")),
DstIP: NewIP4(net.ParseIP("5.6.7.8")),
SrcPort: 0,
DstPort: 0,
}
@@ -131,8 +131,8 @@ var tcpPacketDecode = ParsedPacket{
IPVersion: 4,
IPProto: TCP,
SrcIP: NewIP(net.ParseIP("1.2.3.4")),
DstIP: NewIP(net.ParseIP("5.6.7.8")),
SrcIP: NewIP4(net.ParseIP("1.2.3.4")),
DstIP: NewIP4(net.ParseIP("5.6.7.8")),
SrcPort: 123,
DstPort: 567,
TCPFlags: TCPSynAck,
@@ -159,8 +159,8 @@ var udpRequestDecode = ParsedPacket{
IPVersion: 4,
IPProto: UDP,
SrcIP: NewIP(net.ParseIP("1.2.3.4")),
DstIP: NewIP(net.ParseIP("5.6.7.8")),
SrcIP: NewIP4(net.ParseIP("1.2.3.4")),
DstIP: NewIP4(net.ParseIP("5.6.7.8")),
SrcPort: 123,
DstPort: 567,
}
@@ -185,8 +185,8 @@ var udpReplyDecode = ParsedPacket{
length: len(udpReplyBuffer),
IPProto: UDP,
SrcIP: NewIP(net.ParseIP("1.2.3.4")),
DstIP: NewIP(net.ParseIP("5.6.7.8")),
SrcIP: NewIP4(net.ParseIP("1.2.3.4")),
DstIP: NewIP4(net.ParseIP("5.6.7.8")),
SrcPort: 567,
DstPort: 123,
}
@@ -5,8 +5,8 @@
package packet
// UDPHeader represents an UDP packet header.
type UDPHeader struct {
IPHeader
type UDP4Header struct {
IP4Header
SrcPort uint16
DstPort uint16
}
@@ -17,11 +17,11 @@ const (
udpTotalHeaderLength = ipHeaderLength + udpHeaderLength
)
func (UDPHeader) Len() int {
func (UDP4Header) Len() int {
return udpTotalHeaderLength
}
func (h UDPHeader) Marshal(buf []byte) error {
func (h UDP4Header) Marshal(buf []byte) error {
if len(buf) < udpTotalHeaderLength {
return errSmallBuffer
}
@@ -31,23 +31,23 @@ func (h UDPHeader) Marshal(buf []byte) error {
// The caller does not need to set this.
h.IPProto = UDP
length := len(buf) - h.IPHeader.Len()
length := len(buf) - h.IP4Header.Len()
put16(buf[20:22], h.SrcPort)
put16(buf[22:24], h.DstPort)
put16(buf[24:26], uint16(length))
put16(buf[26:28], 0) // blank checksum
h.IPHeader.MarshalPseudo(buf)
h.IP4Header.MarshalPseudo(buf)
// UDP checksum with IP pseudo header.
put16(buf[26:28], ipChecksum(buf[8:]))
h.IPHeader.Marshal(buf)
h.IP4Header.Marshal(buf)
return nil
}
func (h *UDPHeader) ToResponse() {
func (h *UDP4Header) ToResponse() {
h.SrcPort, h.DstPort = h.DstPort, h.SrcPort
h.IPHeader.ToResponse()
h.IP4Header.ToResponse()
}