Kaynağa Gözat

net/packet: documentation pass.

Signed-off-by: David Anderson <[email protected]>
David Anderson 5 yıl önce
ebeveyn
işleme
a38e28da07
6 değiştirilmiş dosya ile 110 ekleme ve 77 silme
  1. 19 11
      net/packet/icmp4.go
  2. 9 2
      net/packet/icmp6.go
  3. 66 32
      net/packet/ip4.go
  4. 4 0
      net/packet/ip6.go
  5. 0 21
      net/packet/packet.go
  6. 12 11
      net/packet/udp4.go

+ 19 - 11
net/packet/icmp4.go

@@ -6,6 +6,13 @@ 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
 
 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
 
 const (
 	ICMP4NoCode ICMP4Code = 0
 )
 
-// ICMP4Header represents an ICMPv4 packet header.
+// ICMP4Header is an IPv4+ICMPv4 header.
 type ICMP4Header struct {
 	IP4Header
 	Type ICMP4Type
 	Code ICMP4Code
 }
 
-const (
-	icmp4HeaderLength = 4
-	// icmp4AllHeadersLength is the length of all headers in a ICMPv4 packet.
-	icmp4AllHeadersLength = ip4HeaderLength + icmp4HeaderLength
-)
-
-func (ICMP4Header) Len() int {
-	return icmp4AllHeadersLength
+// Len implements Header.
+func (h ICMP4Header) Len() int {
+	return h.IP4Header.Len() + icmp4HeaderLength
 }
 
+// Marshal implements Header.
 func (h ICMP4Header) Marshal(buf []byte) error {
-	if len(buf) < icmp4AllHeadersLength {
+	if len(buf) < h.Len() {
 		return errSmallBuffer
 	}
 	if len(buf) > maxPacketLength {
@@ -68,11 +73,14 @@ func (h ICMP4Header) Marshal(buf []byte) error {
 
 	h.IP4Header.Marshal(buf)
 
-	binary.BigEndian.PutUint16(buf[22:24], ipChecksum(buf))
+	binary.BigEndian.PutUint16(buf[22:24], ip4Checksum(buf))
 
 	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() {
 	// TODO: this doesn't implement ToResponse correctly, as it
 	// assumes the ICMP request type.

+ 9 - 2
net/packet/icmp6.go

@@ -4,6 +4,13 @@
 
 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 (
@@ -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
 
 const (
 	ICMP6NoCode ICMP6Code = 0
 )
-
-const icmp6HeaderLength = 4

+ 66 - 32
net/packet/ip4.go

@@ -14,13 +14,13 @@ import (
 // IP4 is an IPv4 address.
 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 {
 	ipbytes := ip.As4()
 	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 {
 	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))
 }
 
+// IsMulticast returns whether ip is a multicast address.
 func (ip IP4) IsMulticast() bool {
 	return byte(ip>>24)&0xf0 == 0xe0
 }
 
+// IsLinkLocalUnicast returns whether ip is a link-local unicast
+// address.
 func (ip IP4) IsLinkLocalUnicast() bool {
 	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 {
 	IPProto IPProto
 	IPID    uint16
@@ -45,48 +51,83 @@ type IP4Header struct {
 	DstIP   IP4
 }
 
-const ip4HeaderLength = 20
-
-func (IP4Header) Len() int {
+// Len implements Header.
+func (h IP4Header) Len() int {
 	return ip4HeaderLength
 }
 
+// Marshal implements Header.
 func (h IP4Header) Marshal(buf []byte) error {
-	if len(buf) < ip4HeaderLength {
+	if len(buf) < h.Len() {
 		return errSmallBuffer
 	}
 	if len(buf) > maxPacketLength {
 		return errLargePacket
 	}
 
-	buf[0] = 0x40 | (ip4HeaderLength >> 2) // IPv4
-	buf[1] = 0x00                          // DHCP, ECN
-	binary.BigEndian.PutUint16(buf[2:4], uint16(len(buf)))
-	binary.BigEndian.PutUint16(buf[4:6], h.IPID)
-	binary.BigEndian.PutUint16(buf[6:8], 0) // flags, offset
-	buf[8] = 64                             // TTL
-	buf[9] = uint8(h.IPProto)
-	binary.BigEndian.PutUint16(buf[10:12], 0) // blank IP header checksum
-	binary.BigEndian.PutUint32(buf[12:16], uint32(h.SrcIP))
-	binary.BigEndian.PutUint32(buf[16:20], uint32(h.DstIP))
-
-	binary.BigEndian.PutUint16(buf[10:12], ipChecksum(buf[0:20]))
+	buf[0] = 0x40 | (byte(h.Len() >> 2))                   // IPv4 + IHL
+	buf[1] = 0x00                                          // DSCP + ECN
+	binary.BigEndian.PutUint16(buf[2:4], uint16(len(buf))) // Total length
+	binary.BigEndian.PutUint16(buf[4:6], h.IPID)           // ID
+	binary.BigEndian.PutUint16(buf[6:8], 0)                // Flags + fragment offset
+	buf[8] = 64                                            // TTL
+	buf[9] = uint8(h.IPProto)                              // Inner protocol
+	// Blank checksum. This is necessary even though we overwrite
+	// it later, because the checksum computation runs over these
+	// 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], 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) < ip4HeaderLength {
+// 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
+}
+
+// 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) - ip4HeaderLength
+	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
@@ -94,10 +135,3 @@ func (h IP4Header) MarshalPseudo(buf []byte) error {
 	binary.BigEndian.PutUint16(buf[18:20], uint16(length))
 	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
-}

+ 4 - 0
net/packet/ip6.go

@@ -10,8 +10,10 @@ import (
 	"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))
@@ -19,6 +21,7 @@ func IP6FromNetaddr(ip netaddr.IP) IP6 {
 	return IP6(ip.As16())
 }
 
+// Netaddr converts ip to a netaddr.IP.
 func (ip IP6) Netaddr() netaddr.IP {
 	return netaddr.IPFrom16(ip)
 }
@@ -27,4 +30,5 @@ func (ip IP6) String() string {
 	return ip.Netaddr().String()
 }
 
+// ip6HeaderLength is the length of an IPv6 header with no IP options.
 const ip6HeaderLength = 40

+ 0 - 21
net/packet/packet.go

@@ -22,8 +22,6 @@ const (
 )
 
 // 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 {
 	// b is the byte buffer that this decodes.
 	b []byte
@@ -100,25 +98,6 @@ func writeIP6Port(sb *strbuilder.Builder, ip IP6, port uint16) {
 	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.
 // It performs extremely simple packet decoding for basic IPv4 packet types.
 // It extracts only the subprotocol id, IP addresses, and (if any) ports,

+ 12 - 11
net/packet/udp4.go

@@ -6,23 +6,23 @@ package packet
 
 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 {
 	IP4Header
 	SrcPort uint16
 	DstPort uint16
 }
 
-const (
-	// udpHeaderLength is the size of the UDP packet header, not
-	// including the outer IP header.
-	udpHeaderLength = 8
-)
-
-func (UDP4Header) Len() int {
-	return ip4HeaderLength + udpHeaderLength
+// Len implements Header.
+func (h UDP4Header) Len() int {
+	return h.IP4Header.Len() + udpHeaderLength
 }
 
+// Marshal implements Header.
 func (h UDP4Header) Marshal(buf []byte) error {
 	if len(buf) < h.Len() {
 		return errSmallBuffer
@@ -40,14 +40,15 @@ func (h UDP4Header) Marshal(buf []byte) error {
 	binary.BigEndian.PutUint16(buf[26:28], 0) // blank checksum
 
 	// UDP checksum with IP pseudo header.
-	h.IP4Header.MarshalPseudo(buf)
-	binary.BigEndian.PutUint16(buf[26:28], ipChecksum(buf[8:]))
+	h.IP4Header.marshalPseudo(buf)
+	binary.BigEndian.PutUint16(buf[26:28], ip4Checksum(buf[ip4PseudoHeaderOffset:]))
 
 	h.IP4Header.Marshal(buf)
 
 	return nil
 }
 
+// ToResponse implements Header.
 func (h *UDP4Header) ToResponse() {
 	h.SrcPort, h.DstPort = h.DstPort, h.SrcPort
 	h.IP4Header.ToResponse()