Merge remote-tracking branch 'origin/main' into HEAD

* origin/main:
  net/packet: documentation pass.
  net/packet: remove NewIP, offer only a netaddr constructor.
  net/packet: documentation cleanups.
  net/packet: fix panic on invalid IHL field.
  net/packet: remove {get,put}{16,32} indirection to encoding/binary.
  net/packet: support full IPv6 decoding.
  net/packet: add IPv6 source and destination IPs to Parsed.
pull/914/head
Avery Pennarun 4 years ago
commit 563d43b2a5

@ -16,27 +16,34 @@ const tcpHeaderLength = 20
const maxPacketLength = math.MaxUint16 const maxPacketLength = math.MaxUint16
var ( var (
// errSmallBuffer is returned when Marshal receives a buffer
// too small to contain the header to marshal.
errSmallBuffer = errors.New("buffer too small") errSmallBuffer = errors.New("buffer too small")
// errLargePacket is returned when Marshal receives a payload
// larger than the maximum representable size in header
// fields.
errLargePacket = errors.New("packet too large") errLargePacket = errors.New("packet too large")
) )
// Header is a packet header capable of marshaling itself into a byte buffer. // Header is a packet header capable of marshaling itself into a byte
// buffer.
type Header interface { type Header interface {
// Len returns the length of the header after marshaling. // Len returns the length of the marshaled packet.
Len() int Len() int
// Marshal serializes the header into buf in wire format. // Marshal serializes the header into buf, which must be at
// It clobbers the header region, which is the first h.Length() bytes of buf. // least Len() bytes long. Implementations of Marshal assume
// It explicitly initializes every byte of the header region, // that bytes after the first Len() are payload bytes for the
// so pre-zeroing it on reuse is not required. It does not allocate memory. // purpose of computing length and checksum fields. Marshal
// It fails if and only if len(buf) < Length(). // implementations must not allocate memory.
Marshal(buf []byte) error Marshal(buf []byte) error
// ToResponse transforms the header into one for a response packet. // ToResponse transforms the header into one for a response packet.
// For instance, this swaps the source and destination IPs. // For instance, this swaps the source and destination IPs.
ToResponse() ToResponse()
} }
// Generate generates a new packet with the given header and payload. // Generate generates a new packet with the given Header and
// Unlike Header.Marshal, this does allocate memory. // payload. This function allocates memory, see Header.Marshal for an
// allocation-free option.
func Generate(h Header, payload []byte) []byte { func Generate(h Header, payload []byte) []byte {
hlen := h.Len() hlen := h.Len()
buf := make([]byte, hlen+len(payload)) buf := make([]byte, hlen+len(payload))

@ -4,6 +4,15 @@
package packet package packet
import "encoding/binary"
// icmp4HeaderLength is the size of the ICMPv4 packet header, not
// including the outer IP layer or the variable "response data"
// trailer.
const icmp4HeaderLength = 4
// ICMP4Type is an ICMPv4 type, as specified in
// https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml
type ICMP4Type uint8 type ICMP4Type uint8
const ( const (
@ -28,49 +37,50 @@ func (t ICMP4Type) String() string {
} }
} }
// ICMP4Code is an ICMPv4 code, as specified in
// https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml
type ICMP4Code uint8 type ICMP4Code uint8
const ( const (
ICMP4NoCode ICMP4Code = 0 ICMP4NoCode ICMP4Code = 0
) )
// ICMPHeader represents an ICMP packet header. // ICMP4Header is an IPv4+ICMPv4 header.
type ICMP4Header struct { type ICMP4Header struct {
IP4Header IP4Header
Type ICMP4Type Type ICMP4Type
Code ICMP4Code Code ICMP4Code
} }
const ( // Len implements Header.
icmpHeaderLength = 4 func (h ICMP4Header) Len() int {
// icmpTotalHeaderLength is the length of all headers in a ICMP packet. return h.IP4Header.Len() + icmp4HeaderLength
icmpAllHeadersLength = ipHeaderLength + icmpHeaderLength
)
func (ICMP4Header) Len() int {
return icmpAllHeadersLength
} }
// Marshal implements Header.
func (h ICMP4Header) Marshal(buf []byte) error { func (h ICMP4Header) Marshal(buf []byte) error {
if len(buf) < icmpAllHeadersLength { if len(buf) < h.Len() {
return errSmallBuffer return errSmallBuffer
} }
if len(buf) > maxPacketLength { if len(buf) > maxPacketLength {
return errLargePacket return errLargePacket
} }
// The caller does not need to set this. // The caller does not need to set this.
h.IPProto = ICMP h.IPProto = ICMPv4
buf[20] = uint8(h.Type) buf[20] = uint8(h.Type)
buf[21] = uint8(h.Code) buf[21] = uint8(h.Code)
h.IP4Header.Marshal(buf) h.IP4Header.Marshal(buf)
put16(buf[22:24], ipChecksum(buf)) binary.BigEndian.PutUint16(buf[22:24], ip4Checksum(buf))
return nil return nil
} }
// ToResponse implements Header. TODO: it doesn't implement it
// correctly, instead it statically generates an ICMP Echo Reply
// packet.
func (h *ICMP4Header) ToResponse() { func (h *ICMP4Header) ToResponse() {
// TODO: this doesn't implement ToResponse correctly, as it // TODO: this doesn't implement ToResponse correctly, as it
// assumes the ICMP request type. // assumes the ICMP request type.

@ -0,0 +1,44 @@
// 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
// icmp6HeaderLength is the size of the ICMPv6 packet header, not
// including the outer IP layer or the variable "response data"
// trailer.
const icmp6HeaderLength = 4
// ICMP6Type is an ICMPv6 type, as specified in
// https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml
type ICMP6Type uint8
const (
ICMP6Unreachable ICMP6Type = 1
ICMP6TimeExceeded ICMP6Type = 3
ICMP6EchoRequest ICMP6Type = 128
ICMP6EchoReply ICMP6Type = 129
)
func (t ICMP6Type) String() string {
switch t {
case ICMP6Unreachable:
return "Unreachable"
case ICMP6TimeExceeded:
return "TimeExceeded"
case ICMP6EchoRequest:
return "EchoRequest"
case ICMP6EchoReply:
return "EchoReply"
default:
return "Unknown"
}
}
// ICMP6Code is an ICMPv6 code, as specified in
// https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml
type ICMP6Code uint8
const (
ICMP6NoCode ICMP6Code = 0
)

@ -0,0 +1,53 @@
// 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
// IPProto 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
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
// Values from the IANA registry.
ICMPv4 IPProto = 0x01
IGMP IPProto = 0x02
ICMPv6 IPProto = 0x3a
TCP IPProto = 0x06
UDP IPProto = 0x11
// Fragment represents any non-first IP fragment, for which we
// don't have the sub-protocol header (and therefore can't
// figure out what the sub-protocol is).
//
// 0xFF is reserved in the IANA registry, so we steal it for
// internal use.
Fragment IPProto = 0xFF
)
func (p IPProto) String() string {
switch p {
case Fragment:
return "Frag"
case ICMPv4:
return "ICMPv4"
case IGMP:
return "IGMP"
case ICMPv6:
return "ICMPv6"
case UDP:
return "UDP"
case TCP:
return "TCP"
default:
return "Unknown"
}
}

@ -5,8 +5,8 @@
package packet package packet
import ( import (
"encoding/binary"
"fmt" "fmt"
"net"
"inet.af/netaddr" "inet.af/netaddr"
) )
@ -14,23 +14,13 @@ import (
// IP4 is an IPv4 address. // IP4 is an IPv4 address.
type IP4 uint32 type IP4 uint32
// NewIP converts a standard library IP address into an IP. // IPFromNetaddr converts a netaddr.IP to an IP4. Panics if !ip.Is4.
// It panics if b is not an IPv4 address.
func NewIP4(b net.IP) IP4 {
b4 := b.To4()
if b4 == nil {
panic(fmt.Sprintf("To4(%v) failed", b))
}
return IP4(get32(b4))
}
// IPFromNetaddr converts a netaddr.IP to an IP.
func IP4FromNetaddr(ip netaddr.IP) IP4 { func IP4FromNetaddr(ip netaddr.IP) IP4 {
ipbytes := ip.As4() ipbytes := ip.As4()
return IP4(get32(ipbytes[:])) return IP4(binary.BigEndian.Uint32(ipbytes[:]))
} }
// Netaddr converts an IP to a netaddr.IP. // Netaddr converts ip to a netaddr.IP.
func (ip IP4) Netaddr() netaddr.IP { func (ip IP4) Netaddr() netaddr.IP {
return netaddr.IPv4(byte(ip>>24), byte(ip>>16), byte(ip>>8), byte(ip)) return netaddr.IPv4(byte(ip>>24), byte(ip>>16), byte(ip>>8), byte(ip))
} }
@ -39,110 +29,109 @@ func (ip IP4) String() string {
return fmt.Sprintf("%d.%d.%d.%d", byte(ip>>24), byte(ip>>16), byte(ip>>8), byte(ip)) return fmt.Sprintf("%d.%d.%d.%d", byte(ip>>24), byte(ip>>16), byte(ip>>8), byte(ip))
} }
// IsMulticast returns whether ip is a multicast address.
func (ip IP4) IsMulticast() bool { func (ip IP4) IsMulticast() bool {
return byte(ip>>24)&0xf0 == 0xe0 return byte(ip>>24)&0xf0 == 0xe0
} }
// IsLinkLocalUnicast returns whether ip is a link-local unicast
// address.
func (ip IP4) IsLinkLocalUnicast() bool { func (ip IP4) IsLinkLocalUnicast() bool {
return byte(ip>>24) == 169 && byte(ip>>16) == 254 return byte(ip>>24) == 169 && byte(ip>>16) == 254
} }
// IP4Proto is either a real IP protocol (TCP, UDP, ...) or an special // ip4HeaderLength is the length of an IPv4 header with no IP options.
// value like Unknown. If it is a real IP protocol, its value const ip4HeaderLength = 20
// corresponds to its IP protocol number.
type IP4Proto uint8
const (
// Unknown represents an unknown or unsupported protocol; it's deliberately the zero value.
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 IP4Proto = 0xFF
)
func (p IP4Proto) String() string {
switch p {
case Fragment:
return "Frag"
case ICMP:
return "ICMP"
case UDP:
return "UDP"
case TCP:
return "TCP"
default:
return "Unknown"
}
}
// IPHeader represents an IP packet header. // IP4Header represents an IPv4 packet header.
type IP4Header struct { type IP4Header struct {
IPProto IP4Proto IPProto IPProto
IPID uint16 IPID uint16
SrcIP IP4 SrcIP IP4
DstIP IP4 DstIP IP4
} }
const ipHeaderLength = 20 // Len implements Header.
func (h IP4Header) Len() int {
func (IP4Header) Len() int { return ip4HeaderLength
return ipHeaderLength
} }
// Marshal implements Header.
func (h IP4Header) Marshal(buf []byte) error { func (h IP4Header) Marshal(buf []byte) error {
if len(buf) < ipHeaderLength { if len(buf) < h.Len() {
return errSmallBuffer return errSmallBuffer
} }
if len(buf) > maxPacketLength { if len(buf) > maxPacketLength {
return errLargePacket return errLargePacket
} }
buf[0] = 0x40 | (ipHeaderLength >> 2) // IPv4 buf[0] = 0x40 | (byte(h.Len() >> 2)) // IPv4 + IHL
buf[1] = 0x00 // DHCP, ECN buf[1] = 0x00 // DSCP + ECN
put16(buf[2:4], uint16(len(buf))) binary.BigEndian.PutUint16(buf[2:4], uint16(len(buf))) // Total length
put16(buf[4:6], h.IPID) binary.BigEndian.PutUint16(buf[4:6], h.IPID) // ID
put16(buf[6:8], 0) // flags, offset binary.BigEndian.PutUint16(buf[6:8], 0) // Flags + fragment offset
buf[8] = 64 // TTL buf[8] = 64 // TTL
buf[9] = uint8(h.IPProto) buf[9] = uint8(h.IPProto) // Inner protocol
put16(buf[10:12], 0) // blank IP header checksum // Blank checksum. This is necessary even though we overwrite
put32(buf[12:16], uint32(h.SrcIP)) // it later, because the checksum computation runs over these
put32(buf[16:20], uint32(h.DstIP)) // bytes and expects them to be zero.
binary.BigEndian.PutUint16(buf[10:12], 0)
binary.BigEndian.PutUint32(buf[12:16], uint32(h.SrcIP)) // Src
binary.BigEndian.PutUint32(buf[16:20], uint32(h.DstIP)) // Dst
put16(buf[10:12], ipChecksum(buf[0:20])) binary.BigEndian.PutUint16(buf[10:12], ip4Checksum(buf[0:20])) // Checksum
return nil return nil
} }
// MarshalPseudo serializes the header into buf in the "pseudo-header" // ToResponse implements Header.
// form required when calculating UDP checksums. Overwrites the first func (h *IP4Header) ToResponse() {
// h.Length() bytes of buf. h.SrcIP, h.DstIP = h.DstIP, h.SrcIP
func (h IP4Header) MarshalPseudo(buf []byte) error { // Flip the bits in the IPID. If incoming IPIDs are distinct, so are these.
if len(buf) < ipHeaderLength { h.IPID = ^h.IPID
}
// ip4Checksum computes an IPv4 checksum, as specified in
// https://tools.ietf.org/html/rfc1071
func ip4Checksum(b []byte) uint16 {
var ac uint32
i := 0
n := len(b)
for n >= 2 {
ac += uint32(binary.BigEndian.Uint16(b[i : i+2]))
n -= 2
i += 2
}
if n == 1 {
ac += uint32(b[i]) << 8
}
for (ac >> 16) > 0 {
ac = (ac >> 16) + (ac & 0xffff)
}
return uint16(^ac)
}
// ip4PseudoHeaderOffset is the number of bytes by which the IPv4 UDP
// pseudo-header is smaller than the real IPv4 header.
const ip4PseudoHeaderOffset = 8
// marshalPseudo serializes h into buf in the "pseudo-header" form
// required when calculating UDP checksums. The pseudo-header starts
// at buf[ip4PseudoHeaderOffset] so as to abut the following UDP
// header, while leaving enough space in buf for a full IPv4 header.
func (h IP4Header) marshalPseudo(buf []byte) error {
if len(buf) < h.Len() {
return errSmallBuffer return errSmallBuffer
} }
if len(buf) > maxPacketLength { if len(buf) > maxPacketLength {
return errLargePacket return errLargePacket
} }
length := len(buf) - ipHeaderLength length := len(buf) - h.Len()
put32(buf[8:12], uint32(h.SrcIP)) binary.BigEndian.PutUint32(buf[8:12], uint32(h.SrcIP))
put32(buf[12:16], uint32(h.DstIP)) binary.BigEndian.PutUint32(buf[12:16], uint32(h.DstIP))
buf[16] = 0x0 buf[16] = 0x0
buf[17] = uint8(h.IPProto) buf[17] = uint8(h.IPProto)
put16(buf[18:20], uint16(length)) binary.BigEndian.PutUint16(buf[18:20], uint16(length))
return nil return nil
} }
// 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
}

