|
|
@@ -4,6 +4,12 @@
|
|
|
|
|
|
package packet
|
|
|
|
|
|
+import (
|
|
|
+ "encoding/binary"
|
|
|
+
|
|
|
+ "tailscale.com/types/ipproto"
|
|
|
+)
|
|
|
+
|
|
|
// icmp6HeaderLength is the size of the ICMPv6 packet header, not
|
|
|
// including the outer IP layer or the variable "response data"
|
|
|
// trailer.
|
|
|
@@ -42,3 +48,120 @@ type ICMP6Code uint8
|
|
|
const (
|
|
|
ICMP6NoCode ICMP6Code = 0
|
|
|
)
|
|
|
+
|
|
|
+// ICMP6Header is an IPv4+ICMPv4 header.
|
|
|
+type ICMP6Header struct {
|
|
|
+ IP6Header
|
|
|
+ Type ICMP6Type
|
|
|
+ Code ICMP6Code
|
|
|
+}
|
|
|
+
|
|
|
+// Len implements Header.
|
|
|
+func (h ICMP6Header) Len() int {
|
|
|
+ return h.IP6Header.Len() + icmp6HeaderLength
|
|
|
+}
|
|
|
+
|
|
|
+// Marshal implements Header.
|
|
|
+func (h ICMP6Header) Marshal(buf []byte) error {
|
|
|
+ if len(buf) < h.Len() {
|
|
|
+ return errSmallBuffer
|
|
|
+ }
|
|
|
+ if len(buf) > maxPacketLength {
|
|
|
+ return errLargePacket
|
|
|
+ }
|
|
|
+ // The caller does not need to set this.
|
|
|
+ h.IPProto = ipproto.ICMPv6
|
|
|
+
|
|
|
+ h.IP6Header.Marshal(buf)
|
|
|
+
|
|
|
+ const o = ip6HeaderLength // start offset of ICMPv6 header
|
|
|
+ buf[o+0] = uint8(h.Type)
|
|
|
+ buf[o+1] = uint8(h.Code)
|
|
|
+ buf[o+2] = 0 // checksum, to be filled in later
|
|
|
+ buf[o+3] = 0 // checksum, to be filled in later
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+// ToResponse implements Header. TODO: it doesn't implement it
|
|
|
+// correctly, instead it statically generates an ICMP Echo Reply
|
|
|
+// packet.
|
|
|
+func (h *ICMP6Header) ToResponse() {
|
|
|
+ // TODO: this doesn't implement ToResponse correctly, as it
|
|
|
+ // assumes the ICMP request type.
|
|
|
+ h.Type = ICMP6EchoReply
|
|
|
+ h.Code = ICMP6NoCode
|
|
|
+ h.IP6Header.ToResponse()
|
|
|
+}
|
|
|
+
|
|
|
+// WriteChecksum implements HeaderChecksummer, writing just the checksum bytes
|
|
|
+// into the otherwise fully marshaled ICMP6 packet p (which should include the
|
|
|
+// IPv6 header, ICMPv6 header, and payload).
|
|
|
+func (h ICMP6Header) WriteChecksum(p []byte) {
|
|
|
+ const payOff = ip6HeaderLength + icmp6HeaderLength
|
|
|
+ xsum := icmp6Checksum(p[ip6HeaderLength:payOff], h.Src.As16(), h.Dst.As16(), p[payOff:])
|
|
|
+ binary.BigEndian.PutUint16(p[ip6HeaderLength+2:], xsum)
|
|
|
+}
|
|
|
+
|
|
|
+// Adapted from gVisor:
|
|
|
+
|
|
|
+// icmp6Checksum calculates the ICMP checksum over the provided ICMPv6
|
|
|
+// header (without the IPv6 header), IPv6 src/dst addresses and the
|
|
|
+// payload.
|
|
|
+//
|
|
|
+// The header's existing checksum must be zeroed.
|
|
|
+func icmp6Checksum(header []byte, src, dst [16]byte, payload []byte) uint16 {
|
|
|
+ // Calculate the IPv6 pseudo-header upper-layer checksum.
|
|
|
+ xsum := checksumBytes(src[:], 0)
|
|
|
+ xsum = checksumBytes(dst[:], xsum)
|
|
|
+
|
|
|
+ var scratch [4]byte
|
|
|
+ binary.BigEndian.PutUint32(scratch[:], uint32(len(header)+len(payload)))
|
|
|
+ xsum = checksumBytes(scratch[:], xsum)
|
|
|
+ xsum = checksumBytes(append(scratch[:0], 0, 0, 0, uint8(ipproto.ICMPv6)), xsum)
|
|
|
+ xsum = checksumBytes(payload, xsum)
|
|
|
+
|
|
|
+ var hdrz [icmp6HeaderLength]byte
|
|
|
+ copy(hdrz[:], header)
|
|
|
+ // Zero out the header.
|
|
|
+ hdrz[2] = 0
|
|
|
+ hdrz[3] = 0
|
|
|
+ xsum = ^checksumBytes(hdrz[:], xsum)
|
|
|
+ return xsum
|
|
|
+}
|
|
|
+
|
|
|
+// checksumCombine combines the two uint16 to form their
|
|
|
+// checksum. This is done by adding them and the carry.
|
|
|
+//
|
|
|
+// Note that checksum a must have been computed on an even number of
|
|
|
+// bytes.
|
|
|
+func checksumCombine(a, b uint16) uint16 {
|
|
|
+ v := uint32(a) + uint32(b)
|
|
|
+ return uint16(v + v>>16)
|
|
|
+}
|
|
|
+
|
|
|
+// checksumBytes calculates the checksum (as defined in RFC 1071) of
|
|
|
+// the bytes in buf.
|
|
|
+//
|
|
|
+// The initial checksum must have been computed on an even number of bytes.
|
|
|
+func checksumBytes(buf []byte, initial uint16) uint16 {
|
|
|
+ v := uint32(initial)
|
|
|
+
|
|
|
+ odd := len(buf)%2 == 1
|
|
|
+ if odd {
|
|
|
+ v += uint32(buf[0])
|
|
|
+ buf = buf[1:]
|
|
|
+ }
|
|
|
+
|
|
|
+ n := len(buf)
|
|
|
+ odd = n&1 != 0
|
|
|
+ if odd {
|
|
|
+ n--
|
|
|
+ v += uint32(buf[n]) << 8
|
|
|
+ }
|
|
|
+
|
|
|
+ for i := 0; i < n; i += 2 {
|
|
|
+ v += (uint32(buf[i]) << 8) + uint32(buf[i+1])
|
|
|
+ }
|
|
|
+
|
|
|
+ return checksumCombine(uint16(v), uint16(v>>16))
|
|
|
+}
|