mirror of
https://github.com/tailscale/tailscale.git
synced 2025-05-07 08:07:16 +00:00
net/packet: documentation pass.
Signed-off-by: David Anderson <danderson@tailscale.com>
This commit is contained in:
parent
c2cc3acbaf
commit
a38e28da07
@ -6,6 +6,13 @@ package packet
|
|||||||
|
|
||||||
import "encoding/binary"
|
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 (
|
||||||
@ -30,31 +37,29 @@ 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
|
||||||
)
|
)
|
||||||
|
|
||||||
// ICMP4Header represents an ICMPv4 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.
|
||||||
icmp4HeaderLength = 4
|
func (h ICMP4Header) Len() int {
|
||||||
// icmp4AllHeadersLength is the length of all headers in a ICMPv4 packet.
|
return h.IP4Header.Len() + icmp4HeaderLength
|
||||||
icmp4AllHeadersLength = ip4HeaderLength + icmp4HeaderLength
|
|
||||||
)
|
|
||||||
|
|
||||||
func (ICMP4Header) Len() int {
|
|
||||||
return icmp4AllHeadersLength
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Marshal implements Header.
|
||||||
func (h ICMP4Header) Marshal(buf []byte) error {
|
func (h ICMP4Header) Marshal(buf []byte) error {
|
||||||
if len(buf) < icmp4AllHeadersLength {
|
if len(buf) < h.Len() {
|
||||||
return errSmallBuffer
|
return errSmallBuffer
|
||||||
}
|
}
|
||||||
if len(buf) > maxPacketLength {
|
if len(buf) > maxPacketLength {
|
||||||
@ -68,11 +73,14 @@ func (h ICMP4Header) Marshal(buf []byte) error {
|
|||||||
|
|
||||||
h.IP4Header.Marshal(buf)
|
h.IP4Header.Marshal(buf)
|
||||||
|
|
||||||
binary.BigEndian.PutUint16(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.
|
||||||
|
@ -4,6 +4,13 @@
|
|||||||
|
|
||||||
package packet
|
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
|
type ICMP6Type uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -28,10 +35,10 @@ func (t ICMP6Type) String() string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ICMP6Code is an ICMPv6 code, as specified in
|
||||||
|
// https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml
|
||||||
type ICMP6Code uint8
|
type ICMP6Code uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ICMP6NoCode ICMP6Code = 0
|
ICMP6NoCode ICMP6Code = 0
|
||||||
)
|
)
|
||||||
|
|
||||||
const icmp6HeaderLength = 4
|
|
||||||
|
@ -14,13 +14,13 @@ import (
|
|||||||
// IP4 is an IPv4 address.
|
// IP4 is an IPv4 address.
|
||||||
type IP4 uint32
|
type IP4 uint32
|
||||||
|
|
||||||
// IPFromNetaddr converts a netaddr.IP to an IP. Panics if !ip.Is4.
|
// IPFromNetaddr converts a netaddr.IP to an IP4. Panics if !ip.Is4.
|
||||||
func IP4FromNetaddr(ip netaddr.IP) IP4 {
|
func IP4FromNetaddr(ip netaddr.IP) IP4 {
|
||||||
ipbytes := ip.As4()
|
ipbytes := ip.As4()
|
||||||
return IP4(binary.BigEndian.Uint32(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))
|
||||||
}
|
}
|
||||||
@ -29,15 +29,21 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
// IPHeader represents an IP packet header.
|
// ip4HeaderLength is the length of an IPv4 header with no IP options.
|
||||||
|
const ip4HeaderLength = 20
|
||||||
|
|
||||||
|
// IP4Header represents an IPv4 packet header.
|
||||||
type IP4Header struct {
|
type IP4Header struct {
|
||||||
IPProto IPProto
|
IPProto IPProto
|
||||||
IPID uint16
|
IPID uint16
|
||||||
@ -45,59 +51,87 @@ type IP4Header struct {
|
|||||||
DstIP IP4
|
DstIP IP4
|
||||||
}
|
}
|
||||||
|
|
||||||
const ip4HeaderLength = 20
|
// Len implements Header.
|
||||||
|
func (h IP4Header) Len() int {
|
||||||
func (IP4Header) Len() int {
|
|
||||||
return ip4HeaderLength
|
return ip4HeaderLength
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Marshal implements Header.
|
||||||
func (h IP4Header) Marshal(buf []byte) error {
|
func (h IP4Header) Marshal(buf []byte) error {
|
||||||
if len(buf) < ip4HeaderLength {
|
if len(buf) < h.Len() {
|
||||||
return errSmallBuffer
|
return errSmallBuffer
|
||||||
}
|
}
|
||||||
if len(buf) > maxPacketLength {
|
if len(buf) > maxPacketLength {
|
||||||
return errLargePacket
|
return errLargePacket
|
||||||
}
|
}
|
||||||
|
|
||||||
buf[0] = 0x40 | (ip4HeaderLength >> 2) // IPv4
|
buf[0] = 0x40 | (byte(h.Len() >> 2)) // IPv4 + IHL
|
||||||
buf[1] = 0x00 // DHCP, ECN
|
buf[1] = 0x00 // DSCP + ECN
|
||||||
binary.BigEndian.PutUint16(buf[2:4], uint16(len(buf)))
|
binary.BigEndian.PutUint16(buf[2:4], uint16(len(buf))) // Total length
|
||||||
binary.BigEndian.PutUint16(buf[4:6], h.IPID)
|
binary.BigEndian.PutUint16(buf[4:6], h.IPID) // ID
|
||||||
binary.BigEndian.PutUint16(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
|
||||||
binary.BigEndian.PutUint16(buf[10:12], 0) // blank IP header checksum
|
// Blank checksum. This is necessary even though we overwrite
|
||||||
binary.BigEndian.PutUint32(buf[12:16], uint32(h.SrcIP))
|
// it later, because the checksum computation runs over these
|
||||||
binary.BigEndian.PutUint32(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
|
||||||
|
|
||||||
binary.BigEndian.PutUint16(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"
|
|
||||||
// form required when calculating UDP checksums. Overwrites the first
|
|
||||||
// h.Length() bytes of buf.
|
|
||||||
func (h IP4Header) MarshalPseudo(buf []byte) error {
|
|
||||||
if len(buf) < ip4HeaderLength {
|
|
||||||
return errSmallBuffer
|
|
||||||
}
|
|
||||||
if len(buf) > maxPacketLength {
|
|
||||||
return errLargePacket
|
|
||||||
}
|
|
||||||
|
|
||||||
length := len(buf) - ip4HeaderLength
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToResponse implements Header.
|
// ToResponse implements Header.
|
||||||
func (h *IP4Header) ToResponse() {
|
func (h *IP4Header) ToResponse() {
|
||||||
h.SrcIP, h.DstIP = h.DstIP, h.SrcIP
|
h.SrcIP, h.DstIP = h.DstIP, h.SrcIP
|
||||||
// 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
|
||||||
|
}
|
||||||
|
@ -10,8 +10,10 @@ import (
|
|||||||
"inet.af/netaddr"
|
"inet.af/netaddr"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// IP6 is an IPv6 address.
|
||||||
type IP6 [16]byte
|
type IP6 [16]byte
|
||||||
|
|
||||||
|
// IP6FromNetaddr converts a netaddr.IP to an IP6. Panics if !ip.Is6.
|
||||||
func IP6FromNetaddr(ip netaddr.IP) IP6 {
|
func IP6FromNetaddr(ip netaddr.IP) IP6 {
|
||||||
if !ip.Is6() {
|
if !ip.Is6() {
|
||||||
panic(fmt.Sprintf("IP6FromNetaddr called with non-v6 addr %q", ip))
|
panic(fmt.Sprintf("IP6FromNetaddr called with non-v6 addr %q", ip))
|
||||||
@ -19,6 +21,7 @@ func IP6FromNetaddr(ip netaddr.IP) IP6 {
|
|||||||
return IP6(ip.As16())
|
return IP6(ip.As16())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Netaddr converts ip to a netaddr.IP.
|
||||||
func (ip IP6) Netaddr() netaddr.IP {
|
func (ip IP6) Netaddr() netaddr.IP {
|
||||||
return netaddr.IPFrom16(ip)
|
return netaddr.IPFrom16(ip)
|
||||||
}
|
}
|
||||||
@ -27,4 +30,5 @@ func (ip IP6) String() string {
|
|||||||
return ip.Netaddr().String()
|
return ip.Netaddr().String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ip6HeaderLength is the length of an IPv6 header with no IP options.
|
||||||
const ip6HeaderLength = 40
|
const ip6HeaderLength = 40
|
||||||
|
@ -22,8 +22,6 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// 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
|
||||||
@ -100,25 +98,6 @@ func writeIP6Port(sb *strbuilder.Builder, ip IP6, port uint16) {
|
|||||||
sb.WriteUint(uint64(port))
|
sb.WriteUint(uint64(port))
|
||||||
}
|
}
|
||||||
|
|
||||||
// based on https://tools.ietf.org/html/rfc1071
|
|
||||||
func ipChecksum(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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode extracts data from the packet in b into q.
|
// Decode extracts data from the packet in b into q.
|
||||||
// It performs extremely simple packet decoding for basic IPv4 packet types.
|
// It performs extremely simple packet decoding for basic IPv4 packet types.
|
||||||
// It extracts only the subprotocol id, IP addresses, and (if any) ports,
|
// It extracts only the subprotocol id, IP addresses, and (if any) ports,
|
||||||
|
@ -6,23 +6,23 @@ package packet
|
|||||||
|
|
||||||
import "encoding/binary"
|
import "encoding/binary"
|
||||||
|
|
||||||
// UDPHeader represents an UDP packet header.
|
// 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 is the size of the UDP packet header, not
|
func (h UDP4Header) Len() int {
|
||||||
// including the outer IP header.
|
return h.IP4Header.Len() + udpHeaderLength
|
||||||
udpHeaderLength = 8
|
|
||||||
)
|
|
||||||
|
|
||||||
func (UDP4Header) Len() int {
|
|
||||||
return ip4HeaderLength + udpHeaderLength
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Marshal implements Header.
|
||||||
func (h UDP4Header) Marshal(buf []byte) error {
|
func (h UDP4Header) Marshal(buf []byte) error {
|
||||||
if len(buf) < h.Len() {
|
if len(buf) < h.Len() {
|
||||||
return errSmallBuffer
|
return errSmallBuffer
|
||||||
@ -40,14 +40,15 @@ func (h UDP4Header) Marshal(buf []byte) error {
|
|||||||
binary.BigEndian.PutUint16(buf[26:28], 0) // blank checksum
|
binary.BigEndian.PutUint16(buf[26:28], 0) // blank checksum
|
||||||
|
|
||||||
// UDP checksum with IP pseudo header.
|
// UDP checksum with IP pseudo header.
|
||||||
h.IP4Header.MarshalPseudo(buf)
|
h.IP4Header.marshalPseudo(buf)
|
||||||
binary.BigEndian.PutUint16(buf[26:28], ipChecksum(buf[8:]))
|
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()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user