@ -0,0 +1,34 @@
// 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
import (
"fmt"
"inet.af/netaddr"
)
// IP6 is an IPv6 address.
type IP6 [16]byte
// IP6FromNetaddr converts a netaddr.IP to an IP6. Panics if !ip.Is6.
func IP6FromNetaddr(ip netaddr.IP) IP6 {
if !ip.Is6() {
panic(fmt.Sprintf("IP6FromNetaddr called with non-v6 addr %q", ip))
}
return IP6(ip.As16())
}
// Netaddr converts ip to a netaddr.IP.
func (ip IP6) Netaddr() netaddr.IP {
return netaddr.IPFrom16(ip)
}
func (ip IP6) String() string {
return ip.Netaddr().String()
}
// ip6HeaderLength is the length of an IPv6 header with no IP options.
const ip6HeaderLength = 40

@ -21,17 +21,7 @@ const (
TCPSynAck = TCPSyn | TCPAck TCPSynAck = TCPSyn | TCPAck
) )
var (
get16 = binary.BigEndian.Uint16
get32 = binary.BigEndian.Uint32
put16 = binary.BigEndian.PutUint16
put32 = binary.BigEndian.PutUint32
)
// Parsed is a minimal decoding of a packet suitable for use in filters. // Parsed 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 Parsed struct { type Parsed struct {
// b is the byte buffer that this decodes. // b is the byte buffer that this decodes.
b []byte b []byte
@ -43,37 +33,53 @@ type Parsed 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
IPVersion uint8 // 4, 6, or 0 // IPVersion is the IP protocol version of the packet (4 or
IPProto IP4Proto // IP subprotocol (UDP, TCP, etc); the NextHeader field for IPv6 // 6), or 0 if the packet doesn't look like IPv4 or IPv6.
SrcIP IP4 // IP source address (not used for IPv6) IPVersion uint8
DstIP IP4 // IP destination address (not used for IPv6) // IPProto is the IP subprotocol (UDP, TCP, etc.). Valid iff IPVersion != 0.
SrcPort uint16 // TCP/UDP source port IPProto IPProto
DstPort uint16 // TCP/UDP destination port // SrcIP4 is the IPv4 source address. Valid iff IPVersion == 4.
TCPFlags uint8 // TCP flags (SYN, ACK, etc) SrcIP4 IP4
// DstIP4 is the IPv4 destination address. Valid iff IPVersion == 4.
DstIP4 IP4
// SrcIP6 is the IPv6 source address. Valid iff IPVersion == 6.
SrcIP6 IP6
// DstIP6 is the IPv6 destination address. Valid iff IPVersion == 6.
DstIP6 IP6
// SrcPort is the TCP/UDP source port. Valid iff IPProto == TCP || IPProto == UDP.
SrcPort uint16
// DstPort is the TCP/UDP source port. Valid iff IPProto == TCP || IPProto == UDP.
DstPort uint16
// TCPFlags is the packet's TCP flag bigs. Valid iff IPProto == TCP.
TCPFlags uint8
} }
// NextHeader
type NextHeader uint8
func (p *Parsed) String() string { func (p *Parsed) String() string {
if p.IPVersion == 6 { switch p.IPVersion {
return fmt.Sprintf("IPv6{Proto=%d}", p.IPProto) case 4:
}
switch p.IPProto {
case Unknown:
return "Unknown{???}"
}
sb := strbuilder.Get() sb := strbuilder.Get()
sb.WriteString(p.IPProto.String()) sb.WriteString(p.IPProto.String())
sb.WriteByte('{') sb.WriteByte('{')
writeIPPort(sb, p.SrcIP, p.SrcPort) writeIP4Port(sb, p.SrcIP4, p.SrcPort)
sb.WriteString(" > ") sb.WriteString(" > ")
writeIPPort(sb, p.DstIP, p.DstPort) writeIP4Port(sb, p.DstIP4, p.DstPort)
sb.WriteByte('}') sb.WriteByte('}')
return sb.String() return sb.String()
case 6:
sb := strbuilder.Get()
sb.WriteString(p.IPProto.String())
sb.WriteByte('{')
writeIP6Port(sb, p.SrcIP6, p.SrcPort)
sb.WriteString(" > ")
writeIP6Port(sb, p.DstIP6, p.DstPort)
sb.WriteByte('}')
return sb.String()
default:
return "Unknown{???}"
}
} }
func writeIPPort(sb *strbuilder.Builder, ip IP4, port uint16) { func writeIP4Port(sb *strbuilder.Builder, ip IP4, port uint16) {
sb.WriteUint(uint64(byte(ip >> 24))) sb.WriteUint(uint64(byte(ip >> 24)))
sb.WriteByte('.') sb.WriteByte('.')
sb.WriteUint(uint64(byte(ip >> 16))) sb.WriteUint(uint64(byte(ip >> 16)))
@ -85,23 +91,11 @@ func writeIPPort(sb *strbuilder.Builder, ip IP4, port uint16) {
sb.WriteUint(uint64(port)) sb.WriteUint(uint64(port))
} }
// based on https://tools.ietf.org/html/rfc1071 func writeIP6Port(sb *strbuilder.Builder, ip IP6, port uint16) {
func ipChecksum(b []byte) uint16 { sb.WriteByte('[')
var ac uint32 sb.WriteString(ip.Netaddr().String()) // TODO: faster?
i := 0 sb.WriteString("]:")
n := len(b) sb.WriteUint(uint64(port))
for n >= 2 {
ac += uint32(get16(b[i : i+2]))
n -= 2
i += 2
}
if n == 1 {
ac += uint32(b[i]) << 8
}
for (ac >> 16) > 0 {
ac = (ac >> 16) + (ac & 0xffff)
}
return uint16(^ac)
} }
// Decode extracts data from the packet in b into q. // Decode extracts data from the packet in b into q.
@ -111,28 +105,34 @@ func ipChecksum(b []byte) uint16 {
func (q *Parsed) Decode(b []byte) { func (q *Parsed) Decode(b []byte) {
q.b = b q.b = b
if len(b) < ipHeaderLength { if len(b) < 1 {
q.IPVersion = 0 q.IPVersion = 0
q.IPProto = Unknown q.IPProto = Unknown
return return
} }
// Check that it's IPv4.
// TODO(apenwarr): consider IPv6 support
q.IPVersion = (b[0] & 0xF0) >> 4 q.IPVersion = (b[0] & 0xF0) >> 4
switch q.IPVersion { switch q.IPVersion {
case 4: case 4:
q.IPProto = IP4Proto(b[9]) q.decode4(b)
case 6: case 6:
q.IPProto = IP4Proto(b[6]) // "Next Header" field q.decode6(b)
return
default: default:
q.IPVersion = 0 q.IPVersion = 0
q.IPProto = Unknown q.IPProto = Unknown
}
}
func (q *Parsed) decode4(b []byte) {
if len(b) < ip4HeaderLength {
q.IPVersion = 0
q.IPProto = Unknown
return return
} }
q.length = int(get16(b[2:4])) // Check that it's IPv4.
q.IPProto = IPProto(b[9])
q.length = int(binary.BigEndian.Uint16(b[2:4]))
if len(b) < q.length { if len(b) < q.length {
// Packet was cut off before full IPv4 length. // Packet was cut off before full IPv4 length.
q.IPProto = Unknown q.IPProto = Unknown
@ -140,10 +140,15 @@ func (q *Parsed) Decode(b []byte) {
} }
// If it's valid IPv4, then the IP addresses are valid // If it's valid IPv4, then the IP addresses are valid
q.SrcIP = IP4(get32(b[12:16])) q.SrcIP4 = IP4(binary.BigEndian.Uint32(b[12:16]))
q.DstIP = IP4(get32(b[16:20])) q.DstIP4 = IP4(binary.BigEndian.Uint32(b[16:20]))
q.subofs = int((b[0] & 0x0F) << 2) q.subofs = int((b[0] & 0x0F) << 2)
if q.subofs > q.length {
// next-proto starts beyond end of packet.
q.IPProto = Unknown
return
}
sub := b[q.subofs:] sub := b[q.subofs:]
// We don't care much about IP fragmentation, except insofar as it's // We don't care much about IP fragmentation, except insofar as it's
@ -158,7 +163,7 @@ func (q *Parsed) Decode(b []byte) {
// zero reason to send such a short first fragment, so we can treat // zero reason to send such a short first fragment, so we can treat
// it as Unknown. We can also treat any subsequent fragment that starts // it as Unknown. We can also treat any subsequent fragment that starts
// at such a low offset as Unknown. // at such a low offset as Unknown.
fragFlags := get16(b[6:8]) fragFlags := binary.BigEndian.Uint16(b[6:8])
moreFrags := (fragFlags & 0x20) != 0 moreFrags := (fragFlags & 0x20) != 0
fragOfs := fragFlags & 0x1FFF fragOfs := fragFlags & 0x1FFF
if fragOfs == 0 { if fragOfs == 0 {
@ -172,22 +177,22 @@ func (q *Parsed) Decode(b []byte) {
// or a big enough initial fragment that we can read the // or a big enough initial fragment that we can read the
// whole subprotocol header. // whole subprotocol header.
switch q.IPProto { switch q.IPProto {
case ICMP: case ICMPv4:
if len(sub) < icmpHeaderLength { if len(sub) < icmp4HeaderLength {
q.IPProto = Unknown q.IPProto = Unknown
return return
} }
q.SrcPort = 0 q.SrcPort = 0
q.DstPort = 0 q.DstPort = 0
q.dataofs = q.subofs + icmpHeaderLength q.dataofs = q.subofs + icmp4HeaderLength
return return
case TCP: case TCP:
if len(sub) < tcpHeaderLength { if len(sub) < tcpHeaderLength {
q.IPProto = Unknown q.IPProto = Unknown
return return
} }
q.SrcPort = get16(sub[0:2]) q.SrcPort = binary.BigEndian.Uint16(sub[0:2])
q.DstPort = get16(sub[2:4]) q.DstPort = binary.BigEndian.Uint16(sub[2:4])
q.TCPFlags = sub[13] & 0x3F q.TCPFlags = sub[13] & 0x3F
headerLength := (sub[12] & 0xF0) >> 2 headerLength := (sub[12] & 0xF0) >> 2
q.dataofs = q.subofs + int(headerLength) q.dataofs = q.subofs + int(headerLength)
@ -197,8 +202,8 @@ func (q *Parsed) Decode(b []byte) {
q.IPProto = Unknown q.IPProto = Unknown
return return
} }
q.SrcPort = get16(sub[0:2]) q.SrcPort = binary.BigEndian.Uint16(sub[0:2])
q.DstPort = get16(sub[2:4]) q.DstPort = binary.BigEndian.Uint16(sub[2:4])
q.dataofs = q.subofs + udpHeaderLength q.dataofs = q.subofs + udpHeaderLength
return return
default: default:
@ -224,27 +229,103 @@ func (q *Parsed) Decode(b []byte) {
} }
} }
func (q *Parsed) IPHeader() IP4Header { func (q *Parsed) decode6(b []byte) {
ipid := get16(q.b[4:6]) if len(b) < ip6HeaderLength {
q.IPVersion = 0
q.IPProto = Unknown
return
}
q.IPProto = IPProto(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.
q.IPProto = Unknown
return
}
copy(q.SrcIP6[:], b[8:24])
copy(q.DstIP6[:], b[24:40])
// We don't support any IPv6 extension headers. Don't try to
// be clever. Therefore, the IP subprotocol always starts at
// byte 40.
//
// Note that this means we don't support fragmentation in
// IPv6. This is fine, because IPv6 strongly mandates that you
// should not fragment, which makes fragmentation on the open
// internet extremely uncommon.
//
// This also means we don't support IPSec headers (AH/ESP), or
// IPv6 jumbo frames. Those will get marked Unknown and
// dropped.
q.subofs = 40
sub := b[q.subofs:]
switch q.IPProto {
case ICMPv6:
if len(sub) < icmp6HeaderLength {
q.IPProto = Unknown
return
}
q.SrcPort = 0
q.DstPort = 0
q.dataofs = q.subofs + icmp6HeaderLength
case TCP:
if len(sub) < tcpHeaderLength {
q.IPProto = Unknown
return
}
q.SrcPort = binary.BigEndian.Uint16(sub[0:2])
q.DstPort = binary.BigEndian.Uint16(sub[2:4])
q.TCPFlags = sub[13] & 0x3F
headerLength := (sub[12] & 0xF0) >> 2
q.dataofs = q.subofs + int(headerLength)
return
case UDP:
if len(sub) < udpHeaderLength {
q.IPProto = Unknown
return
}
q.SrcPort = binary.BigEndian.Uint16(sub[0:2])
q.DstPort = binary.BigEndian.Uint16(sub[2:4])
q.dataofs = q.subofs + udpHeaderLength
default:
q.IPProto = Unknown
return
}
}
func (q *Parsed) IP4Header() IP4Header {
if q.IPVersion != 4 {
panic("IP4Header called on non-IPv4 Parsed")
}
ipid := binary.BigEndian.Uint16(q.b[4:6])
return IP4Header{ return IP4Header{
IPID: ipid, IPID: ipid,
IPProto: q.IPProto, IPProto: q.IPProto,
SrcIP: q.SrcIP, SrcIP: q.SrcIP4,
DstIP: q.DstIP, DstIP: q.DstIP4,
} }
} }
func (q *Parsed) ICMPHeader() ICMP4Header { func (q *Parsed) ICMP4Header() ICMP4Header {
if q.IPVersion != 4 {
panic("IP4Header called on non-IPv4 Parsed")
}
return ICMP4Header{ return ICMP4Header{
IP4Header: q.IPHeader(), IP4Header: q.IP4Header(),
Type: ICMP4Type(q.b[q.subofs+0]), Type: ICMP4Type(q.b[q.subofs+0]),
Code: ICMP4Code(q.b[q.subofs+1]), Code: ICMP4Code(q.b[q.subofs+1]),
} }
} }
func (q *Parsed) UDPHeader() UDP4Header { func (q *Parsed) UDP4Header() UDP4Header {
if q.IPVersion != 4 {
panic("IP4Header called on non-IPv4 Parsed")
}
return UDP4Header{ return UDP4Header{
IP4Header: q.IPHeader(), IP4Header: q.IP4Header(),
SrcPort: q.SrcPort, SrcPort: q.SrcPort,
DstPort: q.DstPort, DstPort: q.DstPort,
} }
@ -256,59 +337,61 @@ func (q *Parsed) Buffer() []byte {
return q.b return q.b
} }
// Sub returns the IP subprotocol section.
// This is a read-only view; that is, q retains the ownership of the buffer.
func (q *Parsed) Sub(begin, n int) []byte {
return q.b[q.subofs+begin : q.subofs+begin+n]
}
// Payload returns the payload of the IP subprotocol section. // Payload returns the payload of the IP subprotocol section.
// This is a read-only view; that is, q retains the ownership of the buffer. // This is a read-only view; that is, q retains the ownership of the buffer.
func (q *Parsed) Payload() []byte { func (q *Parsed) Payload() []byte {
return q.b[q.dataofs:q.length] return q.b[q.dataofs:q.length]
} }
// Trim trims the buffer to its IPv4 length.
// Sometimes packets arrive from an interface with extra bytes on the end.
// This removes them.
func (q *Parsed) Trim() []byte {
return q.b[:q.length]
}
// IsTCPSyn reports whether q is a TCP SYN packet // IsTCPSyn reports whether q is a TCP SYN packet
// (i.e. the first packet in a new connection). // (i.e. the first packet in a new connection).
func (q *Parsed) IsTCPSyn() bool { func (q *Parsed) IsTCPSyn() bool {
return (q.TCPFlags & TCPSynAck) == TCPSyn return (q.TCPFlags & TCPSynAck) == TCPSyn
} }
// IsError reports whether q is an IPv4 ICMP "Error" packet. // IsError reports whether q is an ICMP "Error" packet.
func (q *Parsed) IsError() bool { func (q *Parsed) IsError() bool {
if q.IPProto == ICMP && len(q.b) >= q.subofs+8 { switch q.IPProto {
switch ICMP4Type(q.b[q.subofs]) { case ICMPv4:
case ICMP4Unreachable, ICMP4TimeExceeded: if len(q.b) < q.subofs+8 {
return true return false
} }
t := ICMP4Type(q.b[q.subofs])
return t == ICMP4Unreachable || t == ICMP4TimeExceeded
case ICMPv6:
if len(q.b) < q.subofs+8 {
return false
} }
t := ICMP6Type(q.b[q.subofs])
return t == ICMP6Unreachable || t == ICMP6TimeExceeded
default:
return false return false
} }
}
// IsEchoRequest reports whether q is an IPv4 ICMP Echo Request. // IsEchoRequest reports whether q is an ICMP Echo Request.
func (q *Parsed) IsEchoRequest() bool { func (q *Parsed) IsEchoRequest() bool {
if q.IPProto == ICMP && len(q.b) >= q.subofs+8 { switch q.IPProto {
return ICMP4Type(q.b[q.subofs]) == ICMP4EchoRequest && case ICMPv4:
ICMP4Code(q.b[q.subofs+1]) == ICMP4NoCode return len(q.b) >= q.subofs+8 && ICMP4Type(q.b[q.subofs]) == ICMP4EchoRequest && ICMP4Code(q.b[q.subofs+1]) == ICMP4NoCode
} case ICMPv6:
return len(q.b) >= q.subofs+8 && ICMP6Type(q.b[q.subofs]) == ICMP6EchoRequest && ICMP6Code(q.b[q.subofs+1]) == ICMP6NoCode
default:
return false return false
} }
}
// IsEchoRequest reports whether q is an IPv4 ICMP Echo Response. // IsEchoRequest reports whether q is an IPv4 ICMP Echo Response.
func (q *Parsed) IsEchoResponse() bool { func (q *Parsed) IsEchoResponse() bool {
if q.IPProto == ICMP && len(q.b) >= q.subofs+8 { switch q.IPProto {
return ICMP4Type(q.b[q.subofs]) == ICMP4EchoReply && case ICMPv4:
ICMP4Code(q.b[q.subofs+1]) == ICMP4NoCode return len(q.b) >= q.subofs+8 && ICMP4Type(q.b[q.subofs]) == ICMP4EchoReply && ICMP4Code(q.b[q.subofs+1]) == ICMP4NoCode
} case ICMPv6:
return len(q.b) >= q.subofs+8 && ICMP6Type(q.b[q.subofs]) == ICMP6EchoReply && ICMP6Code(q.b[q.subofs+1]) == ICMP6NoCode
default:
return false return false
} }
}
func Hexdump(b []byte) string { func Hexdump(b []byte) string {
out := new(strings.Builder) out := new(strings.Builder)

@ -6,14 +6,31 @@ package packet
import ( import (
"bytes" "bytes"
"net"
"reflect" "reflect"
"testing" "testing"
"inet.af/netaddr"
) )
func mustIP4(s string) IP4 {
ip, err := netaddr.ParseIP(s)
if err != nil {
panic(err)
}
return IP4FromNetaddr(ip)
}
func mustIP6(s string) IP6 {
ip, err := netaddr.ParseIP(s)
if err != nil {
panic(err)
}
return IP6FromNetaddr(ip)
}
func TestIP4String(t *testing.T) { func TestIP4String(t *testing.T) {
const str = "1.2.3.4" const str = "1.2.3.4"
ip := NewIP4(net.ParseIP(str)) ip := mustIP4(str)
var got string var got string
allocs := testing.AllocsPerRun(1000, func() { allocs := testing.AllocsPerRun(1000, func() {
@ -28,7 +45,24 @@ func TestIP4String(t *testing.T) {
} }
} }
var icmpRequestBuffer = []byte{ func TestIP6String(t *testing.T) {
const str = "2607:f8b0:400a:809::200e"
ip := mustIP6(str)
var got string
allocs := testing.AllocsPerRun(1000, func() {
got = ip.String()
})
if got != str {
t.Errorf("got %q; want %q", got, str)
}
if allocs != 2 {
t.Errorf("allocs = %v; want 1", allocs)
}
}
var icmp4RequestBuffer = []byte{
// IP header up to checksum // IP header up to checksum
0x45, 0x00, 0x00, 0x27, 0xde, 0xad, 0x00, 0x00, 0x40, 0x01, 0x8c, 0x15, 0x45, 0x00, 0x00, 0x27, 0xde, 0xad, 0x00, 0x00, 0x40, 0x01, 0x8c, 0x15,
// source ip // source ip
@ -41,21 +75,21 @@ var icmpRequestBuffer = []byte{
0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64,
} }
var icmpRequestDecode = Parsed{ var icmp4RequestDecode = Parsed{
b: icmpRequestBuffer, b: icmp4RequestBuffer,
subofs: 20, subofs: 20,
dataofs: 24, dataofs: 24,
length: len(icmpRequestBuffer), length: len(icmp4RequestBuffer),
IPVersion: 4, IPVersion: 4,
IPProto: ICMP, IPProto: ICMPv4,
SrcIP: NewIP4(net.ParseIP("1.2.3.4")), SrcIP4: mustIP4("1.2.3.4"),
DstIP: NewIP4(net.ParseIP("5.6.7.8")), DstIP4: mustIP4("5.6.7.8"),
SrcPort: 0, SrcPort: 0,
DstPort: 0, DstPort: 0,
} }
var icmpReplyBuffer = []byte{ var icmp4ReplyBuffer = []byte{
0x45, 0x00, 0x00, 0x25, 0x21, 0x52, 0x00, 0x00, 0x40, 0x01, 0x49, 0x73, 0x45, 0x00, 0x00, 0x25, 0x21, 0x52, 0x00, 0x00, 0x40, 0x01, 0x49, 0x73,
// source ip // source ip
0x05, 0x06, 0x07, 0x08, 0x05, 0x06, 0x07, 0x08,
@ -67,22 +101,22 @@ var icmpReplyBuffer = []byte{
0x72, 0x65, 0x70, 0x6c, 0x79, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x72, 0x65, 0x70, 0x6c, 0x79, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64,
} }
var icmpReplyDecode = Parsed{ var icmp4ReplyDecode = Parsed{
b: icmpReplyBuffer, b: icmp4ReplyBuffer,
subofs: 20, subofs: 20,
dataofs: 24, dataofs: 24,
length: len(icmpReplyBuffer), length: len(icmp4ReplyBuffer),
IPVersion: 4, IPVersion: 4,
IPProto: ICMP, IPProto: ICMPv4,
SrcIP: NewIP4(net.ParseIP("1.2.3.4")), SrcIP4: mustIP4("1.2.3.4"),
DstIP: NewIP4(net.ParseIP("5.6.7.8")), DstIP4: mustIP4("5.6.7.8"),
SrcPort: 0, SrcPort: 0,
DstPort: 0, DstPort: 0,
} }
// IPv6 Router Solicitation // ICMPv6 Router Solicitation
var ipv6PacketBuffer = []byte{ var icmp6PacketBuffer = []byte{
0x60, 0x00, 0x00, 0x00, 0x00, 0x08, 0x3a, 0xff, 0x60, 0x00, 0x00, 0x00, 0x00, 0x08, 0x3a, 0xff,
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xfb, 0x57, 0x1d, 0xea, 0x9c, 0x39, 0x8f, 0xb7, 0xfb, 0x57, 0x1d, 0xea, 0x9c, 0x39, 0x8f, 0xb7,
@ -91,10 +125,15 @@ var ipv6PacketBuffer = []byte{
0x85, 0x00, 0x38, 0x04, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x38, 0x04, 0x00, 0x00, 0x00, 0x00,
} }
var ipv6PacketDecode = Parsed{ var icmp6PacketDecode = Parsed{
b: ipv6PacketBuffer, b: icmp6PacketBuffer,
subofs: 40,
dataofs: 44,
length: len(icmp6PacketBuffer),
IPVersion: 6, IPVersion: 6,
IPProto: ICMPv6, IPProto: ICMPv6,
SrcIP6: mustIP6("fe80::fb57:1dea:9c39:8fb7"),
DstIP6: mustIP6("ff02::2"),
} }
// This is a malformed IPv4 packet. // This is a malformed IPv4 packet.
@ -109,7 +148,7 @@ var unknownPacketDecode = Parsed{
IPProto: Unknown, IPProto: Unknown,
} }
var tcpPacketBuffer = []byte{ var tcp4PacketBuffer = []byte{
// IP header up to checksum // IP header up to checksum
0x45, 0x00, 0x00, 0x37, 0xde, 0xad, 0x00, 0x00, 0x40, 0x06, 0x49, 0x5f, 0x45, 0x00, 0x00, 0x37, 0xde, 0xad, 0x00, 0x00, 0x40, 0x06, 0x49, 0x5f,
// source ip // source ip
@ -123,22 +162,50 @@ var tcpPacketBuffer = []byte{
0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64,
} }
var tcpPacketDecode = Parsed{ var tcp4PacketDecode = Parsed{
b: tcpPacketBuffer, b: tcp4PacketBuffer,
subofs: 20, subofs: 20,
dataofs: 40, dataofs: 40,
length: len(tcpPacketBuffer), length: len(tcp4PacketBuffer),
IPVersion: 4, IPVersion: 4,
IPProto: TCP, IPProto: TCP,
SrcIP: NewIP4(net.ParseIP("1.2.3.4")), SrcIP4: mustIP4("1.2.3.4"),
DstIP: NewIP4(net.ParseIP("5.6.7.8")), DstIP4: mustIP4("5.6.7.8"),
SrcPort: 123, SrcPort: 123,
DstPort: 567, DstPort: 567,
TCPFlags: TCPSynAck, TCPFlags: TCPSynAck,
} }
var udpRequestBuffer = []byte{ var tcp6RequestBuffer = []byte{
// IPv6 header up to hop limit
0x60, 0x06, 0xef, 0xcc, 0x00, 0x28, 0x06, 0x40,
// Src addr
0x20, 0x01, 0x05, 0x59, 0xbc, 0x13, 0x54, 0x00, 0x17, 0x49, 0x46, 0x28, 0x39, 0x34, 0x0e, 0x1b,
// Dst addr
0x26, 0x07, 0xf8, 0xb0, 0x40, 0x0a, 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x0e,
// TCP SYN segment, no payload
0xa4, 0x60, 0x00, 0x50, 0xf3, 0x82, 0xa1, 0x25, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x02, 0xfd, 0x20,
0xb1, 0xc6, 0x00, 0x00, 0x02, 0x04, 0x05, 0xa0, 0x04, 0x02, 0x08, 0x0a, 0xca, 0x76, 0xa6, 0x8e,
0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x07,
}
var tcp6RequestDecode = Parsed{
b: tcp6RequestBuffer,
subofs: 40,
dataofs: len(tcp6RequestBuffer),
length: len(tcp6RequestBuffer),
IPVersion: 6,
IPProto: TCP,
SrcIP6: mustIP6("2001:559:bc13:5400:1749:4628:3934:e1b"),
DstIP6: mustIP6("2607:f8b0:400a:809::200e"),
SrcPort: 42080,
DstPort: 80,
TCPFlags: TCPSyn,
}
var udp4RequestBuffer = []byte{
// IP header up to checksum // IP header up to checksum
0x45, 0x00, 0x00, 0x2b, 0xde, 0xad, 0x00, 0x00, 0x40, 0x11, 0x8c, 0x01, 0x45, 0x00, 0x00, 0x2b, 0xde, 0xad, 0x00, 0x00, 0x40, 0x11, 0x8c, 0x01,
// source ip // source ip
@ -151,21 +218,70 @@ var udpRequestBuffer = []byte{
0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64,
} }
var udpRequestDecode = Parsed{ var udp4RequestDecode = Parsed{
b: udpRequestBuffer, b: udp4RequestBuffer,
subofs: 20, subofs: 20,
dataofs: 28, dataofs: 28,
length: len(udpRequestBuffer), length: len(udp4RequestBuffer),
IPVersion: 4, IPVersion: 4,
IPProto: UDP, IPProto: UDP,
SrcIP: NewIP4(net.ParseIP("1.2.3.4")), SrcIP4: mustIP4("1.2.3.4"),
DstIP: NewIP4(net.ParseIP("5.6.7.8")), DstIP4: mustIP4("5.6.7.8"),
SrcPort: 123, SrcPort: 123,
DstPort: 567, DstPort: 567,
} }
var udpReplyBuffer = []byte{ var invalid4RequestBuffer = []byte{
// IP header up to checksum. IHL field points beyond end of packet.
0x4a, 0x00, 0x00, 0x14, 0xde, 0xad, 0x00, 0x00, 0x40, 0x11, 0x8c, 0x01,
// source ip
0x01, 0x02, 0x03, 0x04,
// destination ip
0x05, 0x06, 0x07, 0x08,
}
// Regression check for the IHL field pointing beyond the end of the
// packet.
var invalid4RequestDecode = Parsed{
b: invalid4RequestBuffer,
subofs: 40,
length: len(invalid4RequestBuffer),
IPVersion: 4,
IPProto: Unknown,
SrcIP4: mustIP4("1.2.3.4"),
DstIP4: mustIP4("5.6.7.8"),
}
var udp6RequestBuffer = []byte{
// IPv6 header up to hop limit
0x60, 0x0e, 0xc9, 0x67, 0x00, 0x29, 0x11, 0x40,
// Src addr
0x20, 0x01, 0x05, 0x59, 0xbc, 0x13, 0x54, 0x00, 0x17, 0x49, 0x46, 0x28, 0x39, 0x34, 0x0e, 0x1b,
// Dst addr
0x26, 0x07, 0xf8, 0xb0, 0x40, 0x0a, 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x0e,
// UDP header
0xd4, 0x04, 0x01, 0xbb, 0x00, 0x29, 0x96, 0x84,
// Payload
0x5c, 0x06, 0xae, 0x85, 0x02, 0xf5, 0xdb, 0x90, 0xe0, 0xe0, 0x93, 0xed, 0x9a, 0xd9, 0x92, 0x69, 0xbe, 0x36, 0x8a, 0x7d, 0xd7, 0xce, 0xd0, 0x8a, 0xf2, 0x51, 0x95, 0xff, 0xb6, 0x92, 0x70, 0x10, 0xd7,
}
var udp6RequestDecode = Parsed{
b: udp6RequestBuffer,
subofs: 40,
dataofs: 48,
length: len(udp6RequestBuffer),
IPVersion: 6,
IPProto: UDP,
SrcIP6: mustIP6("2001:559:bc13:5400:1749:4628:3934:e1b"),
DstIP6: mustIP6("2607:f8b0:400a:809::200e"),
SrcPort: 54276,
DstPort: 443,
}
var udp4ReplyBuffer = []byte{
// IP header up to checksum // IP header up to checksum
0x45, 0x00, 0x00, 0x29, 0x21, 0x52, 0x00, 0x00, 0x40, 0x11, 0x49, 0x5f, 0x45, 0x00, 0x00, 0x29, 0x21, 0x52, 0x00, 0x00, 0x40, 0x11, 0x49, 0x5f,
// source ip // source ip
@ -178,15 +294,15 @@ var udpReplyBuffer = []byte{
0x72, 0x65, 0x70, 0x6c, 0x79, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x72, 0x65, 0x70, 0x6c, 0x79, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64,
} }
var udpReplyDecode = Parsed{ var udp4ReplyDecode = Parsed{
b: udpReplyBuffer, b: udp4ReplyBuffer,
subofs: 20, subofs: 20,
dataofs: 28, dataofs: 28,
length: len(udpReplyBuffer), length: len(udp4ReplyBuffer),
IPProto: UDP, IPProto: UDP,
SrcIP: NewIP4(net.ParseIP("1.2.3.4")), SrcIP4: mustIP4("1.2.3.4"),
DstIP: NewIP4(net.ParseIP("5.6.7.8")), DstIP4: mustIP4("5.6.7.8"),
SrcPort: 567, SrcPort: 567,
DstPort: 123, DstPort: 123,
} }
@ -197,10 +313,13 @@ func TestParsed(t *testing.T) {
qdecode Parsed qdecode Parsed
want string want string
}{ }{
{"tcp", tcpPacketDecode, "TCP{1.2.3.4:123 > 5.6.7.8:567}"}, {"tcp4", tcp4PacketDecode, "TCP{1.2.3.4:123 > 5.6.7.8:567}"},
{"icmp", icmpRequestDecode, "ICMP{1.2.3.4:0 > 5.6.7.8:0}"}, {"tcp6", tcp6RequestDecode, "TCP{[2001:559:bc13:5400:1749:4628:3934:e1b]:42080 > [2607:f8b0:400a:809::200e]:80}"},
{"udp4", udp4RequestDecode, "UDP{1.2.3.4:123 > 5.6.7.8:567}"},
{"udp6", udp6RequestDecode, "UDP{[2001:559:bc13:5400:1749:4628:3934:e1b]:54276 > [2607:f8b0:400a:809::200e]:443}"},
{"icmp4", icmp4RequestDecode, "ICMPv4{1.2.3.4:0 > 5.6.7.8:0}"},
{"icmp6", icmp6PacketDecode, "ICMPv6{[fe80::fb57:1dea:9c39:8fb7]:0 > [ff02::2]:0}"},
{"unknown", unknownPacketDecode, "Unknown{???}"}, {"unknown", unknownPacketDecode, "Unknown{???}"},
{"ipv6", ipv6PacketDecode, "IPv6{Proto=58}"},
} }
for _, tt := range tests { for _, tt := range tests {
@ -228,11 +347,14 @@ func TestDecode(t *testing.T) {
buf []byte buf []byte
want Parsed want Parsed
}{ }{
{"icmp", icmpRequestBuffer, icmpRequestDecode}, {"icmp4", icmp4RequestBuffer, icmp4RequestDecode},
{"ipv6", ipv6PacketBuffer, ipv6PacketDecode}, {"icmp6", icmp6PacketBuffer, icmp6PacketDecode},
{"tcp4", tcp4PacketBuffer, tcp4PacketDecode},
{"tcp6", tcp6RequestBuffer, tcp6RequestDecode},
{"udp4", udp4RequestBuffer, udp4RequestDecode},
{"udp6", udp6RequestBuffer, udp6RequestDecode},
{"unknown", unknownPacketBuffer, unknownPacketDecode}, {"unknown", unknownPacketBuffer, unknownPacketDecode},
{"tcp", tcpPacketBuffer, tcpPacketDecode}, {"invalid4", invalid4RequestBuffer, invalid4RequestDecode},
{"udp", udpRequestBuffer, udpRequestDecode},
} }
for _, tt := range tests { for _, tt := range tests {
@ -259,9 +381,13 @@ func BenchmarkDecode(b *testing.B) {
name string name string
buf []byte buf []byte
}{ }{
{"icmp", icmpRequestBuffer}, {"tcp4", tcp4PacketBuffer},
{"tcp6", tcp6RequestBuffer},
{"udp4", udp4RequestBuffer},
{"udp6", udp6RequestBuffer},
{"icmp4", icmp4RequestBuffer},
{"icmp6", icmp6PacketBuffer},
{"unknown", unknownPacketBuffer}, {"unknown", unknownPacketBuffer},
{"tcp", tcpPacketBuffer},
} }
for _, bench := range benches { for _, bench := range benches {
@ -280,15 +406,15 @@ func TestMarshalRequest(t *testing.T) {
var small [20]byte var small [20]byte
var large [64]byte var large [64]byte
icmpHeader := icmpRequestDecode.ICMPHeader() icmpHeader := icmp4RequestDecode.ICMP4Header()
udpHeader := udpRequestDecode.UDPHeader() udpHeader := udp4RequestDecode.UDP4Header()
tests := []struct { tests := []struct {
name string name string
header Header header Header
want []byte want []byte
}{ }{
{"icmp", &icmpHeader, icmpRequestBuffer}, {"icmp", &icmpHeader, icmp4RequestBuffer},
{"udp", &udpHeader, udpRequestBuffer}, {"udp", &udpHeader, udp4RequestBuffer},
} }
for _, tt := range tests { for _, tt := range tests {
@ -317,16 +443,16 @@ func TestMarshalRequest(t *testing.T) {
func TestMarshalResponse(t *testing.T) { func TestMarshalResponse(t *testing.T) {
var buf [64]byte var buf [64]byte
icmpHeader := icmpRequestDecode.ICMPHeader() icmpHeader := icmp4RequestDecode.ICMP4Header()
udpHeader := udpRequestDecode.UDPHeader() udpHeader := udp4RequestDecode.UDP4Header()
tests := []struct { tests := []struct {
name string name string
header Header header Header
want []byte want []byte
}{ }{
{"icmp", &icmpHeader, icmpReplyBuffer}, {"icmp", &icmpHeader, icmp4ReplyBuffer},
{"udp", &udpHeader, udpReplyBuffer}, {"udp", &udpHeader, udp4ReplyBuffer},
} }
for _, tt := range tests { for _, tt := range tests {

@ -4,25 +4,27 @@
package packet package packet
// UDPHeader represents an UDP packet header. import "encoding/binary"
// udpHeaderLength is the size of the UDP packet header, not including
// the outer IP header.
const udpHeaderLength = 8
// UDP4Header is an IPv4+UDP header.
type UDP4Header struct { type UDP4Header struct {
IP4Header IP4Header
SrcPort uint16 SrcPort uint16
DstPort uint16 DstPort uint16
} }
const ( // Len implements Header.
udpHeaderLength = 8 func (h UDP4Header) Len() int {
// udpTotalHeaderLength is the length of all headers in a UDP packet. return h.IP4Header.Len() + udpHeaderLength
udpTotalHeaderLength = ipHeaderLength + udpHeaderLength
)
func (UDP4Header) Len() int {
return udpTotalHeaderLength
} }
// Marshal implements Header.
func (h UDP4Header) Marshal(buf []byte) error { func (h UDP4Header) Marshal(buf []byte) error {
if len(buf) < udpTotalHeaderLength { if len(buf) < h.Len() {
return errSmallBuffer return errSmallBuffer
} }
if len(buf) > maxPacketLength { if len(buf) > maxPacketLength {
@ -32,21 +34,21 @@ func (h UDP4Header) Marshal(buf []byte) error {
h.IPProto = UDP h.IPProto = UDP
length := len(buf) - h.IP4Header.Len() length := len(buf) - h.IP4Header.Len()
put16(buf[20:22], h.SrcPort) binary.BigEndian.PutUint16(buf[20:22], h.SrcPort)
put16(buf[22:24], h.DstPort) binary.BigEndian.PutUint16(buf[22:24], h.DstPort)
put16(buf[24:26], uint16(length)) binary.BigEndian.PutUint16(buf[24:26], uint16(length))
put16(buf[26:28], 0) // blank checksum binary.BigEndian.PutUint16(buf[26:28], 0) // blank checksum
h.IP4Header.MarshalPseudo(buf)
// UDP checksum with IP pseudo header. // UDP checksum with IP pseudo header.
put16(buf[26:28], ipChecksum(buf[8:])) h.IP4Header.marshalPseudo(buf)
binary.BigEndian.PutUint16(buf[26:28], ip4Checksum(buf[ip4PseudoHeaderOffset:]))
h.IP4Header.Marshal(buf) h.IP4Header.Marshal(buf)
return nil return nil
} }
// ToResponse implements Header.
func (h *UDP4Header) ToResponse() { func (h *UDP4Header) ToResponse() {
h.SrcPort, h.DstPort = h.DstPort, h.SrcPort h.SrcPort, h.DstPort = h.DstPort, h.SrcPort
h.IP4Header.ToResponse() h.IP4Header.ToResponse()

@ -191,8 +191,8 @@ func (f *Filter) CheckTCP(srcIP, dstIP netaddr.IP, dstPort uint16) Response {
pkt.IPVersion = 4 pkt.IPVersion = 4
pkt.IPProto = packet.TCP pkt.IPProto = packet.TCP
pkt.TCPFlags = packet.TCPSyn pkt.TCPFlags = packet.TCPSyn
pkt.SrcIP = packet.IP4FromNetaddr(srcIP) // TODO: IPv6 pkt.SrcIP4 = packet.IP4FromNetaddr(srcIP) // TODO: IPv6
pkt.DstIP = packet.IP4FromNetaddr(dstIP) pkt.DstIP4 = packet.IP4FromNetaddr(dstIP)
pkt.SrcPort = 0 pkt.SrcPort = 0
pkt.DstPort = dstPort pkt.DstPort = dstPort
@ -233,7 +233,7 @@ func (f *Filter) runIn(q *packet.Parsed) (r Response, why string) {
// A compromised peer could try to send us packets for // A compromised peer could try to send us packets for
// destinations we didn't explicitly advertise. This check is to // destinations we didn't explicitly advertise. This check is to
// prevent that. // prevent that.
if !ip4InList(q.DstIP, f.local4) { if !ip4InList(q.DstIP4, f.local4) {
return Drop, "destination not allowed" return Drop, "destination not allowed"
} }
@ -243,7 +243,7 @@ func (f *Filter) runIn(q *packet.Parsed) (r Response, why string) {
} }
switch q.IPProto { switch q.IPProto {
case packet.ICMP: case packet.ICMPv4:
if q.IsEchoResponse() || q.IsError() { if q.IsEchoResponse() || q.IsError() {
// ICMP responses are allowed. // ICMP responses are allowed.
// TODO(apenwarr): consider using conntrack state. // TODO(apenwarr): consider using conntrack state.
@ -271,7 +271,7 @@ func (f *Filter) runIn(q *packet.Parsed) (r Response, why string) {
return Accept, "tcp ok" return Accept, "tcp ok"
} }
case packet.UDP: case packet.UDP:
t := tuple{q.SrcIP, q.DstIP, q.SrcPort, q.DstPort} t := tuple{q.SrcIP4, q.DstIP4, q.SrcPort, q.DstPort}
f.state.mu.Lock() f.state.mu.Lock()
_, ok := f.state.lru.Get(t) _, ok := f.state.lru.Get(t)
@ -292,7 +292,7 @@ func (f *Filter) runIn(q *packet.Parsed) (r Response, why string) {
// runIn runs the output-specific part of the filter logic. // runIn runs the output-specific part of the filter logic.
func (f *Filter) runOut(q *packet.Parsed) (r Response, why string) { func (f *Filter) runOut(q *packet.Parsed) (r Response, why string) {
if q.IPProto == packet.UDP { if q.IPProto == packet.UDP {
t := tuple{q.DstIP, q.SrcIP, q.DstPort, q.SrcPort} t := tuple{q.DstIP4, q.SrcIP4, q.DstPort, q.SrcPort}
var ti interface{} = t // allocate once, rather than twice inside mutex var ti interface{} = t // allocate once, rather than twice inside mutex
f.state.mu.Lock() f.state.mu.Lock()
@ -338,11 +338,11 @@ func (f *Filter) pre(q *packet.Parsed, rf RunFlags, dir direction) Response {
f.logRateLimit(rf, q, dir, Drop, "ipv6") f.logRateLimit(rf, q, dir, Drop, "ipv6")
return Drop return Drop
} }
if q.DstIP.IsMulticast() { if q.DstIP4.IsMulticast() {
f.logRateLimit(rf, q, dir, Drop, "multicast") f.logRateLimit(rf, q, dir, Drop, "multicast")
return Drop return Drop
} }
if q.DstIP.IsLinkLocalUnicast() { if q.DstIP4.IsLinkLocalUnicast() {
f.logRateLimit(rf, q, dir, Drop, "link-local-unicast") f.logRateLimit(rf, q, dir, Drop, "link-local-unicast")
return Drop return Drop
} }
@ -383,13 +383,13 @@ func omitDropLogging(p *packet.Parsed, dir direction) bool {
// it doesn't know about, so parse it out ourselves if needed. // it doesn't know about, so parse it out ourselves if needed.
ipProto := p.IPProto ipProto := p.IPProto
if ipProto == 0 && len(b) > 8 { if ipProto == 0 && len(b) > 8 {
ipProto = packet.IP4Proto(b[9]) ipProto = packet.IPProto(b[9])
} }
// Omit logging about outgoing IGMP. // Omit logging about outgoing IGMP.
if ipProto == packet.IGMP { if ipProto == packet.IGMP {
return true return true
} }
if p.DstIP.IsMulticast() || p.DstIP.IsLinkLocalUnicast() { if p.DstIP4.IsMulticast() || p.DstIP4.IsLinkLocalUnicast() {
return true return true
} }
case 6: case 6:

@ -9,7 +9,6 @@ import (
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net"
"strconv" "strconv"
"strings" "strings"
"testing" "testing"
@ -20,11 +19,19 @@ import (
) )
var Unknown = packet.Unknown var Unknown = packet.Unknown
var ICMP = packet.ICMP var ICMPv4 = packet.ICMPv4
var TCP = packet.TCP var TCP = packet.TCP
var UDP = packet.UDP var UDP = packet.UDP
var Fragment = packet.Fragment var Fragment = packet.Fragment
func mustIP4(s string) packet.IP4 {
ip, err := netaddr.ParseIP(s)
if err != nil {
panic(err)
}
return packet.IP4FromNetaddr(ip)
}
func pfx(s string) netaddr.IPPrefix { func pfx(s string) netaddr.IPPrefix {
pfx, err := netaddr.ParseIPPrefix(s) pfx, err := netaddr.ParseIPPrefix(s)
if err != nil { if err != nil {
@ -140,7 +147,7 @@ func TestFilter(t *testing.T) {
// Basic // Basic
{Accept, parsed(TCP, 0x08010101, 0x01020304, 999, 22)}, {Accept, parsed(TCP, 0x08010101, 0x01020304, 999, 22)},
{Accept, parsed(UDP, 0x08010101, 0x01020304, 999, 22)}, {Accept, parsed(UDP, 0x08010101, 0x01020304, 999, 22)},
{Accept, parsed(ICMP, 0x08010101, 0x01020304, 0, 0)}, {Accept, parsed(ICMPv4, 0x08010101, 0x01020304, 0, 0)},
{Drop, parsed(TCP, 0x08010101, 0x01020304, 0, 0)}, {Drop, parsed(TCP, 0x08010101, 0x01020304, 0, 0)},
{Accept, parsed(TCP, 0x08010101, 0x01020304, 0, 22)}, {Accept, parsed(TCP, 0x08010101, 0x01020304, 0, 22)},
{Drop, parsed(TCP, 0x08010101, 0x01020304, 0, 21)}, {Drop, parsed(TCP, 0x08010101, 0x01020304, 0, 21)},
@ -168,7 +175,7 @@ func TestFilter(t *testing.T) {
t.Errorf("#%d runIn got=%v want=%v packet:%v", i, got, test.want, test.p) t.Errorf("#%d runIn got=%v want=%v packet:%v", i, got, test.want, test.p)
} }
if test.p.IPProto == TCP { if test.p.IPProto == TCP {
if got := acl.CheckTCP(test.p.SrcIP.Netaddr(), test.p.DstIP.Netaddr(), test.p.DstPort); test.want != got { if got := acl.CheckTCP(test.p.SrcIP4.Netaddr(), test.p.DstIP4.Netaddr(), test.p.DstPort); test.want != got {
t.Errorf("#%d CheckTCP got=%v want=%v packet:%v", i, got, test.want, test.p) t.Errorf("#%d CheckTCP got=%v want=%v packet:%v", i, got, test.want, test.p)
} }
} }
@ -250,7 +257,7 @@ func BenchmarkFilter(b *testing.B) {
tcpPacket := rawpacket(TCP, 0x08010101, 0x01020304, 999, 22, 0) tcpPacket := rawpacket(TCP, 0x08010101, 0x01020304, 999, 22, 0)
udpPacket := rawpacket(UDP, 0x08010101, 0x01020304, 999, 22, 0) udpPacket := rawpacket(UDP, 0x08010101, 0x01020304, 999, 22, 0)
icmpPacket := rawpacket(ICMP, 0x08010101, 0x01020304, 0, 0, 0) icmpPacket := rawpacket(ICMPv4, 0x08010101, 0x01020304, 0, 0, 0)
tcpSynPacket := rawpacket(TCP, 0x08010101, 0x01020304, 999, 22, 0) tcpSynPacket := rawpacket(TCP, 0x08010101, 0x01020304, 999, 22, 0)
// TCP filtering is trivial (Accept) for non-SYN packets. // TCP filtering is trivial (Accept) for non-SYN packets.
@ -299,7 +306,7 @@ func TestPreFilter(t *testing.T) {
{"fragment", Accept, rawdefault(Fragment, 40)}, {"fragment", Accept, rawdefault(Fragment, 40)},
{"tcp", noVerdict, rawdefault(TCP, 200)}, {"tcp", noVerdict, rawdefault(TCP, 200)},
{"udp", noVerdict, rawdefault(UDP, 200)}, {"udp", noVerdict, rawdefault(UDP, 200)},
{"icmp", noVerdict, rawdefault(ICMP, 200)}, {"icmp", noVerdict, rawdefault(ICMPv4, 200)},
} }
f := NewAllowNone(t.Logf) f := NewAllowNone(t.Logf)
for _, testPacket := range packets { for _, testPacket := range packets {
@ -312,11 +319,11 @@ func TestPreFilter(t *testing.T) {
} }
} }
func parsed(proto packet.IP4Proto, src, dst packet.IP4, sport, dport uint16) packet.Parsed { func parsed(proto packet.IPProto, src, dst packet.IP4, sport, dport uint16) packet.Parsed {
return packet.Parsed{ return packet.Parsed{
IPProto: proto, IPProto: proto,
SrcIP: src, SrcIP4: src,
DstIP: dst, DstIP4: dst,
SrcPort: sport, SrcPort: sport,
DstPort: dport, DstPort: dport,
TCPFlags: packet.TCPSyn, TCPFlags: packet.TCPSyn,
@ -325,11 +332,11 @@ func parsed(proto packet.IP4Proto, src, dst packet.IP4, sport, dport uint16) pac
// rawpacket generates a packet with given source and destination ports and IPs // rawpacket generates a packet with given source and destination ports and IPs
// and resizes the header to trimLength if it is nonzero. // and resizes the header to trimLength if it is nonzero.
func rawpacket(proto packet.IP4Proto, src, dst packet.IP4, sport, dport uint16, trimLength int) []byte { func rawpacket(proto packet.IPProto, src, dst packet.IP4, sport, dport uint16, trimLength int) []byte {
var headerLength int var headerLength int
switch proto { switch proto {
case ICMP: case ICMPv4:
headerLength = 24 headerLength = 24
case TCP: case TCP:
headerLength = 40 headerLength = 40
@ -357,7 +364,7 @@ func rawpacket(proto packet.IP4Proto, src, dst packet.IP4, sport, dport uint16,
bin.PutUint16(hdr[22:24], dport) bin.PutUint16(hdr[22:24], dport)
switch proto { switch proto {
case ICMP: case ICMPv4:
hdr[9] = 1 hdr[9] = 1
case TCP: case TCP:
hdr[9] = 6 hdr[9] = 6
@ -379,7 +386,7 @@ func rawpacket(proto packet.IP4Proto, src, dst packet.IP4, sport, dport uint16,
} }
// rawdefault calls rawpacket with default ports and IPs. // rawdefault calls rawpacket with default ports and IPs.
func rawdefault(proto packet.IP4Proto, trimLength int) []byte { func rawdefault(proto packet.IPProto, trimLength int) []byte {
ip := packet.IP4(0x08080808) // 8.8.8.8 ip := packet.IP4(0x08080808) // 8.8.8.8
port := uint16(53) port := uint16(53)
return rawpacket(proto, ip, ip, port, port, trimLength) return rawpacket(proto, ip, ip, port, port, trimLength)
@ -435,19 +442,19 @@ func TestOmitDropLogging(t *testing.T) {
}, },
{ {
name: "v4_multicast_out_low", name: "v4_multicast_out_low",
pkt: &packet.Parsed{IPVersion: 4, DstIP: packet.NewIP4(net.ParseIP("224.0.0.0"))}, pkt: &packet.Parsed{IPVersion: 4, DstIP4: mustIP4("224.0.0.0")},
dir: out, dir: out,
want: true, want: true,
}, },
{ {
name: "v4_multicast_out_high", name: "v4_multicast_out_high",
pkt: &packet.Parsed{IPVersion: 4, DstIP: packet.NewIP4(net.ParseIP("239.255.255.255"))}, pkt: &packet.Parsed{IPVersion: 4, DstIP4: mustIP4("239.255.255.255")},
dir: out, dir: out,
want: true, want: true,
}, },
{ {
name: "v4_link_local_unicast", name: "v4_link_local_unicast",
pkt: &packet.Parsed{IPVersion: 4, DstIP: packet.NewIP4(net.ParseIP("169.254.1.2"))}, pkt: &packet.Parsed{IPVersion: 4, DstIP4: mustIP4("169.254.1.2")},
dir: out, dir: out,
want: true, want: true,
}, },

@ -104,11 +104,11 @@ func newMatches4(ms []Match) (ret matches4) {
// any of ms. // any of ms.
func (ms matches4) match(q *packet.Parsed) bool { func (ms matches4) match(q *packet.Parsed) bool {
for _, m := range ms { for _, m := range ms {
if !ip4InList(q.SrcIP, m.srcs) { if !ip4InList(q.SrcIP4, m.srcs) {
continue continue
} }
for _, dst := range m.dsts { for _, dst := range m.dsts {
if !dst.net.Contains(q.DstIP) { if !dst.net.Contains(q.DstIP4) {
continue continue
} }
if !dst.ports.contains(q.DstPort) { if !dst.ports.contains(q.DstPort) {
@ -124,11 +124,11 @@ func (ms matches4) match(q *packet.Parsed) bool {
// any of ms. // any of ms.
func (ms matches4) matchIPsOnly(q *packet.Parsed) bool { func (ms matches4) matchIPsOnly(q *packet.Parsed) bool {
for _, m := range ms { for _, m := range ms {
if !ip4InList(q.SrcIP, m.srcs) { if !ip4InList(q.SrcIP4, m.srcs) {
continue continue
} }
for _, dst := range m.dsts { for _, dst := range m.dsts {
if dst.net.Contains(q.DstIP) { if dst.net.Contains(q.DstIP4) {
return true return true
} }
} }

@ -283,7 +283,7 @@ func (t *TUN) Read(buf []byte, offset int) (int, error) {
p.Decode(buf[offset : offset+n]) p.Decode(buf[offset : offset+n])
if m, ok := t.destIPActivity.Load().(map[packet.IP4]func()); ok { if m, ok := t.destIPActivity.Load().(map[packet.IP4]func()); ok {
if fn := m[p.DstIP]; fn != nil { if fn := m[p.DstIP4]; fn != nil {
fn() fn()
} }
} }

@ -372,7 +372,7 @@ func newUserspaceEngineAdvanced(conf EngineConfig) (_ Engine, reterr error) {
// echoRespondToAll is an inbound post-filter responding to all echo requests. // echoRespondToAll is an inbound post-filter responding to all echo requests.
func echoRespondToAll(p *packet.Parsed, t *tstun.TUN) filter.Response { func echoRespondToAll(p *packet.Parsed, t *tstun.TUN) filter.Response {
if p.IsEchoRequest() { if p.IsEchoRequest() {
header := p.ICMPHeader() header := p.ICMP4Header()
header.ToResponse() header.ToResponse()
outp := packet.Generate(&header, p.Payload()) outp := packet.Generate(&header, p.Payload())
t.InjectOutbound(outp) t.InjectOutbound(outp)
@ -397,7 +397,7 @@ func (e *userspaceEngine) handleLocalPackets(p *packet.Parsed, t *tstun.TUN) fil
return filter.Drop return filter.Drop
} }
if runtime.GOOS == "darwin" && e.isLocalAddr(p.DstIP) { if runtime.GOOS == "darwin" && e.isLocalAddr(p.DstIP4) {
// macOS NetworkExtension directs packets destined to the // macOS NetworkExtension directs packets destined to the
// tunnel's local IP address into the tunnel, instead of // tunnel's local IP address into the tunnel, instead of
// looping back within the kernel network stack. We have to // looping back within the kernel network stack. We have to
@ -421,10 +421,10 @@ func (e *userspaceEngine) isLocalAddr(ip packet.IP4) bool {
// handleDNS is an outbound pre-filter resolving Tailscale domains. // handleDNS is an outbound pre-filter resolving Tailscale domains.
func (e *userspaceEngine) handleDNS(p *packet.Parsed, t *tstun.TUN) filter.Response { func (e *userspaceEngine) handleDNS(p *packet.Parsed, t *tstun.TUN) filter.Response {
if p.DstIP == magicDNSIP && p.DstPort == magicDNSPort && p.IPProto == packet.UDP { if p.DstIP4 == magicDNSIP && p.DstPort == magicDNSPort && p.IPProto == packet.UDP {
request := tsdns.Packet{ request := tsdns.Packet{
Payload: append([]byte(nil), p.Payload()...), Payload: append([]byte(nil), p.Payload()...),
Addr: netaddr.IPPort{IP: p.SrcIP.Netaddr(), Port: p.SrcPort}, Addr: netaddr.IPPort{IP: p.SrcIP4.Netaddr(), Port: p.SrcPort},
} }
err := e.resolver.EnqueueRequest(request) err := e.resolver.EnqueueRequest(request)
if err != nil { if err != nil {
@ -515,7 +515,7 @@ func (p *pinger) run(ctx context.Context, peerKey wgcfg.Key, ips []wgcfg.IP, src
start := time.Now() start := time.Now()
var dstIPs []packet.IP4 var dstIPs []packet.IP4
for _, ip := range ips { for _, ip := range ips {
dstIPs = append(dstIPs, packet.NewIP4(ip.IP())) dstIPs = append(dstIPs, packet.IP4FromNetaddr(netaddr.IPFrom16(ip.Addr)))
} }
payload := []byte("magicsock_spray") // no meaning payload := []byte("magicsock_spray") // no meaning
@ -555,7 +555,7 @@ func (e *userspaceEngine) pinger(peerKey wgcfg.Key, ips []wgcfg.IP) {
e.wgLock.Lock() e.wgLock.Lock()
if len(e.lastCfgFull.Addresses) > 0 { if len(e.lastCfgFull.Addresses) > 0 {
srcIP = packet.NewIP4(e.lastCfgFull.Addresses[0].IP.IP()) srcIP = packet.IP4FromNetaddr(netaddr.IPFrom16(e.lastCfgFull.Addresses[0].IP.Addr))
} }
e.wgLock.Unlock() e.wgLock.Unlock()

Loading…
Cancel
Save