mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-29 13:05:46 +00:00
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.
This commit is contained in:
commit
563d43b2a5
@ -16,27 +16,34 @@
|
|||||||
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.
|
||||||
|
44
net/packet/icmp6.go
Normal file
44
net/packet/icmp6.go
Normal file
@ -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
|
||||||
|
)
|
53
net/packet/ip.go
Normal file
53
net/packet/ip.go
Normal file
@ -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 @@
|
|||||||
// 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,103 +29,57 @@ 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 (
|
// IP4Header represents an IPv4 packet header.
|
||||||
// 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.
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
if len(buf) > maxPacketLength {
|
|
||||||
return errLargePacket
|
|
||||||
}
|
|
||||||
|
|
||||||
length := len(buf) - ipHeaderLength
|
|
||||||
put32(buf[8:12], uint32(h.SrcIP))
|
|
||||||
put32(buf[12:16], uint32(h.DstIP))
|
|
||||||
buf[16] = 0x0
|
|
||||||
buf[17] = uint8(h.IPProto)
|
|
||||||
put16(buf[18:20], uint16(length))
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -146,3 +90,48 @@ func (h *IP4Header) ToResponse() {
|
|||||||
// Flip the bits in the IPID. If incoming IPIDs are distinct, so are these.
|
// Flip the bits in the IPID. If incoming IPIDs are distinct, so are these.
|
||||||
h.IPID = ^h.IPID
|
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
|
||||||
|
}
|
||||||
|
if len(buf) > maxPacketLength {
|
||||||
|
return errLargePacket
|
||||||
|
}
|
||||||
|
|
||||||
|
length := len(buf) - h.Len()
|
||||||
|
binary.BigEndian.PutUint32(buf[8:12], uint32(h.SrcIP))
|
||||||
|
binary.BigEndian.PutUint32(buf[12:16], uint32(h.DstIP))
|
||||||
|
buf[16] = 0x0
|
||||||
|
buf[17] = uint8(h.IPProto)
|
||||||
|
binary.BigEndian.PutUint16(buf[18:20], uint16(length))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
34
net/packet/ip6.go
Normal file
34
net/packet/ip6.go
Normal file
@ -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 @@
|
|||||||
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:
|
||||||
}
|
sb := strbuilder.Get()
|
||||||
switch p.IPProto {
|
sb.WriteString(p.IPProto.String())
|
||||||
case Unknown:
|
sb.WriteByte('{')
|
||||||
|
writeIP4Port(sb, p.SrcIP4, p.SrcPort)
|
||||||
|
sb.WriteString(" > ")
|
||||||
|
writeIP4Port(sb, p.DstIP4, p.DstPort)
|
||||||
|
sb.WriteByte('}')
|
||||||
|
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{???}"
|
return "Unknown{???}"
|
||||||
}
|
}
|
||||||
sb := strbuilder.Get()
|
|
||||||
sb.WriteString(p.IPProto.String())
|
|
||||||
sb.WriteByte('{')
|
|
||||||
writeIPPort(sb, p.SrcIP, p.SrcPort)
|
|
||||||
sb.WriteString(" > ")
|
|
||||||
writeIPPort(sb, p.DstIP, p.DstPort)
|
|
||||||
sb.WriteByte('}')
|
|
||||||
return sb.String()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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.IPProto = Unknown
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
q.IPVersion = (b[0] & 0xF0) >> 4
|
||||||
|
switch q.IPVersion {
|
||||||
|
case 4:
|
||||||
|
q.decode4(b)
|
||||||
|
case 6:
|
||||||
|
q.decode6(b)
|
||||||
|
default:
|
||||||
|
q.IPVersion = 0
|
||||||
|
q.IPProto = Unknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Parsed) decode4(b []byte) {
|
||||||
|
if len(b) < ip4HeaderLength {
|
||||||
q.IPVersion = 0
|
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
|
q.IPProto = IPProto(b[9])
|
||||||
q.IPVersion = (b[0] & 0xF0) >> 4
|
q.length = int(binary.BigEndian.Uint16(b[2:4]))
|
||||||
switch q.IPVersion {
|
|
||||||
case 4:
|
|
||||||
q.IPProto = IP4Proto(b[9])
|
|
||||||
case 6:
|
|
||||||
q.IPProto = IP4Proto(b[6]) // "Next Header" field
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
q.IPVersion = 0
|
|
||||||
q.IPProto = Unknown
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
q.length = int(get16(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 {
|
||||||
return IP4Header{
|
q.IPVersion = 0
|
||||||
IPID: ipid,
|
q.IPProto = Unknown
|
||||||
IPProto: q.IPProto,
|
return
|
||||||
SrcIP: q.SrcIP,
|
}
|
||||||
DstIP: q.DstIP,
|
|
||||||
|
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) ICMPHeader() ICMP4Header {
|
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{
|
||||||
|
IPID: ipid,
|
||||||
|
IPProto: q.IPProto,
|
||||||
|
SrcIP: q.SrcIP4,
|
||||||
|
DstIP: q.DstIP4,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,58 +337,60 @@ 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 {
|
||||||
|
@ -6,14 +6,31 @@
|
|||||||
|
|
||||||
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 @@ func TestIP4String(t *testing.T) {
|
|||||||
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 @@ func TestIP4String(t *testing.T) {
|
|||||||
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 @@ func TestIP4String(t *testing.T) {
|
|||||||
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 @@ func TestIP4String(t *testing.T) {
|
|||||||
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 @@ func TestIP4String(t *testing.T) {
|
|||||||
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 @@ func TestIP4String(t *testing.T) {
|
|||||||
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 @@ func TestIP4String(t *testing.T) {
|
|||||||
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 @@
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@ -20,11 +19,19 @@
|
|||||||
)
|
)
|
||||||
|
|
||||||
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 @@ type InOut struct {
|
|||||||
// 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 @@ type InOut struct {
|
|||||||
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…
Reference in New Issue
Block a user