Migrate clashtcpip to gVisor tcpip copied
This commit is contained in:
parent
7f8e556bb0
commit
1793988a6d
43 changed files with 7370 additions and 1340 deletions
107
internal/gtcpip/header/checksum.go
Normal file
107
internal/gtcpip/header/checksum.go
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
// Copyright 2018 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package header provides the implementation of the encoding and decoding of
|
||||
// network protocol headers.
|
||||
package header
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/sagernet/sing-tun/internal/gtcpip"
|
||||
"github.com/sagernet/sing-tun/internal/gtcpip/checksum"
|
||||
)
|
||||
|
||||
// PseudoHeaderChecksum calculates the pseudo-header checksum for the given
|
||||
// destination protocol and network address. Pseudo-headers are needed by
|
||||
// transport layers when calculating their own checksum.
|
||||
func PseudoHeaderChecksum(protocol tcpip.TransportProtocolNumber, srcAddr []byte, dstAddr []byte, totalLen uint16) uint16 {
|
||||
xsum := checksum.Checksum(srcAddr, 0)
|
||||
xsum = checksum.Checksum(dstAddr, xsum)
|
||||
|
||||
// Add the length portion of the checksum to the pseudo-checksum.
|
||||
var tmp [2]byte
|
||||
binary.BigEndian.PutUint16(tmp[:], totalLen)
|
||||
xsum = checksum.Checksum(tmp[:], xsum)
|
||||
|
||||
return checksum.Checksum([]byte{0, uint8(protocol)}, xsum)
|
||||
}
|
||||
|
||||
// checksumUpdate2ByteAlignedUint16 updates a uint16 value in a calculated
|
||||
// checksum.
|
||||
//
|
||||
// The value MUST begin at a 2-byte boundary in the original buffer.
|
||||
func checksumUpdate2ByteAlignedUint16(xsum, old, new uint16) uint16 {
|
||||
// As per RFC 1071 page 4,
|
||||
// (4) Incremental Update
|
||||
//
|
||||
// ...
|
||||
//
|
||||
// To update the checksum, simply add the differences of the
|
||||
// sixteen bit integers that have been changed. To see why this
|
||||
// works, observe that every 16-bit integer has an additive inverse
|
||||
// and that addition is associative. From this it follows that
|
||||
// given the original value m, the new value m', and the old
|
||||
// checksum C, the new checksum C' is:
|
||||
//
|
||||
// C' = C + (-m) + m' = C + (m' - m)
|
||||
if old == new {
|
||||
return xsum
|
||||
}
|
||||
return checksum.Combine(xsum, checksum.Combine(new, ^old))
|
||||
}
|
||||
|
||||
// checksumUpdate2ByteAlignedAddress updates an address in a calculated
|
||||
// checksum.
|
||||
//
|
||||
// The addresses must have the same length and must contain an even number
|
||||
// of bytes. The address MUST begin at a 2-byte boundary in the original buffer.
|
||||
func checksumUpdate2ByteAlignedAddress(xsum uint16, old, new tcpip.Address) uint16 {
|
||||
const uint16Bytes = 2
|
||||
|
||||
if old.BitLen() != new.BitLen() {
|
||||
panic(fmt.Sprintf("buffer lengths are different; old = %d, new = %d", old.BitLen()/8, new.BitLen()/8))
|
||||
}
|
||||
|
||||
if oldBytes := old.BitLen() % 16; oldBytes != 0 {
|
||||
panic(fmt.Sprintf("buffer has an odd number of bytes; got = %d", oldBytes))
|
||||
}
|
||||
|
||||
oldAddr := old.AsSlice()
|
||||
newAddr := new.AsSlice()
|
||||
|
||||
// As per RFC 1071 page 4,
|
||||
// (4) Incremental Update
|
||||
//
|
||||
// ...
|
||||
//
|
||||
// To update the checksum, simply add the differences of the
|
||||
// sixteen bit integers that have been changed. To see why this
|
||||
// works, observe that every 16-bit integer has an additive inverse
|
||||
// and that addition is associative. From this it follows that
|
||||
// given the original value m, the new value m', and the old
|
||||
// checksum C, the new checksum C' is:
|
||||
//
|
||||
// C' = C + (-m) + m' = C + (m' - m)
|
||||
for len(oldAddr) != 0 {
|
||||
// Convert the 2 byte sequences to uint16 values then apply the increment
|
||||
// update.
|
||||
xsum = checksumUpdate2ByteAlignedUint16(xsum, (uint16(oldAddr[0])<<8)+uint16(oldAddr[1]), (uint16(newAddr[0])<<8)+uint16(newAddr[1]))
|
||||
oldAddr = oldAddr[uint16Bytes:]
|
||||
newAddr = newAddr[uint16Bytes:]
|
||||
}
|
||||
|
||||
return xsum
|
||||
}
|
||||
192
internal/gtcpip/header/eth.go
Normal file
192
internal/gtcpip/header/eth.go
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
// Copyright 2018 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package header
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/sagernet/sing-tun/internal/gtcpip"
|
||||
)
|
||||
|
||||
const (
|
||||
dstMAC = 0
|
||||
srcMAC = 6
|
||||
ethType = 12
|
||||
)
|
||||
|
||||
// EthernetFields contains the fields of an ethernet frame header. It is used to
|
||||
// describe the fields of a frame that needs to be encoded.
|
||||
type EthernetFields struct {
|
||||
// SrcAddr is the "MAC source" field of an ethernet frame header.
|
||||
SrcAddr tcpip.LinkAddress
|
||||
|
||||
// DstAddr is the "MAC destination" field of an ethernet frame header.
|
||||
DstAddr tcpip.LinkAddress
|
||||
|
||||
// Type is the "ethertype" field of an ethernet frame header.
|
||||
Type tcpip.NetworkProtocolNumber
|
||||
}
|
||||
|
||||
// Ethernet represents an ethernet frame header stored in a byte array.
|
||||
type Ethernet []byte
|
||||
|
||||
const (
|
||||
// EthernetMinimumSize is the minimum size of a valid ethernet frame.
|
||||
EthernetMinimumSize = 14
|
||||
|
||||
// EthernetMaximumSize is the maximum size of a valid ethernet frame.
|
||||
EthernetMaximumSize = 18
|
||||
|
||||
// EthernetAddressSize is the size, in bytes, of an ethernet address.
|
||||
EthernetAddressSize = 6
|
||||
|
||||
// UnspecifiedEthernetAddress is the unspecified ethernet address
|
||||
// (all bits set to 0).
|
||||
UnspecifiedEthernetAddress = tcpip.LinkAddress("\x00\x00\x00\x00\x00\x00")
|
||||
|
||||
// EthernetBroadcastAddress is an ethernet address that addresses every node
|
||||
// on a local link.
|
||||
EthernetBroadcastAddress = tcpip.LinkAddress("\xff\xff\xff\xff\xff\xff")
|
||||
|
||||
// unicastMulticastFlagMask is the mask of the least significant bit in
|
||||
// the first octet (in network byte order) of an ethernet address that
|
||||
// determines whether the ethernet address is a unicast or multicast. If
|
||||
// the masked bit is a 1, then the address is a multicast, unicast
|
||||
// otherwise.
|
||||
//
|
||||
// See the IEEE Std 802-2001 document for more details. Specifically,
|
||||
// section 9.2.1 of http://ieee802.org/secmail/pdfocSP2xXA6d.pdf:
|
||||
// "A 48-bit universal address consists of two parts. The first 24 bits
|
||||
// correspond to the OUI as assigned by the IEEE, expect that the
|
||||
// assignee may set the LSB of the first octet to 1 for group addresses
|
||||
// or set it to 0 for individual addresses."
|
||||
unicastMulticastFlagMask = 1
|
||||
|
||||
// unicastMulticastFlagByteIdx is the byte that holds the
|
||||
// unicast/multicast flag. See unicastMulticastFlagMask.
|
||||
unicastMulticastFlagByteIdx = 0
|
||||
)
|
||||
|
||||
const (
|
||||
// EthernetProtocolAll is a catch-all for all protocols carried inside
|
||||
// an ethernet frame. It is mainly used to create packet sockets that
|
||||
// capture all traffic.
|
||||
EthernetProtocolAll tcpip.NetworkProtocolNumber = 0x0003
|
||||
|
||||
// EthernetProtocolPUP is the PARC Universal Packet protocol ethertype.
|
||||
EthernetProtocolPUP tcpip.NetworkProtocolNumber = 0x0200
|
||||
)
|
||||
|
||||
// Ethertypes holds the protocol numbers describing the payload of an ethernet
|
||||
// frame. These types aren't necessarily supported by netstack, but can be used
|
||||
// to catch all traffic of a type via packet endpoints.
|
||||
var Ethertypes = []tcpip.NetworkProtocolNumber{
|
||||
EthernetProtocolAll,
|
||||
EthernetProtocolPUP,
|
||||
}
|
||||
|
||||
// SourceAddress returns the "MAC source" field of the ethernet frame header.
|
||||
func (b Ethernet) SourceAddress() tcpip.LinkAddress {
|
||||
return tcpip.LinkAddress(b[srcMAC:][:EthernetAddressSize])
|
||||
}
|
||||
|
||||
// DestinationAddress returns the "MAC destination" field of the ethernet frame
|
||||
// header.
|
||||
func (b Ethernet) DestinationAddress() tcpip.LinkAddress {
|
||||
return tcpip.LinkAddress(b[dstMAC:][:EthernetAddressSize])
|
||||
}
|
||||
|
||||
// Type returns the "ethertype" field of the ethernet frame header.
|
||||
func (b Ethernet) Type() tcpip.NetworkProtocolNumber {
|
||||
return tcpip.NetworkProtocolNumber(binary.BigEndian.Uint16(b[ethType:]))
|
||||
}
|
||||
|
||||
// Encode encodes all the fields of the ethernet frame header.
|
||||
func (b Ethernet) Encode(e *EthernetFields) {
|
||||
binary.BigEndian.PutUint16(b[ethType:], uint16(e.Type))
|
||||
copy(b[srcMAC:][:EthernetAddressSize], e.SrcAddr)
|
||||
copy(b[dstMAC:][:EthernetAddressSize], e.DstAddr)
|
||||
}
|
||||
|
||||
// IsMulticastEthernetAddress returns true if the address is a multicast
|
||||
// ethernet address.
|
||||
func IsMulticastEthernetAddress(addr tcpip.LinkAddress) bool {
|
||||
if len(addr) != EthernetAddressSize {
|
||||
return false
|
||||
}
|
||||
|
||||
return addr[unicastMulticastFlagByteIdx]&unicastMulticastFlagMask != 0
|
||||
}
|
||||
|
||||
// IsValidUnicastEthernetAddress returns true if the address is a unicast
|
||||
// ethernet address.
|
||||
func IsValidUnicastEthernetAddress(addr tcpip.LinkAddress) bool {
|
||||
if len(addr) != EthernetAddressSize {
|
||||
return false
|
||||
}
|
||||
|
||||
if addr == UnspecifiedEthernetAddress {
|
||||
return false
|
||||
}
|
||||
|
||||
if addr[unicastMulticastFlagByteIdx]&unicastMulticastFlagMask != 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// EthernetAddressFromMulticastIPv4Address returns a multicast Ethernet address
|
||||
// for a multicast IPv4 address.
|
||||
//
|
||||
// addr MUST be a multicast IPv4 address.
|
||||
func EthernetAddressFromMulticastIPv4Address(addr tcpip.Address) tcpip.LinkAddress {
|
||||
var linkAddrBytes [EthernetAddressSize]byte
|
||||
// RFC 1112 Host Extensions for IP Multicasting
|
||||
//
|
||||
// 6.4. Extensions to an Ethernet Local Network Module:
|
||||
//
|
||||
// An IP host group address is mapped to an Ethernet multicast
|
||||
// address by placing the low-order 23-bits of the IP address
|
||||
// into the low-order 23 bits of the Ethernet multicast address
|
||||
// 01-00-5E-00-00-00 (hex).
|
||||
addrBytes := addr.As4()
|
||||
linkAddrBytes[0] = 0x1
|
||||
linkAddrBytes[2] = 0x5e
|
||||
linkAddrBytes[3] = addrBytes[1] & 0x7F
|
||||
copy(linkAddrBytes[4:], addrBytes[IPv4AddressSize-2:])
|
||||
return tcpip.LinkAddress(linkAddrBytes[:])
|
||||
}
|
||||
|
||||
// EthernetAddressFromMulticastIPv6Address returns a multicast Ethernet address
|
||||
// for a multicast IPv6 address.
|
||||
//
|
||||
// addr MUST be a multicast IPv6 address.
|
||||
func EthernetAddressFromMulticastIPv6Address(addr tcpip.Address) tcpip.LinkAddress {
|
||||
// RFC 2464 Transmission of IPv6 Packets over Ethernet Networks
|
||||
//
|
||||
// 7. Address Mapping -- Multicast
|
||||
//
|
||||
// An IPv6 packet with a multicast destination address DST,
|
||||
// consisting of the sixteen octets DST[1] through DST[16], is
|
||||
// transmitted to the Ethernet multicast address whose first
|
||||
// two octets are the value 3333 hexadecimal and whose last
|
||||
// four octets are the last four octets of DST.
|
||||
addrBytes := addr.As16()
|
||||
linkAddrBytes := []byte(addrBytes[IPv6AddressSize-EthernetAddressSize:])
|
||||
linkAddrBytes[0] = 0x33
|
||||
linkAddrBytes[1] = 0x33
|
||||
return tcpip.LinkAddress(linkAddrBytes[:])
|
||||
}
|
||||
228
internal/gtcpip/header/icmpv4.go
Normal file
228
internal/gtcpip/header/icmpv4.go
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
// Copyright 2018 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package header
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/sagernet/sing-tun/internal/gtcpip"
|
||||
"github.com/sagernet/sing-tun/internal/gtcpip/checksum"
|
||||
)
|
||||
|
||||
// ICMPv4 represents an ICMPv4 header stored in a byte array.
|
||||
type ICMPv4 []byte
|
||||
|
||||
const (
|
||||
// ICMPv4PayloadOffset defines the start of ICMP payload.
|
||||
ICMPv4PayloadOffset = 8
|
||||
|
||||
// ICMPv4MinimumSize is the minimum size of a valid ICMP packet.
|
||||
ICMPv4MinimumSize = 8
|
||||
|
||||
// ICMPv4MinimumErrorPayloadSize Is the smallest number of bytes of an
|
||||
// errant packet's transport layer that an ICMP error type packet should
|
||||
// attempt to send as per RFC 792 (see each type) and RFC 1122
|
||||
// section 3.2.2 which states:
|
||||
// Every ICMP error message includes the Internet header and at
|
||||
// least the first 8 data octets of the datagram that triggered
|
||||
// the error; more than 8 octets MAY be sent; this header and data
|
||||
// MUST be unchanged from the received datagram.
|
||||
//
|
||||
// RFC 792 shows:
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Type | Code | Checksum |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | unused |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Internet Header + 64 bits of Original Data Datagram |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
ICMPv4MinimumErrorPayloadSize = 8
|
||||
|
||||
// ICMPv4ProtocolNumber is the ICMP transport protocol number.
|
||||
ICMPv4ProtocolNumber tcpip.TransportProtocolNumber = 1
|
||||
|
||||
// icmpv4ChecksumOffset is the offset of the checksum field
|
||||
// in an ICMPv4 message.
|
||||
icmpv4ChecksumOffset = 2
|
||||
|
||||
// icmpv4MTUOffset is the offset of the MTU field
|
||||
// in an ICMPv4FragmentationNeeded message.
|
||||
icmpv4MTUOffset = 6
|
||||
|
||||
// icmpv4IdentOffset is the offset of the ident field
|
||||
// in an ICMPv4EchoRequest/Reply message.
|
||||
icmpv4IdentOffset = 4
|
||||
|
||||
// icmpv4PointerOffset is the offset of the pointer field
|
||||
// in an ICMPv4ParamProblem message.
|
||||
icmpv4PointerOffset = 4
|
||||
|
||||
// icmpv4SequenceOffset is the offset of the sequence field
|
||||
// in an ICMPv4EchoRequest/Reply message.
|
||||
icmpv4SequenceOffset = 6
|
||||
)
|
||||
|
||||
// ICMPv4Type is the ICMP type field described in RFC 792.
|
||||
type ICMPv4Type byte
|
||||
|
||||
// ICMPv4Code is the ICMP code field described in RFC 792.
|
||||
type ICMPv4Code byte
|
||||
|
||||
// Typical values of ICMPv4Type defined in RFC 792.
|
||||
const (
|
||||
ICMPv4EchoReply ICMPv4Type = 0
|
||||
ICMPv4DstUnreachable ICMPv4Type = 3
|
||||
ICMPv4SrcQuench ICMPv4Type = 4
|
||||
ICMPv4Redirect ICMPv4Type = 5
|
||||
ICMPv4Echo ICMPv4Type = 8
|
||||
ICMPv4TimeExceeded ICMPv4Type = 11
|
||||
ICMPv4ParamProblem ICMPv4Type = 12
|
||||
ICMPv4Timestamp ICMPv4Type = 13
|
||||
ICMPv4TimestampReply ICMPv4Type = 14
|
||||
ICMPv4InfoRequest ICMPv4Type = 15
|
||||
ICMPv4InfoReply ICMPv4Type = 16
|
||||
)
|
||||
|
||||
// ICMP codes for ICMPv4 Time Exceeded messages as defined in RFC 792.
|
||||
const (
|
||||
ICMPv4TTLExceeded ICMPv4Code = 0
|
||||
ICMPv4ReassemblyTimeout ICMPv4Code = 1
|
||||
)
|
||||
|
||||
// ICMP codes for ICMPv4 Destination Unreachable messages as defined in RFC 792,
|
||||
// RFC 1122 section 3.2.2.1 and RFC 1812 section 5.2.7.1.
|
||||
const (
|
||||
ICMPv4NetUnreachable ICMPv4Code = 0
|
||||
ICMPv4HostUnreachable ICMPv4Code = 1
|
||||
ICMPv4ProtoUnreachable ICMPv4Code = 2
|
||||
ICMPv4PortUnreachable ICMPv4Code = 3
|
||||
ICMPv4FragmentationNeeded ICMPv4Code = 4
|
||||
ICMPv4SourceRouteFailed ICMPv4Code = 5
|
||||
ICMPv4DestinationNetworkUnknown ICMPv4Code = 6
|
||||
ICMPv4DestinationHostUnknown ICMPv4Code = 7
|
||||
ICMPv4SourceHostIsolated ICMPv4Code = 8
|
||||
ICMPv4NetProhibited ICMPv4Code = 9
|
||||
ICMPv4HostProhibited ICMPv4Code = 10
|
||||
ICMPv4NetUnreachableForTos ICMPv4Code = 11
|
||||
ICMPv4HostUnreachableForTos ICMPv4Code = 12
|
||||
ICMPv4AdminProhibited ICMPv4Code = 13
|
||||
ICMPv4HostPrecedenceViolation ICMPv4Code = 14
|
||||
ICMPv4PrecedenceCutInEffect ICMPv4Code = 15
|
||||
)
|
||||
|
||||
// ICMPv4UnusedCode is a code to use in ICMP messages where no code is needed.
|
||||
const ICMPv4UnusedCode ICMPv4Code = 0
|
||||
|
||||
// Type is the ICMP type field.
|
||||
func (b ICMPv4) Type() ICMPv4Type { return ICMPv4Type(b[0]) }
|
||||
|
||||
// SetType sets the ICMP type field.
|
||||
func (b ICMPv4) SetType(t ICMPv4Type) { b[0] = byte(t) }
|
||||
|
||||
// Code is the ICMP code field. Its meaning depends on the value of Type.
|
||||
func (b ICMPv4) Code() ICMPv4Code { return ICMPv4Code(b[1]) }
|
||||
|
||||
// SetCode sets the ICMP code field.
|
||||
func (b ICMPv4) SetCode(c ICMPv4Code) { b[1] = byte(c) }
|
||||
|
||||
// Pointer returns the pointer field in a Parameter Problem packet.
|
||||
func (b ICMPv4) Pointer() byte { return b[icmpv4PointerOffset] }
|
||||
|
||||
// SetPointer sets the pointer field in a Parameter Problem packet.
|
||||
func (b ICMPv4) SetPointer(c byte) { b[icmpv4PointerOffset] = c }
|
||||
|
||||
// Checksum is the ICMP checksum field.
|
||||
func (b ICMPv4) Checksum() uint16 {
|
||||
return binary.BigEndian.Uint16(b[icmpv4ChecksumOffset:])
|
||||
}
|
||||
|
||||
// SetChecksum sets the ICMP checksum field.
|
||||
func (b ICMPv4) SetChecksum(cs uint16) {
|
||||
checksum.Put(b[icmpv4ChecksumOffset:], cs)
|
||||
}
|
||||
|
||||
// SourcePort implements Transport.SourcePort.
|
||||
func (ICMPv4) SourcePort() uint16 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// DestinationPort implements Transport.DestinationPort.
|
||||
func (ICMPv4) DestinationPort() uint16 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// SetSourcePort implements Transport.SetSourcePort.
|
||||
func (ICMPv4) SetSourcePort(uint16) {
|
||||
}
|
||||
|
||||
// SetDestinationPort implements Transport.SetDestinationPort.
|
||||
func (ICMPv4) SetDestinationPort(uint16) {
|
||||
}
|
||||
|
||||
// Payload implements Transport.Payload.
|
||||
func (b ICMPv4) Payload() []byte {
|
||||
return b[ICMPv4PayloadOffset:]
|
||||
}
|
||||
|
||||
// MTU retrieves the MTU field from an ICMPv4 message.
|
||||
func (b ICMPv4) MTU() uint16 {
|
||||
return binary.BigEndian.Uint16(b[icmpv4MTUOffset:])
|
||||
}
|
||||
|
||||
// SetMTU sets the MTU field from an ICMPv4 message.
|
||||
func (b ICMPv4) SetMTU(mtu uint16) {
|
||||
binary.BigEndian.PutUint16(b[icmpv4MTUOffset:], mtu)
|
||||
}
|
||||
|
||||
// Ident retrieves the Ident field from an ICMPv4 message.
|
||||
func (b ICMPv4) Ident() uint16 {
|
||||
return binary.BigEndian.Uint16(b[icmpv4IdentOffset:])
|
||||
}
|
||||
|
||||
// SetIdent sets the Ident field from an ICMPv4 message.
|
||||
func (b ICMPv4) SetIdent(ident uint16) {
|
||||
binary.BigEndian.PutUint16(b[icmpv4IdentOffset:], ident)
|
||||
}
|
||||
|
||||
// SetIdentWithChecksumUpdate sets the Ident field and updates the checksum.
|
||||
func (b ICMPv4) SetIdentWithChecksumUpdate(new uint16) {
|
||||
old := b.Ident()
|
||||
b.SetIdent(new)
|
||||
b.SetChecksum(^checksumUpdate2ByteAlignedUint16(^b.Checksum(), old, new))
|
||||
}
|
||||
|
||||
// Sequence retrieves the Sequence field from an ICMPv4 message.
|
||||
func (b ICMPv4) Sequence() uint16 {
|
||||
return binary.BigEndian.Uint16(b[icmpv4SequenceOffset:])
|
||||
}
|
||||
|
||||
// SetSequence sets the Sequence field from an ICMPv4 message.
|
||||
func (b ICMPv4) SetSequence(sequence uint16) {
|
||||
binary.BigEndian.PutUint16(b[icmpv4SequenceOffset:], sequence)
|
||||
}
|
||||
|
||||
// ICMPv4Checksum calculates the ICMP checksum over the provided ICMP header,
|
||||
// and payload.
|
||||
func ICMPv4Checksum(h ICMPv4, payloadCsum uint16) uint16 {
|
||||
xsum := payloadCsum
|
||||
|
||||
// h[2:4] is the checksum itself, skip it to avoid checksumming the checksum.
|
||||
xsum = checksum.Checksum(h[:2], xsum)
|
||||
xsum = checksum.Checksum(h[4:], xsum)
|
||||
|
||||
return ^xsum
|
||||
}
|
||||
304
internal/gtcpip/header/icmpv6.go
Normal file
304
internal/gtcpip/header/icmpv6.go
Normal file
|
|
@ -0,0 +1,304 @@
|
|||
// Copyright 2018 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package header
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/sagernet/sing-tun/internal/gtcpip"
|
||||
"github.com/sagernet/sing-tun/internal/gtcpip/checksum"
|
||||
)
|
||||
|
||||
// ICMPv6 represents an ICMPv6 header stored in a byte array.
|
||||
type ICMPv6 []byte
|
||||
|
||||
const (
|
||||
// ICMPv6HeaderSize is the size of the ICMPv6 header. That is, the
|
||||
// sum of the size of the ICMPv6 Type, Code and Checksum fields, as
|
||||
// per RFC 4443 section 2.1. After the ICMPv6 header, the ICMPv6
|
||||
// message body begins.
|
||||
ICMPv6HeaderSize = 4
|
||||
|
||||
// ICMPv6MinimumSize is the minimum size of a valid ICMP packet.
|
||||
ICMPv6MinimumSize = 8
|
||||
|
||||
// ICMPv6PayloadOffset is the offset of the payload in an
|
||||
// ICMP packet.
|
||||
ICMPv6PayloadOffset = 8
|
||||
|
||||
// ICMPv6ProtocolNumber is the ICMP transport protocol number.
|
||||
ICMPv6ProtocolNumber tcpip.TransportProtocolNumber = 58
|
||||
|
||||
// ICMPv6NeighborSolicitMinimumSize is the minimum size of a
|
||||
// neighbor solicitation packet.
|
||||
ICMPv6NeighborSolicitMinimumSize = ICMPv6HeaderSize + NDPNSMinimumSize
|
||||
|
||||
// ICMPv6NeighborAdvertMinimumSize is the minimum size of a
|
||||
// neighbor advertisement packet.
|
||||
ICMPv6NeighborAdvertMinimumSize = ICMPv6HeaderSize + NDPNAMinimumSize
|
||||
|
||||
// ICMPv6EchoMinimumSize is the minimum size of a valid echo packet.
|
||||
ICMPv6EchoMinimumSize = 8
|
||||
|
||||
// ICMPv6ErrorHeaderSize is the size of an ICMP error packet header,
|
||||
// as per RFC 4443, Appendix A, item 4 and the errata.
|
||||
// ... all ICMP error messages shall have exactly
|
||||
// 32 bits of type-specific data, so that receivers can reliably find
|
||||
// the embedded invoking packet even when they don't recognize the
|
||||
// ICMP message Type.
|
||||
ICMPv6ErrorHeaderSize = 8
|
||||
|
||||
// ICMPv6DstUnreachableMinimumSize is the minimum size of a valid ICMP
|
||||
// destination unreachable packet.
|
||||
ICMPv6DstUnreachableMinimumSize = ICMPv6MinimumSize
|
||||
|
||||
// ICMPv6PacketTooBigMinimumSize is the minimum size of a valid ICMP
|
||||
// packet-too-big packet.
|
||||
ICMPv6PacketTooBigMinimumSize = ICMPv6MinimumSize
|
||||
|
||||
// ICMPv6ChecksumOffset is the offset of the checksum field
|
||||
// in an ICMPv6 message.
|
||||
ICMPv6ChecksumOffset = 2
|
||||
|
||||
// icmpv6PointerOffset is the offset of the pointer
|
||||
// in an ICMPv6 Parameter problem message.
|
||||
icmpv6PointerOffset = 4
|
||||
|
||||
// icmpv6MTUOffset is the offset of the MTU field in an ICMPv6
|
||||
// PacketTooBig message.
|
||||
icmpv6MTUOffset = 4
|
||||
|
||||
// icmpv6IdentOffset is the offset of the ident field
|
||||
// in a ICMPv6 Echo Request/Reply message.
|
||||
icmpv6IdentOffset = 4
|
||||
|
||||
// icmpv6SequenceOffset is the offset of the sequence field
|
||||
// in a ICMPv6 Echo Request/Reply message.
|
||||
icmpv6SequenceOffset = 6
|
||||
|
||||
// NDPHopLimit is the expected IP hop limit value of 255 for received
|
||||
// NDP packets, as per RFC 4861 sections 4.1 - 4.5, 6.1.1, 6.1.2, 7.1.1,
|
||||
// 7.1.2 and 8.1. If the hop limit value is not 255, nodes MUST silently
|
||||
// drop the NDP packet. All outgoing NDP packets must use this value for
|
||||
// its IP hop limit field.
|
||||
NDPHopLimit = 255
|
||||
)
|
||||
|
||||
// ICMPv6Type is the ICMP type field described in RFC 4443.
|
||||
type ICMPv6Type byte
|
||||
|
||||
// Values for use in the Type field of ICMPv6 packet from RFC 4433.
|
||||
const (
|
||||
ICMPv6DstUnreachable ICMPv6Type = 1
|
||||
ICMPv6PacketTooBig ICMPv6Type = 2
|
||||
ICMPv6TimeExceeded ICMPv6Type = 3
|
||||
ICMPv6ParamProblem ICMPv6Type = 4
|
||||
ICMPv6EchoRequest ICMPv6Type = 128
|
||||
ICMPv6EchoReply ICMPv6Type = 129
|
||||
|
||||
// Neighbor Discovery Protocol (NDP) messages, see RFC 4861.
|
||||
|
||||
ICMPv6RouterSolicit ICMPv6Type = 133
|
||||
ICMPv6RouterAdvert ICMPv6Type = 134
|
||||
ICMPv6NeighborSolicit ICMPv6Type = 135
|
||||
ICMPv6NeighborAdvert ICMPv6Type = 136
|
||||
ICMPv6RedirectMsg ICMPv6Type = 137
|
||||
|
||||
// Multicast Listener Discovery (MLD) messages, see RFC 2710.
|
||||
|
||||
ICMPv6MulticastListenerQuery ICMPv6Type = 130
|
||||
ICMPv6MulticastListenerReport ICMPv6Type = 131
|
||||
ICMPv6MulticastListenerDone ICMPv6Type = 132
|
||||
|
||||
// Multicast Listener Discovert Version 2 (MLDv2) messages, see RFC 3810.
|
||||
|
||||
ICMPv6MulticastListenerV2Report ICMPv6Type = 143
|
||||
)
|
||||
|
||||
// IsErrorType returns true if the receiver is an ICMP error type.
|
||||
func (typ ICMPv6Type) IsErrorType() bool {
|
||||
// Per RFC 4443 section 2.1:
|
||||
// ICMPv6 messages are grouped into two classes: error messages and
|
||||
// informational messages. Error messages are identified as such by a
|
||||
// zero in the high-order bit of their message Type field values. Thus,
|
||||
// error messages have message types from 0 to 127; informational
|
||||
// messages have message types from 128 to 255.
|
||||
return typ&0x80 == 0
|
||||
}
|
||||
|
||||
// ICMPv6Code is the ICMP Code field described in RFC 4443.
|
||||
type ICMPv6Code byte
|
||||
|
||||
// ICMP codes used with Destination Unreachable (Type 1). As per RFC 4443
|
||||
// section 3.1.
|
||||
const (
|
||||
ICMPv6NetworkUnreachable ICMPv6Code = 0
|
||||
ICMPv6Prohibited ICMPv6Code = 1
|
||||
ICMPv6BeyondScope ICMPv6Code = 2
|
||||
ICMPv6AddressUnreachable ICMPv6Code = 3
|
||||
ICMPv6PortUnreachable ICMPv6Code = 4
|
||||
ICMPv6Policy ICMPv6Code = 5
|
||||
ICMPv6RejectRoute ICMPv6Code = 6
|
||||
)
|
||||
|
||||
// ICMP codes used with Time Exceeded (Type 3). As per RFC 4443 section 3.3.
|
||||
const (
|
||||
ICMPv6HopLimitExceeded ICMPv6Code = 0
|
||||
ICMPv6ReassemblyTimeout ICMPv6Code = 1
|
||||
)
|
||||
|
||||
// ICMP codes used with Parameter Problem (Type 4). As per RFC 4443 section 3.4.
|
||||
const (
|
||||
// ICMPv6ErroneousHeader indicates an erroneous header field was encountered.
|
||||
ICMPv6ErroneousHeader ICMPv6Code = 0
|
||||
|
||||
// ICMPv6UnknownHeader indicates an unrecognized Next Header type encountered.
|
||||
ICMPv6UnknownHeader ICMPv6Code = 1
|
||||
|
||||
// ICMPv6UnknownOption indicates an unrecognized IPv6 option was encountered.
|
||||
ICMPv6UnknownOption ICMPv6Code = 2
|
||||
)
|
||||
|
||||
// ICMPv6UnusedCode is the code value used with ICMPv6 messages which don't use
|
||||
// the code field. (Types not mentioned above.)
|
||||
const ICMPv6UnusedCode ICMPv6Code = 0
|
||||
|
||||
// Type is the ICMP type field.
|
||||
func (b ICMPv6) Type() ICMPv6Type { return ICMPv6Type(b[0]) }
|
||||
|
||||
// SetType sets the ICMP type field.
|
||||
func (b ICMPv6) SetType(t ICMPv6Type) { b[0] = byte(t) }
|
||||
|
||||
// Code is the ICMP code field. Its meaning depends on the value of Type.
|
||||
func (b ICMPv6) Code() ICMPv6Code { return ICMPv6Code(b[1]) }
|
||||
|
||||
// SetCode sets the ICMP code field.
|
||||
func (b ICMPv6) SetCode(c ICMPv6Code) { b[1] = byte(c) }
|
||||
|
||||
// TypeSpecific returns the type specific data field.
|
||||
func (b ICMPv6) TypeSpecific() uint32 {
|
||||
return binary.BigEndian.Uint32(b[icmpv6PointerOffset:])
|
||||
}
|
||||
|
||||
// SetTypeSpecific sets the type specific data field.
|
||||
func (b ICMPv6) SetTypeSpecific(val uint32) {
|
||||
binary.BigEndian.PutUint32(b[icmpv6PointerOffset:], val)
|
||||
}
|
||||
|
||||
// Checksum is the ICMP checksum field.
|
||||
func (b ICMPv6) Checksum() uint16 {
|
||||
return binary.BigEndian.Uint16(b[ICMPv6ChecksumOffset:])
|
||||
}
|
||||
|
||||
// SetChecksum sets the ICMP checksum field.
|
||||
func (b ICMPv6) SetChecksum(cs uint16) {
|
||||
checksum.Put(b[ICMPv6ChecksumOffset:], cs)
|
||||
}
|
||||
|
||||
// SourcePort implements Transport.SourcePort.
|
||||
func (ICMPv6) SourcePort() uint16 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// DestinationPort implements Transport.DestinationPort.
|
||||
func (ICMPv6) DestinationPort() uint16 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// SetSourcePort implements Transport.SetSourcePort.
|
||||
func (ICMPv6) SetSourcePort(uint16) {
|
||||
}
|
||||
|
||||
// SetDestinationPort implements Transport.SetDestinationPort.
|
||||
func (ICMPv6) SetDestinationPort(uint16) {
|
||||
}
|
||||
|
||||
// MTU retrieves the MTU field from an ICMPv6 message.
|
||||
func (b ICMPv6) MTU() uint32 {
|
||||
return binary.BigEndian.Uint32(b[icmpv6MTUOffset:])
|
||||
}
|
||||
|
||||
// SetMTU sets the MTU field from an ICMPv6 message.
|
||||
func (b ICMPv6) SetMTU(mtu uint32) {
|
||||
binary.BigEndian.PutUint32(b[icmpv6MTUOffset:], mtu)
|
||||
}
|
||||
|
||||
// Ident retrieves the Ident field from an ICMPv6 message.
|
||||
func (b ICMPv6) Ident() uint16 {
|
||||
return binary.BigEndian.Uint16(b[icmpv6IdentOffset:])
|
||||
}
|
||||
|
||||
// SetIdent sets the Ident field from an ICMPv6 message.
|
||||
func (b ICMPv6) SetIdent(ident uint16) {
|
||||
binary.BigEndian.PutUint16(b[icmpv6IdentOffset:], ident)
|
||||
}
|
||||
|
||||
// SetIdentWithChecksumUpdate sets the Ident field and updates the checksum.
|
||||
func (b ICMPv6) SetIdentWithChecksumUpdate(new uint16) {
|
||||
old := b.Ident()
|
||||
b.SetIdent(new)
|
||||
b.SetChecksum(^checksumUpdate2ByteAlignedUint16(^b.Checksum(), old, new))
|
||||
}
|
||||
|
||||
// Sequence retrieves the Sequence field from an ICMPv6 message.
|
||||
func (b ICMPv6) Sequence() uint16 {
|
||||
return binary.BigEndian.Uint16(b[icmpv6SequenceOffset:])
|
||||
}
|
||||
|
||||
// SetSequence sets the Sequence field from an ICMPv6 message.
|
||||
func (b ICMPv6) SetSequence(sequence uint16) {
|
||||
binary.BigEndian.PutUint16(b[icmpv6SequenceOffset:], sequence)
|
||||
}
|
||||
|
||||
// MessageBody returns the message body as defined by RFC 4443 section 2.1; the
|
||||
// portion of the ICMPv6 buffer after the first ICMPv6HeaderSize bytes.
|
||||
func (b ICMPv6) MessageBody() []byte {
|
||||
return b[ICMPv6HeaderSize:]
|
||||
}
|
||||
|
||||
// Payload implements Transport.Payload.
|
||||
func (b ICMPv6) Payload() []byte {
|
||||
return b[ICMPv6PayloadOffset:]
|
||||
}
|
||||
|
||||
// ICMPv6ChecksumParams contains parameters to calculate ICMPv6 checksum.
|
||||
type ICMPv6ChecksumParams struct {
|
||||
Header ICMPv6
|
||||
Src tcpip.Address
|
||||
Dst tcpip.Address
|
||||
PayloadCsum uint16
|
||||
PayloadLen int
|
||||
}
|
||||
|
||||
// ICMPv6Checksum calculates the ICMP checksum over the provided ICMPv6 header,
|
||||
// IPv6 src/dst addresses and the payload.
|
||||
func ICMPv6Checksum(params ICMPv6ChecksumParams) uint16 {
|
||||
h := params.Header
|
||||
|
||||
xsum := PseudoHeaderChecksum(ICMPv6ProtocolNumber, params.Src.AsSlice(), params.Dst.AsSlice(), uint16(len(h)+params.PayloadLen))
|
||||
xsum = checksum.Combine(xsum, params.PayloadCsum)
|
||||
|
||||
// h[2:4] is the checksum itself, skip it to avoid checksumming the checksum.
|
||||
xsum = checksum.Checksum(h[:2], xsum)
|
||||
xsum = checksum.Checksum(h[4:], xsum)
|
||||
|
||||
return ^xsum
|
||||
}
|
||||
|
||||
// UpdateChecksumPseudoHeaderAddress updates the checksum to reflect an
|
||||
// updated address in the pseudo header.
|
||||
func (b ICMPv6) UpdateChecksumPseudoHeaderAddress(old, new tcpip.Address) {
|
||||
b.SetChecksum(^checksumUpdate2ByteAlignedAddress(^b.Checksum(), old, new))
|
||||
}
|
||||
1205
internal/gtcpip/header/ipv4.go
Normal file
1205
internal/gtcpip/header/ipv4.go
Normal file
File diff suppressed because it is too large
Load diff
578
internal/gtcpip/header/ipv6.go
Normal file
578
internal/gtcpip/header/ipv6.go
Normal file
|
|
@ -0,0 +1,578 @@
|
|||
// Copyright 2018 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package header
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
|
||||
"github.com/sagernet/sing-tun/internal/gtcpip"
|
||||
)
|
||||
|
||||
const (
|
||||
versTCFL = 0
|
||||
// IPv6PayloadLenOffset is the offset of the PayloadLength field in
|
||||
// IPv6 header.
|
||||
IPv6PayloadLenOffset = 4
|
||||
// IPv6NextHeaderOffset is the offset of the NextHeader field in
|
||||
// IPv6 header.
|
||||
IPv6NextHeaderOffset = 6
|
||||
hopLimit = 7
|
||||
v6SrcAddr = 8
|
||||
v6DstAddr = v6SrcAddr + IPv6AddressSize
|
||||
|
||||
// IPv6FixedHeaderSize is the size of the fixed header.
|
||||
IPv6FixedHeaderSize = v6DstAddr + IPv6AddressSize
|
||||
)
|
||||
|
||||
// IPv6Fields contains the fields of an IPv6 packet. It is used to describe the
|
||||
// fields of a packet that needs to be encoded.
|
||||
type IPv6Fields struct {
|
||||
// TrafficClass is the "traffic class" field of an IPv6 packet.
|
||||
TrafficClass uint8
|
||||
|
||||
// FlowLabel is the "flow label" field of an IPv6 packet.
|
||||
FlowLabel uint32
|
||||
|
||||
// PayloadLength is the "payload length" field of an IPv6 packet, including
|
||||
// the length of all extension headers.
|
||||
PayloadLength uint16
|
||||
|
||||
// TransportProtocol is the transport layer protocol number. Serialized in the
|
||||
// last "next header" field of the IPv6 header + extension headers.
|
||||
TransportProtocol tcpip.TransportProtocolNumber
|
||||
|
||||
// HopLimit is the "Hop Limit" field of an IPv6 packet.
|
||||
HopLimit uint8
|
||||
|
||||
// SrcAddr is the "source ip address" of an IPv6 packet.
|
||||
SrcAddr netip.Addr
|
||||
|
||||
// DstAddr is the "destination ip address" of an IPv6 packet.
|
||||
DstAddr netip.Addr
|
||||
|
||||
// ExtensionHeaders are the extension headers following the IPv6 header.
|
||||
ExtensionHeaders IPv6ExtHdrSerializer
|
||||
}
|
||||
|
||||
// IPv6 represents an ipv6 header stored in a byte array.
|
||||
// Most of the methods of IPv6 access to the underlying slice without
|
||||
// checking the boundaries and could panic because of 'index out of range'.
|
||||
// Always call IsValid() to validate an instance of IPv6 before using other methods.
|
||||
type IPv6 []byte
|
||||
|
||||
const (
|
||||
// IPv6MinimumSize is the minimum size of a valid IPv6 packet.
|
||||
IPv6MinimumSize = IPv6FixedHeaderSize
|
||||
|
||||
// IPv6AddressSize is the size, in bytes, of an IPv6 address.
|
||||
IPv6AddressSize = 16
|
||||
|
||||
// IPv6AddressSizeBits is the size, in bits, of an IPv6 address.
|
||||
IPv6AddressSizeBits = 128
|
||||
|
||||
// IPv6MaximumPayloadSize is the maximum size of a valid IPv6 payload per
|
||||
// RFC 8200 Section 4.5.
|
||||
IPv6MaximumPayloadSize = 65535
|
||||
|
||||
// IPv6ProtocolNumber is IPv6's network protocol number.
|
||||
IPv6ProtocolNumber tcpip.NetworkProtocolNumber = 0x86dd
|
||||
|
||||
// IPv6Version is the version of the ipv6 protocol.
|
||||
IPv6Version = 6
|
||||
|
||||
// IIDSize is the size of an interface identifier (IID), in bytes, as
|
||||
// defined by RFC 4291 section 2.5.1.
|
||||
IIDSize = 8
|
||||
|
||||
// IPv6MinimumMTU is the minimum MTU required by IPv6, per RFC 8200,
|
||||
// section 5:
|
||||
// IPv6 requires that every link in the Internet have an MTU of 1280 octets
|
||||
// or greater. This is known as the IPv6 minimum link MTU.
|
||||
IPv6MinimumMTU = 1280
|
||||
|
||||
// IIDOffsetInIPv6Address is the offset, in bytes, from the start
|
||||
// of an IPv6 address to the beginning of the interface identifier
|
||||
// (IID) for auto-generated addresses. That is, all bytes before
|
||||
// the IIDOffsetInIPv6Address-th byte are the prefix bytes, and all
|
||||
// bytes including and after the IIDOffsetInIPv6Address-th byte are
|
||||
// for the IID.
|
||||
IIDOffsetInIPv6Address = 8
|
||||
|
||||
// OpaqueIIDSecretKeyMinBytes is the recommended minimum number of bytes
|
||||
// for the secret key used to generate an opaque interface identifier as
|
||||
// outlined by RFC 7217.
|
||||
OpaqueIIDSecretKeyMinBytes = 16
|
||||
|
||||
// ipv6MulticastAddressScopeByteIdx is the byte where the scope (scop) field
|
||||
// is located within a multicast IPv6 address, as per RFC 4291 section 2.7.
|
||||
ipv6MulticastAddressScopeByteIdx = 1
|
||||
|
||||
// ipv6MulticastAddressScopeMask is the mask for the scope (scop) field,
|
||||
// within the byte holding the field, as per RFC 4291 section 2.7.
|
||||
ipv6MulticastAddressScopeMask = 0xF
|
||||
)
|
||||
|
||||
var (
|
||||
// IPv6AllNodesMulticastAddress is a link-local multicast group that
|
||||
// all IPv6 nodes MUST join, as per RFC 4291, section 2.8. Packets
|
||||
// destined to this address will reach all nodes on a link.
|
||||
//
|
||||
// The address is ff02::1.
|
||||
IPv6AllNodesMulticastAddress = tcpip.AddrFrom16([16]byte{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01})
|
||||
|
||||
// IPv6AllRoutersInterfaceLocalMulticastAddress is an interface-local
|
||||
// multicast group that all IPv6 routers MUST join, as per RFC 4291, section
|
||||
// 2.8. Packets destined to this address will reach the router on an
|
||||
// interface.
|
||||
//
|
||||
// The address is ff01::2.
|
||||
IPv6AllRoutersInterfaceLocalMulticastAddress = tcpip.AddrFrom16([16]byte{0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02})
|
||||
|
||||
// IPv6AllRoutersLinkLocalMulticastAddress is a link-local multicast group
|
||||
// that all IPv6 routers MUST join, as per RFC 4291, section 2.8. Packets
|
||||
// destined to this address will reach all routers on a link.
|
||||
//
|
||||
// The address is ff02::2.
|
||||
IPv6AllRoutersLinkLocalMulticastAddress = tcpip.AddrFrom16([16]byte{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02})
|
||||
|
||||
// IPv6AllRoutersSiteLocalMulticastAddress is a site-local multicast group
|
||||
// that all IPv6 routers MUST join, as per RFC 4291, section 2.8. Packets
|
||||
// destined to this address will reach all routers in a site.
|
||||
//
|
||||
// The address is ff05::2.
|
||||
IPv6AllRoutersSiteLocalMulticastAddress = tcpip.AddrFrom16([16]byte{0xff, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02})
|
||||
|
||||
// IPv6Loopback is the IPv6 Loopback address.
|
||||
IPv6Loopback = tcpip.AddrFrom16([16]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01})
|
||||
|
||||
// IPv6Any is the non-routable IPv6 "any" meta address. It is also
|
||||
// known as the unspecified address.
|
||||
IPv6Any = tcpip.AddrFrom16([16]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
|
||||
)
|
||||
|
||||
// IPv6EmptySubnet is the empty IPv6 subnet. It may also be known as the
|
||||
// catch-all or wildcard subnet. That is, all IPv6 addresses are considered to
|
||||
// be contained within this subnet.
|
||||
var IPv6EmptySubnet = tcpip.AddressWithPrefix{
|
||||
Address: IPv6Any,
|
||||
PrefixLen: 0,
|
||||
}.Subnet()
|
||||
|
||||
// IPv4MappedIPv6Subnet is the prefix for an IPv4 mapped IPv6 address as defined
|
||||
// by RFC 4291 section 2.5.5.
|
||||
var IPv4MappedIPv6Subnet = tcpip.AddressWithPrefix{
|
||||
Address: tcpip.AddrFrom16([16]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00}),
|
||||
PrefixLen: 96,
|
||||
}.Subnet()
|
||||
|
||||
// IPv6LinkLocalPrefix is the prefix for IPv6 link-local addresses, as defined
|
||||
// by RFC 4291 section 2.5.6.
|
||||
//
|
||||
// The prefix is fe80::/64
|
||||
var IPv6LinkLocalPrefix = tcpip.AddressWithPrefix{
|
||||
Address: tcpip.AddrFrom16([16]byte{0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}),
|
||||
PrefixLen: 64,
|
||||
}
|
||||
|
||||
// PayloadLength returns the value of the "payload length" field of the ipv6
|
||||
// header.
|
||||
func (b IPv6) PayloadLength() uint16 {
|
||||
return binary.BigEndian.Uint16(b[IPv6PayloadLenOffset:])
|
||||
}
|
||||
|
||||
// HopLimit returns the value of the "Hop Limit" field of the ipv6 header.
|
||||
func (b IPv6) HopLimit() uint8 {
|
||||
return b[hopLimit]
|
||||
}
|
||||
|
||||
// NextHeader returns the value of the "next header" field of the ipv6 header.
|
||||
func (b IPv6) NextHeader() uint8 {
|
||||
return b[IPv6NextHeaderOffset]
|
||||
}
|
||||
|
||||
// TransportProtocol implements Network.TransportProtocol.
|
||||
func (b IPv6) TransportProtocol() tcpip.TransportProtocolNumber {
|
||||
return tcpip.TransportProtocolNumber(b.NextHeader())
|
||||
}
|
||||
|
||||
// Payload implements Network.Payload.
|
||||
func (b IPv6) Payload() []byte {
|
||||
return b[IPv6MinimumSize:][:b.PayloadLength()]
|
||||
}
|
||||
|
||||
// SourceAddress returns the "source address" field of the ipv6 header.
|
||||
func (b IPv6) SourceAddress() tcpip.Address {
|
||||
return tcpip.AddrFrom16([16]byte(b[v6SrcAddr:][:IPv6AddressSize]))
|
||||
}
|
||||
|
||||
// DestinationAddress returns the "destination address" field of the ipv6
|
||||
// header.
|
||||
func (b IPv6) DestinationAddress() tcpip.Address {
|
||||
return tcpip.AddrFrom16([16]byte(b[v6DstAddr:][:IPv6AddressSize]))
|
||||
}
|
||||
|
||||
// SourceAddressSlice returns the "source address" field of the ipv6 header as a
|
||||
// byte slice.
|
||||
func (b IPv6) SourceAddressSlice() []byte {
|
||||
return []byte(b[v6SrcAddr:][:IPv6AddressSize])
|
||||
}
|
||||
|
||||
// DestinationAddressSlice returns the "destination address" field of the ipv6
|
||||
// header as a byte slice.
|
||||
func (b IPv6) DestinationAddressSlice() []byte {
|
||||
return []byte(b[v6DstAddr:][:IPv6AddressSize])
|
||||
}
|
||||
|
||||
// Checksum implements Network.Checksum. Given that IPv6 doesn't have a
|
||||
// checksum, it just returns 0.
|
||||
func (IPv6) Checksum() uint16 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// TOS returns the "traffic class" and "flow label" fields of the ipv6 header.
|
||||
func (b IPv6) TOS() (uint8, uint32) {
|
||||
v := binary.BigEndian.Uint32(b[versTCFL:])
|
||||
return uint8(v >> 20), v & 0xfffff
|
||||
}
|
||||
|
||||
// SetTOS sets the "traffic class" and "flow label" fields of the ipv6 header.
|
||||
func (b IPv6) SetTOS(t uint8, l uint32) {
|
||||
vtf := (6 << 28) | (uint32(t) << 20) | (l & 0xfffff)
|
||||
binary.BigEndian.PutUint32(b[versTCFL:], vtf)
|
||||
}
|
||||
|
||||
// SetPayloadLength sets the "payload length" field of the ipv6 header.
|
||||
func (b IPv6) SetPayloadLength(payloadLength uint16) {
|
||||
binary.BigEndian.PutUint16(b[IPv6PayloadLenOffset:], payloadLength)
|
||||
}
|
||||
|
||||
// SetSourceAddress sets the "source address" field of the ipv6 header.
|
||||
func (b IPv6) SetSourceAddress(addr tcpip.Address) {
|
||||
copy(b[v6SrcAddr:][:IPv6AddressSize], addr.AsSlice())
|
||||
}
|
||||
|
||||
// SetDestinationAddress sets the "destination address" field of the ipv6
|
||||
// header.
|
||||
func (b IPv6) SetDestinationAddress(addr tcpip.Address) {
|
||||
copy(b[v6DstAddr:][:IPv6AddressSize], addr.AsSlice())
|
||||
}
|
||||
|
||||
// SetHopLimit sets the value of the "Hop Limit" field.
|
||||
func (b IPv6) SetHopLimit(v uint8) {
|
||||
b[hopLimit] = v
|
||||
}
|
||||
|
||||
// SetNextHeader sets the value of the "next header" field of the ipv6 header.
|
||||
func (b IPv6) SetNextHeader(v uint8) {
|
||||
b[IPv6NextHeaderOffset] = v
|
||||
}
|
||||
|
||||
// SetChecksum implements Network.SetChecksum. Given that IPv6 doesn't have a
|
||||
// checksum, it is empty.
|
||||
func (IPv6) SetChecksum(uint16) {
|
||||
}
|
||||
|
||||
// Encode encodes all the fields of the ipv6 header.
|
||||
func (b IPv6) Encode(i *IPv6Fields) {
|
||||
extHdr := b[IPv6MinimumSize:]
|
||||
b.SetTOS(i.TrafficClass, i.FlowLabel)
|
||||
b.SetPayloadLength(i.PayloadLength)
|
||||
b[hopLimit] = i.HopLimit
|
||||
b.SetSourceAddr(i.SrcAddr)
|
||||
b.SetDestinationAddr(i.DstAddr)
|
||||
nextHeader, _ := i.ExtensionHeaders.Serialize(i.TransportProtocol, extHdr)
|
||||
b[IPv6NextHeaderOffset] = nextHeader
|
||||
}
|
||||
|
||||
// IsValid performs basic validation on the packet.
|
||||
func (b IPv6) IsValid(pktSize int) bool {
|
||||
if len(b) < IPv6MinimumSize {
|
||||
return false
|
||||
}
|
||||
|
||||
dlen := int(b.PayloadLength())
|
||||
if dlen > pktSize-IPv6MinimumSize {
|
||||
return false
|
||||
}
|
||||
|
||||
if IPVersion(b) != IPv6Version {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// IsV4MappedAddress determines if the provided address is an IPv4 mapped
|
||||
// address by checking if its prefix is 0:0:0:0:0:ffff::/96.
|
||||
func IsV4MappedAddress(addr tcpip.Address) bool {
|
||||
if addr.BitLen() != IPv6AddressSizeBits {
|
||||
return false
|
||||
}
|
||||
|
||||
return IPv4MappedIPv6Subnet.Contains(addr)
|
||||
}
|
||||
|
||||
// IsV6MulticastAddress determines if the provided address is an IPv6
|
||||
// multicast address (anything starting with FF).
|
||||
func IsV6MulticastAddress(addr tcpip.Address) bool {
|
||||
if addr.BitLen() != IPv6AddressSizeBits {
|
||||
return false
|
||||
}
|
||||
return addr.As16()[0] == 0xff
|
||||
}
|
||||
|
||||
// IsV6UnicastAddress determines if the provided address is a valid IPv6
|
||||
// unicast (and specified) address. That is, IsV6UnicastAddress returns
|
||||
// true if addr contains IPv6AddressSize bytes, is not the unspecified
|
||||
// address and is not a multicast address.
|
||||
func IsV6UnicastAddress(addr tcpip.Address) bool {
|
||||
if addr.BitLen() != IPv6AddressSizeBits {
|
||||
return false
|
||||
}
|
||||
|
||||
// Must not be unspecified
|
||||
if addr == IPv6Any {
|
||||
return false
|
||||
}
|
||||
|
||||
// Return if not a multicast.
|
||||
return addr.As16()[0] != 0xff
|
||||
}
|
||||
|
||||
var solicitedNodeMulticastPrefix = [13]byte{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff}
|
||||
|
||||
// SolicitedNodeAddr computes the solicited-node multicast address. This is
|
||||
// used for NDP. Described in RFC 4291. The argument must be a full-length IPv6
|
||||
// address.
|
||||
func SolicitedNodeAddr(addr tcpip.Address) tcpip.Address {
|
||||
addrBytes := addr.As16()
|
||||
return tcpip.AddrFrom16([16]byte(append(solicitedNodeMulticastPrefix[:], addrBytes[len(addrBytes)-3:]...)))
|
||||
}
|
||||
|
||||
// IsSolicitedNodeAddr determines whether the address is a solicited-node
|
||||
// multicast address.
|
||||
func IsSolicitedNodeAddr(addr tcpip.Address) bool {
|
||||
addrBytes := addr.As16()
|
||||
return solicitedNodeMulticastPrefix == [13]byte(addrBytes[:len(addrBytes)-3])
|
||||
}
|
||||
|
||||
// EthernetAdddressToModifiedEUI64IntoBuf populates buf with a modified EUI-64
|
||||
// from a 48-bit Ethernet/MAC address, as per RFC 4291 section 2.5.1.
|
||||
//
|
||||
// buf MUST be at least 8 bytes.
|
||||
func EthernetAdddressToModifiedEUI64IntoBuf(linkAddr tcpip.LinkAddress, buf []byte) {
|
||||
buf[0] = linkAddr[0] ^ 2
|
||||
buf[1] = linkAddr[1]
|
||||
buf[2] = linkAddr[2]
|
||||
buf[3] = 0xFF
|
||||
buf[4] = 0xFE
|
||||
buf[5] = linkAddr[3]
|
||||
buf[6] = linkAddr[4]
|
||||
buf[7] = linkAddr[5]
|
||||
}
|
||||
|
||||
// EthernetAddressToModifiedEUI64 computes a modified EUI-64 from a 48-bit
|
||||
// Ethernet/MAC address, as per RFC 4291 section 2.5.1.
|
||||
func EthernetAddressToModifiedEUI64(linkAddr tcpip.LinkAddress) [IIDSize]byte {
|
||||
var buf [IIDSize]byte
|
||||
EthernetAdddressToModifiedEUI64IntoBuf(linkAddr, buf[:])
|
||||
return buf
|
||||
}
|
||||
|
||||
// LinkLocalAddr computes the default IPv6 link-local address from a link-layer
|
||||
// (MAC) address.
|
||||
func LinkLocalAddr(linkAddr tcpip.LinkAddress) tcpip.Address {
|
||||
// Convert a 48-bit MAC to a modified EUI-64 and then prepend the
|
||||
// link-local header, FE80::.
|
||||
//
|
||||
// The conversion is very nearly:
|
||||
// aa:bb:cc:dd:ee:ff => FE80::Aabb:ccFF:FEdd:eeff
|
||||
// Note the capital A. The conversion aa->Aa involves a bit flip.
|
||||
lladdrb := [IPv6AddressSize]byte{
|
||||
0: 0xFE,
|
||||
1: 0x80,
|
||||
}
|
||||
EthernetAdddressToModifiedEUI64IntoBuf(linkAddr, lladdrb[IIDOffsetInIPv6Address:])
|
||||
return tcpip.AddrFrom16(lladdrb)
|
||||
}
|
||||
|
||||
// IsV6LinkLocalUnicastAddress returns true iff the provided address is an IPv6
|
||||
// link-local unicast address, as defined by RFC 4291 section 2.5.6.
|
||||
func IsV6LinkLocalUnicastAddress(addr tcpip.Address) bool {
|
||||
if addr.BitLen() != IPv6AddressSizeBits {
|
||||
return false
|
||||
}
|
||||
addrBytes := addr.As16()
|
||||
return addrBytes[0] == 0xfe && (addrBytes[1]&0xc0) == 0x80
|
||||
}
|
||||
|
||||
// IsV6LoopbackAddress returns true iff the provided address is an IPv6 loopback
|
||||
// address, as defined by RFC 4291 section 2.5.3.
|
||||
func IsV6LoopbackAddress(addr tcpip.Address) bool {
|
||||
return addr == IPv6Loopback
|
||||
}
|
||||
|
||||
// IsV6LinkLocalMulticastAddress returns true iff the provided address is an
|
||||
// IPv6 link-local multicast address, as defined by RFC 4291 section 2.7.
|
||||
func IsV6LinkLocalMulticastAddress(addr tcpip.Address) bool {
|
||||
return IsV6MulticastAddress(addr) && V6MulticastScope(addr) == IPv6LinkLocalMulticastScope
|
||||
}
|
||||
|
||||
// AppendOpaqueInterfaceIdentifier appends a 64 bit opaque interface identifier
|
||||
// (IID) to buf as outlined by RFC 7217 and returns the extended buffer.
|
||||
//
|
||||
// The opaque IID is generated from the cryptographic hash of the concatenation
|
||||
// of the prefix, NIC's name, DAD counter (DAD retry counter) and the secret
|
||||
// key. The secret key SHOULD be at least OpaqueIIDSecretKeyMinBytes bytes and
|
||||
// MUST be generated to a pseudo-random number. See RFC 4086 for randomness
|
||||
// requirements for security.
|
||||
//
|
||||
// If buf has enough capacity for the IID (IIDSize bytes), a new underlying
|
||||
// array for the buffer will not be allocated.
|
||||
func AppendOpaqueInterfaceIdentifier(buf []byte, prefix tcpip.Subnet, nicName string, dadCounter uint8, secretKey []byte) []byte {
|
||||
// As per RFC 7217 section 5, the opaque identifier can be generated as a
|
||||
// cryptographic hash of the concatenation of each of the function parameters.
|
||||
// Note, we omit the optional Network_ID field.
|
||||
h := sha256.New()
|
||||
// h.Write never returns an error.
|
||||
prefixID := prefix.ID()
|
||||
h.Write([]byte(prefixID.AsSlice()[:IIDOffsetInIPv6Address]))
|
||||
h.Write([]byte(nicName))
|
||||
h.Write([]byte{dadCounter})
|
||||
h.Write(secretKey)
|
||||
|
||||
var sumBuf [sha256.Size]byte
|
||||
sum := h.Sum(sumBuf[:0])
|
||||
|
||||
return append(buf, sum[:IIDSize]...)
|
||||
}
|
||||
|
||||
// LinkLocalAddrWithOpaqueIID computes the default IPv6 link-local address with
|
||||
// an opaque IID.
|
||||
func LinkLocalAddrWithOpaqueIID(nicName string, dadCounter uint8, secretKey []byte) tcpip.Address {
|
||||
lladdrb := [IPv6AddressSize]byte{
|
||||
0: 0xFE,
|
||||
1: 0x80,
|
||||
}
|
||||
|
||||
return tcpip.AddrFrom16([16]byte(AppendOpaqueInterfaceIdentifier(lladdrb[:IIDOffsetInIPv6Address], IPv6LinkLocalPrefix.Subnet(), nicName, dadCounter, secretKey)))
|
||||
}
|
||||
|
||||
// IPv6AddressScope is the scope of an IPv6 address.
|
||||
type IPv6AddressScope int
|
||||
|
||||
const (
|
||||
// LinkLocalScope indicates a link-local address.
|
||||
LinkLocalScope IPv6AddressScope = iota
|
||||
|
||||
// GlobalScope indicates a global address.
|
||||
GlobalScope
|
||||
)
|
||||
|
||||
// ScopeForIPv6Address returns the scope for an IPv6 address.
|
||||
func ScopeForIPv6Address(addr tcpip.Address) (IPv6AddressScope, tcpip.Error) {
|
||||
if addr.BitLen() != IPv6AddressSizeBits {
|
||||
return GlobalScope, &tcpip.ErrBadAddress{}
|
||||
}
|
||||
|
||||
switch {
|
||||
case IsV6LinkLocalMulticastAddress(addr):
|
||||
return LinkLocalScope, nil
|
||||
|
||||
case IsV6LinkLocalUnicastAddress(addr):
|
||||
return LinkLocalScope, nil
|
||||
|
||||
default:
|
||||
return GlobalScope, nil
|
||||
}
|
||||
}
|
||||
|
||||
// GenerateTempIPv6SLAACAddr generates a temporary SLAAC IPv6 address for an
|
||||
// associated stable/permanent SLAAC address.
|
||||
//
|
||||
// GenerateTempIPv6SLAACAddr will update the temporary IID history value to be
|
||||
// used when generating a new temporary IID.
|
||||
//
|
||||
// Panics if tempIIDHistory is not at least IIDSize bytes.
|
||||
func GenerateTempIPv6SLAACAddr(tempIIDHistory []byte, stableAddr tcpip.Address) tcpip.AddressWithPrefix {
|
||||
addrBytes := stableAddr.As16()
|
||||
h := sha256.New()
|
||||
h.Write(tempIIDHistory)
|
||||
h.Write(addrBytes[IIDOffsetInIPv6Address:])
|
||||
var sumBuf [sha256.Size]byte
|
||||
sum := h.Sum(sumBuf[:0])
|
||||
|
||||
// The rightmost 64 bits of sum are saved for the next iteration.
|
||||
if n := copy(tempIIDHistory, sum[sha256.Size-IIDSize:]); n != IIDSize {
|
||||
panic(fmt.Sprintf("copied %d bytes, expected %d bytes", n, IIDSize))
|
||||
}
|
||||
|
||||
// The leftmost 64 bits of sum is used as the IID.
|
||||
if n := copy(addrBytes[IIDOffsetInIPv6Address:], sum); n != IIDSize {
|
||||
panic(fmt.Sprintf("copied %d IID bytes, expected %d bytes", n, IIDSize))
|
||||
}
|
||||
|
||||
return tcpip.AddressWithPrefix{
|
||||
Address: tcpip.AddrFrom16(addrBytes),
|
||||
PrefixLen: IIDOffsetInIPv6Address * 8,
|
||||
}
|
||||
}
|
||||
|
||||
// IPv6MulticastScope is the scope of a multicast IPv6 address, as defined by
|
||||
// RFC 7346 section 2.
|
||||
type IPv6MulticastScope uint8
|
||||
|
||||
// The various values for IPv6 multicast scopes, as per RFC 7346 section 2:
|
||||
//
|
||||
// +------+--------------------------+-------------------------+
|
||||
// | scop | NAME | REFERENCE |
|
||||
// +------+--------------------------+-------------------------+
|
||||
// | 0 | Reserved | [RFC4291], RFC 7346 |
|
||||
// | 1 | Interface-Local scope | [RFC4291], RFC 7346 |
|
||||
// | 2 | Link-Local scope | [RFC4291], RFC 7346 |
|
||||
// | 3 | Realm-Local scope | [RFC4291], RFC 7346 |
|
||||
// | 4 | Admin-Local scope | [RFC4291], RFC 7346 |
|
||||
// | 5 | Site-Local scope | [RFC4291], RFC 7346 |
|
||||
// | 6 | Unassigned | |
|
||||
// | 7 | Unassigned | |
|
||||
// | 8 | Organization-Local scope | [RFC4291], RFC 7346 |
|
||||
// | 9 | Unassigned | |
|
||||
// | A | Unassigned | |
|
||||
// | B | Unassigned | |
|
||||
// | C | Unassigned | |
|
||||
// | D | Unassigned | |
|
||||
// | E | Global scope | [RFC4291], RFC 7346 |
|
||||
// | F | Reserved | [RFC4291], RFC 7346 |
|
||||
// +------+--------------------------+-------------------------+
|
||||
const (
|
||||
IPv6Reserved0MulticastScope = IPv6MulticastScope(0x0)
|
||||
IPv6InterfaceLocalMulticastScope = IPv6MulticastScope(0x1)
|
||||
IPv6LinkLocalMulticastScope = IPv6MulticastScope(0x2)
|
||||
IPv6RealmLocalMulticastScope = IPv6MulticastScope(0x3)
|
||||
IPv6AdminLocalMulticastScope = IPv6MulticastScope(0x4)
|
||||
IPv6SiteLocalMulticastScope = IPv6MulticastScope(0x5)
|
||||
IPv6OrganizationLocalMulticastScope = IPv6MulticastScope(0x8)
|
||||
IPv6GlobalMulticastScope = IPv6MulticastScope(0xE)
|
||||
IPv6ReservedFMulticastScope = IPv6MulticastScope(0xF)
|
||||
)
|
||||
|
||||
// V6MulticastScope returns the scope of a multicast address.
|
||||
func V6MulticastScope(addr tcpip.Address) IPv6MulticastScope {
|
||||
addrBytes := addr.As16()
|
||||
return IPv6MulticastScope(addrBytes[ipv6MulticastAddressScopeByteIdx] & ipv6MulticastAddressScopeMask)
|
||||
}
|
||||
955
internal/gtcpip/header/ipv6_extension_headers.go
Normal file
955
internal/gtcpip/header/ipv6_extension_headers.go
Normal file
|
|
@ -0,0 +1,955 @@
|
|||
// Copyright 2020 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package header
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/buffer"
|
||||
"github.com/sagernet/sing-tun/internal/gtcpip"
|
||||
"github.com/sagernet/sing/common"
|
||||
)
|
||||
|
||||
// IPv6ExtensionHeaderIdentifier is an IPv6 extension header identifier.
|
||||
type IPv6ExtensionHeaderIdentifier uint8
|
||||
|
||||
const (
|
||||
// IPv6HopByHopOptionsExtHdrIdentifier is the header identifier of a Hop by
|
||||
// Hop Options extension header, as per RFC 8200 section 4.3.
|
||||
IPv6HopByHopOptionsExtHdrIdentifier IPv6ExtensionHeaderIdentifier = 0
|
||||
|
||||
// IPv6RoutingExtHdrIdentifier is the header identifier of a Routing extension
|
||||
// header, as per RFC 8200 section 4.4.
|
||||
IPv6RoutingExtHdrIdentifier IPv6ExtensionHeaderIdentifier = 43
|
||||
|
||||
// IPv6FragmentExtHdrIdentifier is the header identifier of a Fragment
|
||||
// extension header, as per RFC 8200 section 4.5.
|
||||
IPv6FragmentExtHdrIdentifier IPv6ExtensionHeaderIdentifier = 44
|
||||
|
||||
// IPv6DestinationOptionsExtHdrIdentifier is the header identifier of a
|
||||
// Destination Options extension header, as per RFC 8200 section 4.6.
|
||||
IPv6DestinationOptionsExtHdrIdentifier IPv6ExtensionHeaderIdentifier = 60
|
||||
|
||||
// IPv6NoNextHeaderIdentifier is the header identifier used to signify the end
|
||||
// of an IPv6 payload, as per RFC 8200 section 4.7.
|
||||
IPv6NoNextHeaderIdentifier IPv6ExtensionHeaderIdentifier = 59
|
||||
|
||||
// IPv6UnknownExtHdrIdentifier is reserved by IANA.
|
||||
// https://www.iana.org/assignments/ipv6-parameters/ipv6-parameters.xhtml#extension-header
|
||||
// "254 Use for experimentation and testing [RFC3692][RFC4727]"
|
||||
IPv6UnknownExtHdrIdentifier IPv6ExtensionHeaderIdentifier = 254
|
||||
)
|
||||
|
||||
const (
|
||||
// ipv6UnknownExtHdrOptionActionMask is the mask of the action to take when
|
||||
// a node encounters an unrecognized option.
|
||||
ipv6UnknownExtHdrOptionActionMask = 192
|
||||
|
||||
// ipv6UnknownExtHdrOptionActionShift is the least significant bits to discard
|
||||
// from the action value for an unrecognized option identifier.
|
||||
ipv6UnknownExtHdrOptionActionShift = 6
|
||||
|
||||
// ipv6RoutingExtHdrSegmentsLeftIdx is the index to the Segments Left field
|
||||
// within an IPv6RoutingExtHdr.
|
||||
ipv6RoutingExtHdrSegmentsLeftIdx = 1
|
||||
|
||||
// IPv6FragmentExtHdrLength is the length of an IPv6 extension header, in
|
||||
// bytes.
|
||||
IPv6FragmentExtHdrLength = 8
|
||||
|
||||
// ipv6FragmentExtHdrFragmentOffsetOffset is the offset to the start of the
|
||||
// Fragment Offset field within an IPv6FragmentExtHdr.
|
||||
ipv6FragmentExtHdrFragmentOffsetOffset = 0
|
||||
|
||||
// ipv6FragmentExtHdrFragmentOffsetShift is the bit offset of the Fragment
|
||||
// Offset field within an IPv6FragmentExtHdr.
|
||||
ipv6FragmentExtHdrFragmentOffsetShift = 3
|
||||
|
||||
// ipv6FragmentExtHdrFlagsIdx is the index to the flags field within an
|
||||
// IPv6FragmentExtHdr.
|
||||
ipv6FragmentExtHdrFlagsIdx = 1
|
||||
|
||||
// ipv6FragmentExtHdrMFlagMask is the mask of the More (M) flag within the
|
||||
// flags field of an IPv6FragmentExtHdr.
|
||||
ipv6FragmentExtHdrMFlagMask = 1
|
||||
|
||||
// ipv6FragmentExtHdrIdentificationOffset is the offset to the Identification
|
||||
// field within an IPv6FragmentExtHdr.
|
||||
ipv6FragmentExtHdrIdentificationOffset = 2
|
||||
|
||||
// ipv6ExtHdrLenBytesPerUnit is the unit size of an extension header's length
|
||||
// field. That is, given a Length field of 2, the extension header expects
|
||||
// 16 bytes following the first 8 bytes (see ipv6ExtHdrLenBytesExcluded for
|
||||
// details about the first 8 bytes' exclusion from the Length field).
|
||||
ipv6ExtHdrLenBytesPerUnit = 8
|
||||
|
||||
// ipv6ExtHdrLenBytesExcluded is the number of bytes excluded from an
|
||||
// extension header's Length field following the Length field.
|
||||
//
|
||||
// The Length field excludes the first 8 bytes, but the Next Header and Length
|
||||
// field take up the first 2 of the 8 bytes so we expect (at minimum) 6 bytes
|
||||
// after the Length field.
|
||||
//
|
||||
// This ensures that every extension header is at least 8 bytes.
|
||||
ipv6ExtHdrLenBytesExcluded = 6
|
||||
|
||||
// IPv6FragmentExtHdrFragmentOffsetBytesPerUnit is the unit size of a Fragment
|
||||
// extension header's Fragment Offset field. That is, given a Fragment Offset
|
||||
// of 2, the extension header is indicating that the fragment's payload
|
||||
// starts at the 16th byte in the reassembled packet.
|
||||
IPv6FragmentExtHdrFragmentOffsetBytesPerUnit = 8
|
||||
)
|
||||
|
||||
// padIPv6OptionsLength returns the total length for IPv6 options of length l
|
||||
// considering the 8-octet alignment as stated in RFC 8200 Section 4.2.
|
||||
func padIPv6OptionsLength(length int) int {
|
||||
return (length + ipv6ExtHdrLenBytesPerUnit - 1) & ^(ipv6ExtHdrLenBytesPerUnit - 1)
|
||||
}
|
||||
|
||||
// padIPv6Option fills b with the appropriate padding options depending on its
|
||||
// length.
|
||||
func padIPv6Option(b []byte) {
|
||||
switch len(b) {
|
||||
case 0: // No padding needed.
|
||||
case 1: // Pad with Pad1.
|
||||
b[ipv6ExtHdrOptionTypeOffset] = uint8(ipv6Pad1ExtHdrOptionIdentifier)
|
||||
default: // Pad with PadN.
|
||||
s := b[ipv6ExtHdrOptionPayloadOffset:]
|
||||
common.ClearArray(s)
|
||||
b[ipv6ExtHdrOptionTypeOffset] = uint8(ipv6PadNExtHdrOptionIdentifier)
|
||||
b[ipv6ExtHdrOptionLengthOffset] = uint8(len(s))
|
||||
}
|
||||
}
|
||||
|
||||
// ipv6OptionsAlignmentPadding returns the number of padding bytes needed to
|
||||
// serialize an option at headerOffset with alignment requirements
|
||||
// [align]n + alignOffset.
|
||||
func ipv6OptionsAlignmentPadding(headerOffset int, align int, alignOffset int) int {
|
||||
padLen := headerOffset - alignOffset
|
||||
return ((padLen + align - 1) & ^(align - 1)) - padLen
|
||||
}
|
||||
|
||||
// IPv6PayloadHeader is implemented by the various headers that can be found
|
||||
// in an IPv6 payload.
|
||||
//
|
||||
// These headers include IPv6 extension headers or upper layer data.
|
||||
type IPv6PayloadHeader interface {
|
||||
isIPv6PayloadHeader()
|
||||
|
||||
// Release frees all resources held by the header.
|
||||
Release()
|
||||
}
|
||||
|
||||
// IPv6RawPayloadHeader the remainder of an IPv6 payload after an iterator
|
||||
// encounters a Next Header field it does not recognize as an IPv6 extension
|
||||
// header. The caller is responsible for releasing the underlying buffer after
|
||||
// it's no longer needed.
|
||||
type IPv6RawPayloadHeader struct {
|
||||
Identifier IPv6ExtensionHeaderIdentifier
|
||||
Buf buffer.Buffer
|
||||
}
|
||||
|
||||
// isIPv6PayloadHeader implements IPv6PayloadHeader.isIPv6PayloadHeader.
|
||||
func (IPv6RawPayloadHeader) isIPv6PayloadHeader() {}
|
||||
|
||||
// Release implements IPv6PayloadHeader.Release.
|
||||
func (i IPv6RawPayloadHeader) Release() {
|
||||
i.Buf.Release()
|
||||
}
|
||||
|
||||
// ipv6OptionsExtHdr is an IPv6 extension header that holds options.
|
||||
type ipv6OptionsExtHdr struct {
|
||||
buf *buffer.View
|
||||
}
|
||||
|
||||
// Release implements IPv6PayloadHeader.Release.
|
||||
func (i ipv6OptionsExtHdr) Release() {
|
||||
if i.buf != nil {
|
||||
i.buf.Release()
|
||||
}
|
||||
}
|
||||
|
||||
// Iter returns an iterator over the IPv6 extension header options held in b.
|
||||
func (i ipv6OptionsExtHdr) Iter() IPv6OptionsExtHdrOptionsIterator {
|
||||
it := IPv6OptionsExtHdrOptionsIterator{}
|
||||
it.reader = i.buf
|
||||
return it
|
||||
}
|
||||
|
||||
// IPv6OptionsExtHdrOptionsIterator is an iterator over IPv6 extension header
|
||||
// options.
|
||||
//
|
||||
// Note, between when an IPv6OptionsExtHdrOptionsIterator is obtained and last
|
||||
// used, no changes to the underlying buffer may happen. Doing so may cause
|
||||
// undefined and unexpected behaviour. It is fine to obtain an
|
||||
// IPv6OptionsExtHdrOptionsIterator, iterate over the first few options then
|
||||
// modify the backing payload so long as the IPv6OptionsExtHdrOptionsIterator
|
||||
// obtained before modification is no longer used.
|
||||
type IPv6OptionsExtHdrOptionsIterator struct {
|
||||
reader *buffer.View
|
||||
|
||||
// optionOffset is the number of bytes from the first byte of the
|
||||
// options field to the beginning of the current option.
|
||||
optionOffset uint32
|
||||
|
||||
// nextOptionOffset is the offset of the next option.
|
||||
nextOptionOffset uint32
|
||||
}
|
||||
|
||||
// OptionOffset returns the number of bytes parsed while processing the
|
||||
// option field of the current Extension Header.
|
||||
func (i *IPv6OptionsExtHdrOptionsIterator) OptionOffset() uint32 {
|
||||
return i.optionOffset
|
||||
}
|
||||
|
||||
// IPv6OptionUnknownAction is the action that must be taken if the processing
|
||||
// IPv6 node does not recognize the option, as outlined in RFC 8200 section 4.2.
|
||||
type IPv6OptionUnknownAction int
|
||||
|
||||
const (
|
||||
// IPv6OptionUnknownActionSkip indicates that the unrecognized option must
|
||||
// be skipped and the node should continue processing the header.
|
||||
IPv6OptionUnknownActionSkip IPv6OptionUnknownAction = 0
|
||||
|
||||
// IPv6OptionUnknownActionDiscard indicates that the packet must be silently
|
||||
// discarded.
|
||||
IPv6OptionUnknownActionDiscard IPv6OptionUnknownAction = 1
|
||||
|
||||
// IPv6OptionUnknownActionDiscardSendICMP indicates that the packet must be
|
||||
// discarded and the node must send an ICMP Parameter Problem, Code 2, message
|
||||
// to the packet's source, regardless of whether or not the packet's
|
||||
// Destination was a multicast address.
|
||||
IPv6OptionUnknownActionDiscardSendICMP IPv6OptionUnknownAction = 2
|
||||
|
||||
// IPv6OptionUnknownActionDiscardSendICMPNoMulticastDest indicates that the
|
||||
// packet must be discarded and the node must send an ICMP Parameter Problem,
|
||||
// Code 2, message to the packet's source only if the packet's Destination was
|
||||
// not a multicast address.
|
||||
IPv6OptionUnknownActionDiscardSendICMPNoMulticastDest IPv6OptionUnknownAction = 3
|
||||
)
|
||||
|
||||
// IPv6ExtHdrOption is implemented by the various IPv6 extension header options.
|
||||
type IPv6ExtHdrOption interface {
|
||||
// UnknownAction returns the action to take in response to an unrecognized
|
||||
// option.
|
||||
UnknownAction() IPv6OptionUnknownAction
|
||||
|
||||
// isIPv6ExtHdrOption is used to "lock" this interface so it is not
|
||||
// implemented by other packages.
|
||||
isIPv6ExtHdrOption()
|
||||
}
|
||||
|
||||
// IPv6ExtHdrOptionIdentifier is an IPv6 extension header option identifier.
|
||||
type IPv6ExtHdrOptionIdentifier uint8
|
||||
|
||||
const (
|
||||
// ipv6Pad1ExtHdrOptionIdentifier is the identifier for a padding option that
|
||||
// provides 1 byte padding, as outlined in RFC 8200 section 4.2.
|
||||
ipv6Pad1ExtHdrOptionIdentifier IPv6ExtHdrOptionIdentifier = 0
|
||||
|
||||
// ipv6PadNExtHdrOptionIdentifier is the identifier for a padding option that
|
||||
// provides variable length byte padding, as outlined in RFC 8200 section 4.2.
|
||||
ipv6PadNExtHdrOptionIdentifier IPv6ExtHdrOptionIdentifier = 1
|
||||
|
||||
// ipv6RouterAlertHopByHopOptionIdentifier is the identifier for the Router
|
||||
// Alert Hop by Hop option as defined in RFC 2711 section 2.1.
|
||||
ipv6RouterAlertHopByHopOptionIdentifier IPv6ExtHdrOptionIdentifier = 5
|
||||
|
||||
// ipv6ExtHdrOptionTypeOffset is the option type offset in an extension header
|
||||
// option as defined in RFC 8200 section 4.2.
|
||||
ipv6ExtHdrOptionTypeOffset = 0
|
||||
|
||||
// ipv6ExtHdrOptionLengthOffset is the option length offset in an extension
|
||||
// header option as defined in RFC 8200 section 4.2.
|
||||
ipv6ExtHdrOptionLengthOffset = 1
|
||||
|
||||
// ipv6ExtHdrOptionPayloadOffset is the option payload offset in an extension
|
||||
// header option as defined in RFC 8200 section 4.2.
|
||||
ipv6ExtHdrOptionPayloadOffset = 2
|
||||
)
|
||||
|
||||
// ipv6UnknownActionFromIdentifier maps an extension header option's
|
||||
// identifier's high bits to the action to take when the identifier is unknown.
|
||||
func ipv6UnknownActionFromIdentifier(id IPv6ExtHdrOptionIdentifier) IPv6OptionUnknownAction {
|
||||
return IPv6OptionUnknownAction((id & ipv6UnknownExtHdrOptionActionMask) >> ipv6UnknownExtHdrOptionActionShift)
|
||||
}
|
||||
|
||||
// ErrMalformedIPv6ExtHdrOption indicates that an IPv6 extension header option
|
||||
// is malformed.
|
||||
var ErrMalformedIPv6ExtHdrOption = errors.New("malformed IPv6 extension header option")
|
||||
|
||||
// IPv6UnknownExtHdrOption holds the identifier and data for an IPv6 extension
|
||||
// header option that is unknown by the parsing utilities.
|
||||
type IPv6UnknownExtHdrOption struct {
|
||||
Identifier IPv6ExtHdrOptionIdentifier
|
||||
Data *buffer.View
|
||||
}
|
||||
|
||||
// UnknownAction implements IPv6OptionUnknownAction.UnknownAction.
|
||||
func (o *IPv6UnknownExtHdrOption) UnknownAction() IPv6OptionUnknownAction {
|
||||
return ipv6UnknownActionFromIdentifier(o.Identifier)
|
||||
}
|
||||
|
||||
// isIPv6ExtHdrOption implements IPv6ExtHdrOption.isIPv6ExtHdrOption.
|
||||
func (*IPv6UnknownExtHdrOption) isIPv6ExtHdrOption() {}
|
||||
|
||||
// Next returns the next option in the options data.
|
||||
//
|
||||
// If the next item is not a known extension header option,
|
||||
// IPv6UnknownExtHdrOption will be returned with the option identifier and data.
|
||||
//
|
||||
// The return is of the format (option, done, error). done will be true when
|
||||
// Next is unable to return anything because the iterator has reached the end of
|
||||
// the options data, or an error occurred.
|
||||
func (i *IPv6OptionsExtHdrOptionsIterator) Next() (IPv6ExtHdrOption, bool, error) {
|
||||
for {
|
||||
i.optionOffset = i.nextOptionOffset
|
||||
temp, err := i.reader.ReadByte()
|
||||
if err != nil {
|
||||
// If we can't read the first byte of a new option, then we know the
|
||||
// options buffer has been exhausted and we are done iterating.
|
||||
return nil, true, nil
|
||||
}
|
||||
id := IPv6ExtHdrOptionIdentifier(temp)
|
||||
|
||||
// If the option identifier indicates the option is a Pad1 option, then we
|
||||
// know the option does not have Length and Data fields. End processing of
|
||||
// the Pad1 option and continue processing the buffer as a new option.
|
||||
if id == ipv6Pad1ExtHdrOptionIdentifier {
|
||||
i.nextOptionOffset = i.optionOffset + 1
|
||||
continue
|
||||
}
|
||||
|
||||
length, err := i.reader.ReadByte()
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
// ReadByte should only ever return nil or io.EOF.
|
||||
panic(fmt.Sprintf("unexpected error when reading the option's Length field for option with id = %d: %s", id, err))
|
||||
}
|
||||
|
||||
// We use io.ErrUnexpectedEOF as exhausting the buffer is unexpected once
|
||||
// we start parsing an option; we expect the reader to contain enough
|
||||
// bytes for the whole option.
|
||||
return nil, true, fmt.Errorf("error when reading the option's Length field for option with id = %d: %w", id, io.ErrUnexpectedEOF)
|
||||
}
|
||||
|
||||
// Do we have enough bytes in the reader for the next option?
|
||||
if n := i.reader.Size(); n < int(length) {
|
||||
// Consume the remaining buffer.
|
||||
i.reader.TrimFront(i.reader.Size())
|
||||
|
||||
// We return the same error as if we failed to read a non-padding option
|
||||
// so consumers of this iterator don't need to differentiate between
|
||||
// padding and non-padding options.
|
||||
return nil, true, fmt.Errorf("read %d out of %d option data bytes for option with id = %d: %w", n, length, id, io.ErrUnexpectedEOF)
|
||||
}
|
||||
|
||||
i.nextOptionOffset = i.optionOffset + uint32(length) + 1 /* option ID */ + 1 /* length byte */
|
||||
|
||||
switch id {
|
||||
case ipv6PadNExtHdrOptionIdentifier:
|
||||
// Special-case the variable length padding option to avoid a copy.
|
||||
i.reader.TrimFront(int(length))
|
||||
continue
|
||||
case ipv6RouterAlertHopByHopOptionIdentifier:
|
||||
var routerAlertValue [ipv6RouterAlertPayloadLength]byte
|
||||
if n, err := io.ReadFull(i.reader, routerAlertValue[:]); err != nil {
|
||||
switch err {
|
||||
case io.EOF, io.ErrUnexpectedEOF:
|
||||
return nil, true, fmt.Errorf("got invalid length (%d) for router alert option (want = %d): %w", length, ipv6RouterAlertPayloadLength, ErrMalformedIPv6ExtHdrOption)
|
||||
default:
|
||||
return nil, true, fmt.Errorf("read %d out of %d option data bytes for router alert option: %w", n, ipv6RouterAlertPayloadLength, err)
|
||||
}
|
||||
} else if n != int(length) {
|
||||
return nil, true, fmt.Errorf("got invalid length (%d) for router alert option (want = %d): %w", length, ipv6RouterAlertPayloadLength, ErrMalformedIPv6ExtHdrOption)
|
||||
}
|
||||
return &IPv6RouterAlertOption{Value: IPv6RouterAlertValue(binary.BigEndian.Uint16(routerAlertValue[:]))}, false, nil
|
||||
default:
|
||||
bytes := buffer.NewView(int(length))
|
||||
if n, err := io.CopyN(bytes, i.reader, int64(length)); err != nil {
|
||||
if err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
return nil, true, fmt.Errorf("read %d out of %d option data bytes for option with id = %d: %w", n, length, id, err)
|
||||
}
|
||||
return &IPv6UnknownExtHdrOption{Identifier: id, Data: bytes}, false, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IPv6HopByHopOptionsExtHdr is a buffer holding the Hop By Hop Options
|
||||
// extension header.
|
||||
type IPv6HopByHopOptionsExtHdr struct {
|
||||
ipv6OptionsExtHdr
|
||||
}
|
||||
|
||||
// isIPv6PayloadHeader implements IPv6PayloadHeader.isIPv6PayloadHeader.
|
||||
func (IPv6HopByHopOptionsExtHdr) isIPv6PayloadHeader() {}
|
||||
|
||||
// IPv6DestinationOptionsExtHdr is a buffer holding the Destination Options
|
||||
// extension header.
|
||||
type IPv6DestinationOptionsExtHdr struct {
|
||||
ipv6OptionsExtHdr
|
||||
}
|
||||
|
||||
// isIPv6PayloadHeader implements IPv6PayloadHeader.isIPv6PayloadHeader.
|
||||
func (IPv6DestinationOptionsExtHdr) isIPv6PayloadHeader() {}
|
||||
|
||||
// IPv6RoutingExtHdr is a buffer holding the Routing extension header specific
|
||||
// data as outlined in RFC 8200 section 4.4.
|
||||
type IPv6RoutingExtHdr struct {
|
||||
Buf *buffer.View
|
||||
}
|
||||
|
||||
// isIPv6PayloadHeader implements IPv6PayloadHeader.isIPv6PayloadHeader.
|
||||
func (IPv6RoutingExtHdr) isIPv6PayloadHeader() {}
|
||||
|
||||
// Release implements IPv6PayloadHeader.Release.
|
||||
func (b IPv6RoutingExtHdr) Release() {
|
||||
b.Buf.Release()
|
||||
}
|
||||
|
||||
// SegmentsLeft returns the Segments Left field.
|
||||
func (b IPv6RoutingExtHdr) SegmentsLeft() uint8 {
|
||||
return b.Buf.AsSlice()[ipv6RoutingExtHdrSegmentsLeftIdx]
|
||||
}
|
||||
|
||||
// IPv6FragmentExtHdr is a buffer holding the Fragment extension header specific
|
||||
// data as outlined in RFC 8200 section 4.5.
|
||||
//
|
||||
// Note, the buffer does not include the Next Header and Reserved fields.
|
||||
type IPv6FragmentExtHdr [6]byte
|
||||
|
||||
// isIPv6PayloadHeader implements IPv6PayloadHeader.isIPv6PayloadHeader.
|
||||
func (IPv6FragmentExtHdr) isIPv6PayloadHeader() {}
|
||||
|
||||
// Release implements IPv6PayloadHeader.Release.
|
||||
func (IPv6FragmentExtHdr) Release() {}
|
||||
|
||||
// FragmentOffset returns the Fragment Offset field.
|
||||
//
|
||||
// This value indicates where the buffer following the Fragment extension header
|
||||
// starts in the target (reassembled) packet.
|
||||
func (b IPv6FragmentExtHdr) FragmentOffset() uint16 {
|
||||
return binary.BigEndian.Uint16(b[ipv6FragmentExtHdrFragmentOffsetOffset:]) >> ipv6FragmentExtHdrFragmentOffsetShift
|
||||
}
|
||||
|
||||
// More returns the More (M) flag.
|
||||
//
|
||||
// This indicates whether any fragments are expected to succeed b.
|
||||
func (b IPv6FragmentExtHdr) More() bool {
|
||||
return b[ipv6FragmentExtHdrFlagsIdx]&ipv6FragmentExtHdrMFlagMask != 0
|
||||
}
|
||||
|
||||
// ID returns the Identification field.
|
||||
//
|
||||
// This value is used to uniquely identify the packet, between a
|
||||
// source and destination.
|
||||
func (b IPv6FragmentExtHdr) ID() uint32 {
|
||||
return binary.BigEndian.Uint32(b[ipv6FragmentExtHdrIdentificationOffset:])
|
||||
}
|
||||
|
||||
// IsAtomic returns whether the fragment header indicates an atomic fragment. An
|
||||
// atomic fragment is a fragment that contains all the data required to
|
||||
// reassemble a full packet.
|
||||
func (b IPv6FragmentExtHdr) IsAtomic() bool {
|
||||
return !b.More() && b.FragmentOffset() == 0
|
||||
}
|
||||
|
||||
// IPv6PayloadIterator is an iterator over the contents of an IPv6 payload.
|
||||
//
|
||||
// The IPv6 payload may contain IPv6 extension headers before any upper layer
|
||||
// data.
|
||||
//
|
||||
// Note, between when an IPv6PayloadIterator is obtained and last used, no
|
||||
// changes to the payload may happen. Doing so may cause undefined and
|
||||
// unexpected behaviour. It is fine to obtain an IPv6PayloadIterator, iterate
|
||||
// over the first few headers then modify the backing payload so long as the
|
||||
// IPv6PayloadIterator obtained before modification is no longer used.
|
||||
type IPv6PayloadIterator struct {
|
||||
// The identifier of the next header to parse.
|
||||
nextHdrIdentifier IPv6ExtensionHeaderIdentifier
|
||||
|
||||
payload buffer.Buffer
|
||||
|
||||
// Indicates to the iterator that it should return the remaining payload as a
|
||||
// raw payload on the next call to Next.
|
||||
forceRaw bool
|
||||
|
||||
// headerOffset is the offset of the beginning of the current extension
|
||||
// header starting from the beginning of the fixed header.
|
||||
headerOffset uint32
|
||||
|
||||
// parseOffset is the byte offset into the current extension header of the
|
||||
// field we are currently examining. It can be added to the header offset
|
||||
// if the absolute offset within the packet is required.
|
||||
parseOffset uint32
|
||||
|
||||
// nextOffset is the offset of the next header.
|
||||
nextOffset uint32
|
||||
}
|
||||
|
||||
// HeaderOffset returns the offset to the start of the extension
|
||||
// header most recently processed.
|
||||
func (i IPv6PayloadIterator) HeaderOffset() uint32 {
|
||||
return i.headerOffset
|
||||
}
|
||||
|
||||
// ParseOffset returns the number of bytes successfully parsed.
|
||||
func (i IPv6PayloadIterator) ParseOffset() uint32 {
|
||||
return i.headerOffset + i.parseOffset
|
||||
}
|
||||
|
||||
// MakeIPv6PayloadIterator returns an iterator over the IPv6 payload containing
|
||||
// extension headers, or a raw payload if the payload cannot be parsed. The
|
||||
// iterator takes ownership of the payload.
|
||||
func MakeIPv6PayloadIterator(nextHdrIdentifier IPv6ExtensionHeaderIdentifier, payload buffer.Buffer) IPv6PayloadIterator {
|
||||
return IPv6PayloadIterator{
|
||||
nextHdrIdentifier: nextHdrIdentifier,
|
||||
payload: payload,
|
||||
nextOffset: IPv6FixedHeaderSize,
|
||||
}
|
||||
}
|
||||
|
||||
// Release frees the resources owned by the iterator.
|
||||
func (i *IPv6PayloadIterator) Release() {
|
||||
i.payload.Release()
|
||||
}
|
||||
|
||||
// AsRawHeader returns the remaining payload of i as a raw header and
|
||||
// optionally consumes the iterator.
|
||||
//
|
||||
// If consume is true, calls to Next after calling AsRawHeader on i will
|
||||
// indicate that the iterator is done. The returned header takes ownership of
|
||||
// its payload.
|
||||
func (i *IPv6PayloadIterator) AsRawHeader(consume bool) IPv6RawPayloadHeader {
|
||||
identifier := i.nextHdrIdentifier
|
||||
|
||||
var buf buffer.Buffer
|
||||
if consume {
|
||||
// Since we consume the iterator, we return the payload as is.
|
||||
buf = i.payload
|
||||
|
||||
// Mark i as done, but keep track of where we were for error reporting.
|
||||
*i = IPv6PayloadIterator{
|
||||
nextHdrIdentifier: IPv6NoNextHeaderIdentifier,
|
||||
headerOffset: i.headerOffset,
|
||||
nextOffset: i.nextOffset,
|
||||
}
|
||||
} else {
|
||||
buf = i.payload.Clone()
|
||||
}
|
||||
|
||||
return IPv6RawPayloadHeader{Identifier: identifier, Buf: buf}
|
||||
}
|
||||
|
||||
// Next returns the next item in the payload.
|
||||
//
|
||||
// If the next item is not a known IPv6 extension header, IPv6RawPayloadHeader
|
||||
// will be returned with the remaining bytes and next header identifier.
|
||||
//
|
||||
// The return is of the format (header, done, error). done will be true when
|
||||
// Next is unable to return anything because the iterator has reached the end of
|
||||
// the payload, or an error occurred.
|
||||
func (i *IPv6PayloadIterator) Next() (IPv6PayloadHeader, bool, error) {
|
||||
i.headerOffset = i.nextOffset
|
||||
i.parseOffset = 0
|
||||
// We could be forced to return i as a raw header when the previous header was
|
||||
// a fragment extension header as the data following the fragment extension
|
||||
// header may not be complete.
|
||||
if i.forceRaw {
|
||||
return i.AsRawHeader(true /* consume */), false, nil
|
||||
}
|
||||
|
||||
// Is the header we are parsing a known extension header?
|
||||
switch i.nextHdrIdentifier {
|
||||
case IPv6HopByHopOptionsExtHdrIdentifier:
|
||||
nextHdrIdentifier, view, err := i.nextHeaderData(false /* fragmentHdr */, nil)
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
|
||||
i.nextHdrIdentifier = nextHdrIdentifier
|
||||
return IPv6HopByHopOptionsExtHdr{ipv6OptionsExtHdr{view}}, false, nil
|
||||
case IPv6RoutingExtHdrIdentifier:
|
||||
nextHdrIdentifier, view, err := i.nextHeaderData(false /* fragmentHdr */, nil)
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
|
||||
i.nextHdrIdentifier = nextHdrIdentifier
|
||||
return IPv6RoutingExtHdr{view}, false, nil
|
||||
case IPv6FragmentExtHdrIdentifier:
|
||||
var data [6]byte
|
||||
// We ignore the returned bytes because we know the fragment extension
|
||||
// header specific data will fit in data.
|
||||
nextHdrIdentifier, _, err := i.nextHeaderData(true /* fragmentHdr */, data[:])
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
|
||||
fragmentExtHdr := IPv6FragmentExtHdr(data)
|
||||
|
||||
// If the packet is not the first fragment, do not attempt to parse anything
|
||||
// after the fragment extension header as the payload following the fragment
|
||||
// extension header should not contain any headers; the first fragment must
|
||||
// hold all the headers up to and including any upper layer headers, as per
|
||||
// RFC 8200 section 4.5.
|
||||
if fragmentExtHdr.FragmentOffset() != 0 {
|
||||
i.forceRaw = true
|
||||
}
|
||||
|
||||
i.nextHdrIdentifier = nextHdrIdentifier
|
||||
return fragmentExtHdr, false, nil
|
||||
case IPv6DestinationOptionsExtHdrIdentifier:
|
||||
nextHdrIdentifier, view, err := i.nextHeaderData(false /* fragmentHdr */, nil)
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
|
||||
i.nextHdrIdentifier = nextHdrIdentifier
|
||||
return IPv6DestinationOptionsExtHdr{ipv6OptionsExtHdr{view}}, false, nil
|
||||
case IPv6NoNextHeaderIdentifier:
|
||||
// This indicates the end of the IPv6 payload.
|
||||
return nil, true, nil
|
||||
|
||||
default:
|
||||
// The header we are parsing is not a known extension header. Return the
|
||||
// raw payload.
|
||||
return i.AsRawHeader(true /* consume */), false, nil
|
||||
}
|
||||
}
|
||||
|
||||
// NextHeaderIdentifier returns the identifier of the header next returned by
|
||||
// it.Next().
|
||||
func (i *IPv6PayloadIterator) NextHeaderIdentifier() IPv6ExtensionHeaderIdentifier {
|
||||
return i.nextHdrIdentifier
|
||||
}
|
||||
|
||||
// nextHeaderData returns the extension header's Next Header field and raw data.
|
||||
//
|
||||
// fragmentHdr indicates that the extension header being parsed is the Fragment
|
||||
// extension header so the Length field should be ignored as it is Reserved
|
||||
// for the Fragment extension header.
|
||||
//
|
||||
// If bytes is not nil, extension header specific data will be read into bytes
|
||||
// if it has enough capacity. If bytes is provided but does not have enough
|
||||
// capacity for the data, nextHeaderData will panic.
|
||||
func (i *IPv6PayloadIterator) nextHeaderData(fragmentHdr bool, bytes []byte) (IPv6ExtensionHeaderIdentifier, *buffer.View, error) {
|
||||
// We ignore the number of bytes read because we know we will only ever read
|
||||
// at max 1 bytes since rune has a length of 1. If we read 0 bytes, the Read
|
||||
// would return io.EOF to indicate that io.Reader has reached the end of the
|
||||
// payload.
|
||||
rdr := i.payload.AsBufferReader()
|
||||
nextHdrIdentifier, err := rdr.ReadByte()
|
||||
if err != nil {
|
||||
return 0, nil, fmt.Errorf("error when reading the Next Header field for extension header with id = %d: %w", i.nextHdrIdentifier, err)
|
||||
}
|
||||
i.parseOffset++
|
||||
|
||||
var length uint8
|
||||
length, err = rdr.ReadByte()
|
||||
if err != nil {
|
||||
if fragmentHdr {
|
||||
return 0, nil, fmt.Errorf("error when reading the Length field for extension header with id = %d: %w", i.nextHdrIdentifier, err)
|
||||
}
|
||||
|
||||
return 0, nil, fmt.Errorf("error when reading the Reserved field for extension header with id = %d: %w", i.nextHdrIdentifier, err)
|
||||
}
|
||||
if fragmentHdr {
|
||||
length = 0
|
||||
}
|
||||
|
||||
// Make parseOffset point to the first byte of the Extension Header
|
||||
// specific data.
|
||||
i.parseOffset++
|
||||
|
||||
// length is in 8 byte chunks but doesn't include the first one.
|
||||
// See RFC 8200 for each header type, sections 4.3-4.6 and the requirement
|
||||
// in section 4.8 for new extension headers at the top of page 24.
|
||||
// [ Hdr Ext Len ] ... Length of the Destination Options header in 8-octet
|
||||
// units, not including the first 8 octets.
|
||||
i.nextOffset += uint32((length + 1) * ipv6ExtHdrLenBytesPerUnit)
|
||||
|
||||
bytesLen := int(length)*ipv6ExtHdrLenBytesPerUnit + ipv6ExtHdrLenBytesExcluded
|
||||
if fragmentHdr {
|
||||
if n := len(bytes); n < bytesLen {
|
||||
panic(fmt.Sprintf("bytes only has space for %d bytes but need space for %d bytes (length = %d) for extension header with id = %d", n, bytesLen, length, i.nextHdrIdentifier))
|
||||
}
|
||||
if n, err := io.ReadFull(&rdr, bytes); err != nil {
|
||||
return 0, nil, fmt.Errorf("read %d out of %d extension header data bytes (length = %d) for header with id = %d: %w", n, bytesLen, length, i.nextHdrIdentifier, err)
|
||||
}
|
||||
return IPv6ExtensionHeaderIdentifier(nextHdrIdentifier), nil, nil
|
||||
}
|
||||
v := buffer.NewView(bytesLen)
|
||||
if n, err := io.CopyN(v, &rdr, int64(bytesLen)); err != nil {
|
||||
if err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
v.Release()
|
||||
return 0, nil, fmt.Errorf("read %d out of %d extension header data bytes (length = %d) for header with id = %d: %w", n, bytesLen, length, i.nextHdrIdentifier, err)
|
||||
}
|
||||
return IPv6ExtensionHeaderIdentifier(nextHdrIdentifier), v, nil
|
||||
}
|
||||
|
||||
// IPv6SerializableExtHdr provides serialization for IPv6 extension
|
||||
// headers.
|
||||
type IPv6SerializableExtHdr interface {
|
||||
// identifier returns the assigned IPv6 header identifier for this extension
|
||||
// header.
|
||||
identifier() IPv6ExtensionHeaderIdentifier
|
||||
|
||||
// length returns the total serialized length in bytes of this extension
|
||||
// header, including the common next header and length fields.
|
||||
length() int
|
||||
|
||||
// serializeInto serializes the receiver into the provided byte
|
||||
// buffer and with the provided nextHeader value.
|
||||
//
|
||||
// Note, the caller MUST provide a byte buffer with size of at least
|
||||
// length. Implementers of this function may assume that the byte buffer
|
||||
// is of sufficient size. serializeInto MAY panic if the provided byte
|
||||
// buffer is not of sufficient size.
|
||||
//
|
||||
// serializeInto returns the number of bytes that was used to serialize the
|
||||
// receiver. Implementers must only use the number of bytes required to
|
||||
// serialize the receiver. Callers MAY provide a larger buffer than required
|
||||
// to serialize into.
|
||||
serializeInto(nextHeader uint8, b []byte) int
|
||||
}
|
||||
|
||||
var _ IPv6SerializableExtHdr = (*IPv6SerializableHopByHopExtHdr)(nil)
|
||||
|
||||
// IPv6SerializableHopByHopExtHdr implements serialization of the Hop by Hop
|
||||
// options extension header.
|
||||
type IPv6SerializableHopByHopExtHdr []IPv6SerializableHopByHopOption
|
||||
|
||||
const (
|
||||
// ipv6HopByHopExtHdrNextHeaderOffset is the offset of the next header field
|
||||
// in a hop by hop extension header as defined in RFC 8200 section 4.3.
|
||||
ipv6HopByHopExtHdrNextHeaderOffset = 0
|
||||
|
||||
// ipv6HopByHopExtHdrLengthOffset is the offset of the length field in a hop
|
||||
// by hop extension header as defined in RFC 8200 section 4.3.
|
||||
ipv6HopByHopExtHdrLengthOffset = 1
|
||||
|
||||
// ipv6HopByHopExtHdrPayloadOffset is the offset of the options in a hop by
|
||||
// hop extension header as defined in RFC 8200 section 4.3.
|
||||
ipv6HopByHopExtHdrOptionsOffset = 2
|
||||
|
||||
// ipv6HopByHopExtHdrUnaccountedLenWords is the implicit number of 8-octet
|
||||
// words in a hop by hop extension header's length field, as stated in RFC
|
||||
// 8200 section 4.3:
|
||||
// Length of the Hop-by-Hop Options header in 8-octet units,
|
||||
// not including the first 8 octets.
|
||||
ipv6HopByHopExtHdrUnaccountedLenWords = 1
|
||||
)
|
||||
|
||||
// identifier implements IPv6SerializableExtHdr.
|
||||
func (IPv6SerializableHopByHopExtHdr) identifier() IPv6ExtensionHeaderIdentifier {
|
||||
return IPv6HopByHopOptionsExtHdrIdentifier
|
||||
}
|
||||
|
||||
// length implements IPv6SerializableExtHdr.
|
||||
func (h IPv6SerializableHopByHopExtHdr) length() int {
|
||||
var total int
|
||||
for _, opt := range h {
|
||||
align, alignOffset := opt.alignment()
|
||||
total += ipv6OptionsAlignmentPadding(total, align, alignOffset)
|
||||
total += ipv6ExtHdrOptionPayloadOffset + int(opt.length())
|
||||
}
|
||||
// Account for next header and total length fields and add padding.
|
||||
return padIPv6OptionsLength(ipv6HopByHopExtHdrOptionsOffset + total)
|
||||
}
|
||||
|
||||
// serializeInto implements IPv6SerializableExtHdr.
|
||||
func (h IPv6SerializableHopByHopExtHdr) serializeInto(nextHeader uint8, b []byte) int {
|
||||
optBuffer := b[ipv6HopByHopExtHdrOptionsOffset:]
|
||||
totalLength := ipv6HopByHopExtHdrOptionsOffset
|
||||
for _, opt := range h {
|
||||
// Calculate alignment requirements and pad buffer if necessary.
|
||||
align, alignOffset := opt.alignment()
|
||||
padLen := ipv6OptionsAlignmentPadding(totalLength, align, alignOffset)
|
||||
if padLen != 0 {
|
||||
padIPv6Option(optBuffer[:padLen])
|
||||
totalLength += padLen
|
||||
optBuffer = optBuffer[padLen:]
|
||||
}
|
||||
|
||||
l := opt.serializeInto(optBuffer[ipv6ExtHdrOptionPayloadOffset:])
|
||||
optBuffer[ipv6ExtHdrOptionTypeOffset] = uint8(opt.identifier())
|
||||
optBuffer[ipv6ExtHdrOptionLengthOffset] = l
|
||||
l += ipv6ExtHdrOptionPayloadOffset
|
||||
totalLength += int(l)
|
||||
optBuffer = optBuffer[l:]
|
||||
}
|
||||
padded := padIPv6OptionsLength(totalLength)
|
||||
if padded != totalLength {
|
||||
padIPv6Option(optBuffer[:padded-totalLength])
|
||||
totalLength = padded
|
||||
}
|
||||
wordsLen := totalLength/ipv6ExtHdrLenBytesPerUnit - ipv6HopByHopExtHdrUnaccountedLenWords
|
||||
if wordsLen > math.MaxUint8 {
|
||||
panic(fmt.Sprintf("IPv6 hop by hop options too large: %d+1 64-bit words", wordsLen))
|
||||
}
|
||||
b[ipv6HopByHopExtHdrNextHeaderOffset] = nextHeader
|
||||
b[ipv6HopByHopExtHdrLengthOffset] = uint8(wordsLen)
|
||||
return totalLength
|
||||
}
|
||||
|
||||
// IPv6SerializableHopByHopOption provides serialization for hop by hop options.
|
||||
type IPv6SerializableHopByHopOption interface {
|
||||
// identifier returns the option identifier of this Hop by Hop option.
|
||||
identifier() IPv6ExtHdrOptionIdentifier
|
||||
|
||||
// length returns the *payload* size of the option (not considering the type
|
||||
// and length fields).
|
||||
length() uint8
|
||||
|
||||
// alignment returns the alignment requirements from this option.
|
||||
//
|
||||
// Alignment requirements take the form [align]n + offset as specified in
|
||||
// RFC 8200 section 4.2. The alignment requirement is on the offset between
|
||||
// the option type byte and the start of the hop by hop header.
|
||||
//
|
||||
// align must be a power of 2.
|
||||
alignment() (align int, offset int)
|
||||
|
||||
// serializeInto serializes the receiver into the provided byte
|
||||
// buffer.
|
||||
//
|
||||
// Note, the caller MUST provide a byte buffer with size of at least
|
||||
// length. Implementers of this function may assume that the byte buffer
|
||||
// is of sufficient size. serializeInto MAY panic if the provided byte
|
||||
// buffer is not of sufficient size.
|
||||
//
|
||||
// serializeInto will return the number of bytes that was used to
|
||||
// serialize the receiver. Implementers must only use the number of
|
||||
// bytes required to serialize the receiver. Callers MAY provide a
|
||||
// larger buffer than required to serialize into.
|
||||
serializeInto([]byte) uint8
|
||||
}
|
||||
|
||||
var _ IPv6SerializableHopByHopOption = (*IPv6RouterAlertOption)(nil)
|
||||
|
||||
// IPv6RouterAlertOption is the IPv6 Router alert Hop by Hop option defined in
|
||||
// RFC 2711 section 2.1.
|
||||
type IPv6RouterAlertOption struct {
|
||||
Value IPv6RouterAlertValue
|
||||
}
|
||||
|
||||
// IPv6RouterAlertValue is the payload of an IPv6 Router Alert option.
|
||||
type IPv6RouterAlertValue uint16
|
||||
|
||||
const (
|
||||
// IPv6RouterAlertMLD indicates a datagram containing a Multicast Listener
|
||||
// Discovery message as defined in RFC 2711 section 2.1.
|
||||
IPv6RouterAlertMLD IPv6RouterAlertValue = 0
|
||||
// IPv6RouterAlertRSVP indicates a datagram containing an RSVP message as
|
||||
// defined in RFC 2711 section 2.1.
|
||||
IPv6RouterAlertRSVP IPv6RouterAlertValue = 1
|
||||
// IPv6RouterAlertActiveNetworks indicates a datagram containing an Active
|
||||
// Networks message as defined in RFC 2711 section 2.1.
|
||||
IPv6RouterAlertActiveNetworks IPv6RouterAlertValue = 2
|
||||
|
||||
// ipv6RouterAlertPayloadLength is the length of the Router Alert payload
|
||||
// as defined in RFC 2711.
|
||||
ipv6RouterAlertPayloadLength = 2
|
||||
|
||||
// ipv6RouterAlertAlignmentRequirement is the alignment requirement for the
|
||||
// Router Alert option defined as 2n+0 in RFC 2711.
|
||||
ipv6RouterAlertAlignmentRequirement = 2
|
||||
|
||||
// ipv6RouterAlertAlignmentOffsetRequirement is the alignment offset
|
||||
// requirement for the Router Alert option defined as 2n+0 in RFC 2711 section
|
||||
// 2.1.
|
||||
ipv6RouterAlertAlignmentOffsetRequirement = 0
|
||||
)
|
||||
|
||||
// UnknownAction implements IPv6ExtHdrOption.
|
||||
func (*IPv6RouterAlertOption) UnknownAction() IPv6OptionUnknownAction {
|
||||
return ipv6UnknownActionFromIdentifier(ipv6RouterAlertHopByHopOptionIdentifier)
|
||||
}
|
||||
|
||||
// isIPv6ExtHdrOption implements IPv6ExtHdrOption.
|
||||
func (*IPv6RouterAlertOption) isIPv6ExtHdrOption() {}
|
||||
|
||||
// identifier implements IPv6SerializableHopByHopOption.
|
||||
func (*IPv6RouterAlertOption) identifier() IPv6ExtHdrOptionIdentifier {
|
||||
return ipv6RouterAlertHopByHopOptionIdentifier
|
||||
}
|
||||
|
||||
// length implements IPv6SerializableHopByHopOption.
|
||||
func (*IPv6RouterAlertOption) length() uint8 {
|
||||
return ipv6RouterAlertPayloadLength
|
||||
}
|
||||
|
||||
// alignment implements IPv6SerializableHopByHopOption.
|
||||
func (*IPv6RouterAlertOption) alignment() (int, int) {
|
||||
// From RFC 2711 section 2.1:
|
||||
// Alignment requirement: 2n+0.
|
||||
return ipv6RouterAlertAlignmentRequirement, ipv6RouterAlertAlignmentOffsetRequirement
|
||||
}
|
||||
|
||||
// serializeInto implements IPv6SerializableHopByHopOption.
|
||||
func (o *IPv6RouterAlertOption) serializeInto(b []byte) uint8 {
|
||||
binary.BigEndian.PutUint16(b, uint16(o.Value))
|
||||
return ipv6RouterAlertPayloadLength
|
||||
}
|
||||
|
||||
// IPv6ExtHdrSerializer provides serialization of IPv6 extension headers.
|
||||
type IPv6ExtHdrSerializer []IPv6SerializableExtHdr
|
||||
|
||||
// Serialize serializes the provided list of IPv6 extension headers into b.
|
||||
//
|
||||
// Note, b must be of sufficient size to hold all the headers in s. See
|
||||
// IPv6ExtHdrSerializer.Length for details on the getting the total size of a
|
||||
// serialized IPv6ExtHdrSerializer.
|
||||
//
|
||||
// Serialize may panic if b is not of sufficient size to hold all the options
|
||||
// in s.
|
||||
//
|
||||
// Serialize takes the transportProtocol value to be used as the last extension
|
||||
// header's Next Header value and returns the header identifier of the first
|
||||
// serialized extension header and the total serialized length.
|
||||
func (s IPv6ExtHdrSerializer) Serialize(transportProtocol tcpip.TransportProtocolNumber, b []byte) (uint8, int) {
|
||||
nextHeader := uint8(transportProtocol)
|
||||
if len(s) == 0 {
|
||||
return nextHeader, 0
|
||||
}
|
||||
var totalLength int
|
||||
for i, h := range s[:len(s)-1] {
|
||||
length := h.serializeInto(uint8(s[i+1].identifier()), b)
|
||||
b = b[length:]
|
||||
totalLength += length
|
||||
}
|
||||
totalLength += s[len(s)-1].serializeInto(nextHeader, b)
|
||||
return uint8(s[0].identifier()), totalLength
|
||||
}
|
||||
|
||||
// Length returns the total number of bytes required to serialize the extension
|
||||
// headers.
|
||||
func (s IPv6ExtHdrSerializer) Length() int {
|
||||
var totalLength int
|
||||
for _, h := range s {
|
||||
totalLength += h.length()
|
||||
}
|
||||
return totalLength
|
||||
}
|
||||
158
internal/gtcpip/header/ipv6_fragment.go
Normal file
158
internal/gtcpip/header/ipv6_fragment.go
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
// Copyright 2018 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package header
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/sagernet/sing-tun/internal/gtcpip"
|
||||
)
|
||||
|
||||
const (
|
||||
nextHdrFrag = 0
|
||||
fragOff = 2
|
||||
more = 3
|
||||
idV6 = 4
|
||||
)
|
||||
|
||||
var _ IPv6SerializableExtHdr = (*IPv6SerializableFragmentExtHdr)(nil)
|
||||
|
||||
// IPv6SerializableFragmentExtHdr is used to serialize an IPv6 fragment
|
||||
// extension header as defined in RFC 8200 section 4.5.
|
||||
type IPv6SerializableFragmentExtHdr struct {
|
||||
// FragmentOffset is the "fragment offset" field of an IPv6 fragment.
|
||||
FragmentOffset uint16
|
||||
|
||||
// M is the "more" field of an IPv6 fragment.
|
||||
M bool
|
||||
|
||||
// Identification is the "identification" field of an IPv6 fragment.
|
||||
Identification uint32
|
||||
}
|
||||
|
||||
// identifier implements IPv6SerializableFragmentExtHdr.
|
||||
func (h *IPv6SerializableFragmentExtHdr) identifier() IPv6ExtensionHeaderIdentifier {
|
||||
return IPv6FragmentHeader
|
||||
}
|
||||
|
||||
// length implements IPv6SerializableFragmentExtHdr.
|
||||
func (h *IPv6SerializableFragmentExtHdr) length() int {
|
||||
return IPv6FragmentHeaderSize
|
||||
}
|
||||
|
||||
// serializeInto implements IPv6SerializableFragmentExtHdr.
|
||||
func (h *IPv6SerializableFragmentExtHdr) serializeInto(nextHeader uint8, b []byte) int {
|
||||
// Prevent too many bounds checks.
|
||||
_ = b[IPv6FragmentHeaderSize:]
|
||||
binary.BigEndian.PutUint32(b[idV6:], h.Identification)
|
||||
binary.BigEndian.PutUint16(b[fragOff:], h.FragmentOffset<<ipv6FragmentExtHdrFragmentOffsetShift)
|
||||
b[nextHdrFrag] = nextHeader
|
||||
if h.M {
|
||||
b[more] |= ipv6FragmentExtHdrMFlagMask
|
||||
}
|
||||
return IPv6FragmentHeaderSize
|
||||
}
|
||||
|
||||
// IPv6Fragment represents an ipv6 fragment header stored in a byte array.
|
||||
// Most of the methods of IPv6Fragment access to the underlying slice without
|
||||
// checking the boundaries and could panic because of 'index out of range'.
|
||||
// Always call IsValid() to validate an instance of IPv6Fragment before using other methods.
|
||||
type IPv6Fragment []byte
|
||||
|
||||
const (
|
||||
// IPv6FragmentHeader header is the number used to specify that the next
|
||||
// header is a fragment header, per RFC 2460.
|
||||
IPv6FragmentHeader = 44
|
||||
|
||||
// IPv6FragmentHeaderSize is the size of the fragment header.
|
||||
IPv6FragmentHeaderSize = 8
|
||||
)
|
||||
|
||||
// IsValid performs basic validation on the fragment header.
|
||||
func (b IPv6Fragment) IsValid() bool {
|
||||
return len(b) >= IPv6FragmentHeaderSize
|
||||
}
|
||||
|
||||
// NextHeader returns the value of the "next header" field of the ipv6 fragment.
|
||||
func (b IPv6Fragment) NextHeader() uint8 {
|
||||
return b[nextHdrFrag]
|
||||
}
|
||||
|
||||
// FragmentOffset returns the "fragment offset" field of the ipv6 fragment.
|
||||
func (b IPv6Fragment) FragmentOffset() uint16 {
|
||||
return binary.BigEndian.Uint16(b[fragOff:]) >> 3
|
||||
}
|
||||
|
||||
// More returns the "more" field of the ipv6 fragment.
|
||||
func (b IPv6Fragment) More() bool {
|
||||
return b[more]&1 > 0
|
||||
}
|
||||
|
||||
// Payload implements Network.Payload.
|
||||
func (b IPv6Fragment) Payload() []byte {
|
||||
return b[IPv6FragmentHeaderSize:]
|
||||
}
|
||||
|
||||
// ID returns the value of the identifier field of the ipv6 fragment.
|
||||
func (b IPv6Fragment) ID() uint32 {
|
||||
return binary.BigEndian.Uint32(b[idV6:])
|
||||
}
|
||||
|
||||
// TransportProtocol implements Network.TransportProtocol.
|
||||
func (b IPv6Fragment) TransportProtocol() tcpip.TransportProtocolNumber {
|
||||
return tcpip.TransportProtocolNumber(b.NextHeader())
|
||||
}
|
||||
|
||||
// The functions below have been added only to satisfy the Network interface.
|
||||
|
||||
// Checksum is not supported by IPv6Fragment.
|
||||
func (b IPv6Fragment) Checksum() uint16 {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
// SourceAddress is not supported by IPv6Fragment.
|
||||
func (b IPv6Fragment) SourceAddress() tcpip.Address {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
// DestinationAddress is not supported by IPv6Fragment.
|
||||
func (b IPv6Fragment) DestinationAddress() tcpip.Address {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
// SetSourceAddress is not supported by IPv6Fragment.
|
||||
func (b IPv6Fragment) SetSourceAddress(tcpip.Address) {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
// SetDestinationAddress is not supported by IPv6Fragment.
|
||||
func (b IPv6Fragment) SetDestinationAddress(tcpip.Address) {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
// SetChecksum is not supported by IPv6Fragment.
|
||||
func (b IPv6Fragment) SetChecksum(uint16) {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
// TOS is not supported by IPv6Fragment.
|
||||
func (b IPv6Fragment) TOS() (uint8, uint32) {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
// SetTOS is not supported by IPv6Fragment.
|
||||
func (b IPv6Fragment) SetTOS(t uint8, l uint32) {
|
||||
panic("not supported")
|
||||
}
|
||||
110
internal/gtcpip/header/ndp_neighbor_advert.go
Normal file
110
internal/gtcpip/header/ndp_neighbor_advert.go
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
// Copyright 2019 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package header
|
||||
|
||||
import "github.com/sagernet/sing-tun/internal/gtcpip"
|
||||
|
||||
// NDPNeighborAdvert is an NDP Neighbor Advertisement message. It will
|
||||
// only contain the body of an ICMPv6 packet.
|
||||
//
|
||||
// See RFC 4861 section 4.4 for more details.
|
||||
type NDPNeighborAdvert []byte
|
||||
|
||||
const (
|
||||
// NDPNAMinimumSize is the minimum size of a valid NDP Neighbor
|
||||
// Advertisement message (body of an ICMPv6 packet).
|
||||
NDPNAMinimumSize = 20
|
||||
|
||||
// ndpNATargetAddressOffset is the start of the Target Address
|
||||
// field within an NDPNeighborAdvert.
|
||||
ndpNATargetAddressOffset = 4
|
||||
|
||||
// ndpNAOptionsOffset is the start of the NDP options in an
|
||||
// NDPNeighborAdvert.
|
||||
ndpNAOptionsOffset = ndpNATargetAddressOffset + IPv6AddressSize
|
||||
|
||||
// ndpNAFlagsOffset is the offset of the flags within an
|
||||
// NDPNeighborAdvert
|
||||
ndpNAFlagsOffset = 0
|
||||
|
||||
// ndpNARouterFlagMask is the mask of the Router Flag field in
|
||||
// the flags byte within in an NDPNeighborAdvert.
|
||||
ndpNARouterFlagMask = (1 << 7)
|
||||
|
||||
// ndpNASolicitedFlagMask is the mask of the Solicited Flag field in
|
||||
// the flags byte within in an NDPNeighborAdvert.
|
||||
ndpNASolicitedFlagMask = (1 << 6)
|
||||
|
||||
// ndpNAOverrideFlagMask is the mask of the Override Flag field in
|
||||
// the flags byte within in an NDPNeighborAdvert.
|
||||
ndpNAOverrideFlagMask = (1 << 5)
|
||||
)
|
||||
|
||||
// TargetAddress returns the value within the Target Address field.
|
||||
func (b NDPNeighborAdvert) TargetAddress() tcpip.Address {
|
||||
return tcpip.AddrFrom16Slice(b[ndpNATargetAddressOffset:][:IPv6AddressSize])
|
||||
}
|
||||
|
||||
// SetTargetAddress sets the value within the Target Address field.
|
||||
func (b NDPNeighborAdvert) SetTargetAddress(addr tcpip.Address) {
|
||||
copy(b[ndpNATargetAddressOffset:][:IPv6AddressSize], addr.AsSlice())
|
||||
}
|
||||
|
||||
// RouterFlag returns the value of the Router Flag field.
|
||||
func (b NDPNeighborAdvert) RouterFlag() bool {
|
||||
return b[ndpNAFlagsOffset]&ndpNARouterFlagMask != 0
|
||||
}
|
||||
|
||||
// SetRouterFlag sets the value in the Router Flag field.
|
||||
func (b NDPNeighborAdvert) SetRouterFlag(f bool) {
|
||||
if f {
|
||||
b[ndpNAFlagsOffset] |= ndpNARouterFlagMask
|
||||
} else {
|
||||
b[ndpNAFlagsOffset] &^= ndpNARouterFlagMask
|
||||
}
|
||||
}
|
||||
|
||||
// SolicitedFlag returns the value of the Solicited Flag field.
|
||||
func (b NDPNeighborAdvert) SolicitedFlag() bool {
|
||||
return b[ndpNAFlagsOffset]&ndpNASolicitedFlagMask != 0
|
||||
}
|
||||
|
||||
// SetSolicitedFlag sets the value in the Solicited Flag field.
|
||||
func (b NDPNeighborAdvert) SetSolicitedFlag(f bool) {
|
||||
if f {
|
||||
b[ndpNAFlagsOffset] |= ndpNASolicitedFlagMask
|
||||
} else {
|
||||
b[ndpNAFlagsOffset] &^= ndpNASolicitedFlagMask
|
||||
}
|
||||
}
|
||||
|
||||
// OverrideFlag returns the value of the Override Flag field.
|
||||
func (b NDPNeighborAdvert) OverrideFlag() bool {
|
||||
return b[ndpNAFlagsOffset]&ndpNAOverrideFlagMask != 0
|
||||
}
|
||||
|
||||
// SetOverrideFlag sets the value in the Override Flag field.
|
||||
func (b NDPNeighborAdvert) SetOverrideFlag(f bool) {
|
||||
if f {
|
||||
b[ndpNAFlagsOffset] |= ndpNAOverrideFlagMask
|
||||
} else {
|
||||
b[ndpNAFlagsOffset] &^= ndpNAOverrideFlagMask
|
||||
}
|
||||
}
|
||||
|
||||
// Options returns an NDPOptions of the options body.
|
||||
func (b NDPNeighborAdvert) Options() NDPOptions {
|
||||
return NDPOptions(b[ndpNAOptionsOffset:])
|
||||
}
|
||||
52
internal/gtcpip/header/ndp_neighbor_solicit.go
Normal file
52
internal/gtcpip/header/ndp_neighbor_solicit.go
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
// Copyright 2019 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package header
|
||||
|
||||
import "github.com/sagernet/sing-tun/internal/gtcpip"
|
||||
|
||||
// NDPNeighborSolicit is an NDP Neighbor Solicitation message. It will only
|
||||
// contain the body of an ICMPv6 packet.
|
||||
//
|
||||
// See RFC 4861 section 4.3 for more details.
|
||||
type NDPNeighborSolicit []byte
|
||||
|
||||
const (
|
||||
// NDPNSMinimumSize is the minimum size of a valid NDP Neighbor
|
||||
// Solicitation message (body of an ICMPv6 packet).
|
||||
NDPNSMinimumSize = 20
|
||||
|
||||
// ndpNSTargetAddessOffset is the start of the Target Address
|
||||
// field within an NDPNeighborSolicit.
|
||||
ndpNSTargetAddessOffset = 4
|
||||
|
||||
// ndpNSOptionsOffset is the start of the NDP options in an
|
||||
// NDPNeighborSolicit.
|
||||
ndpNSOptionsOffset = ndpNSTargetAddessOffset + IPv6AddressSize
|
||||
)
|
||||
|
||||
// TargetAddress returns the value within the Target Address field.
|
||||
func (b NDPNeighborSolicit) TargetAddress() tcpip.Address {
|
||||
return tcpip.AddrFrom16Slice(b[ndpNSTargetAddessOffset:][:IPv6AddressSize])
|
||||
}
|
||||
|
||||
// SetTargetAddress sets the value within the Target Address field.
|
||||
func (b NDPNeighborSolicit) SetTargetAddress(addr tcpip.Address) {
|
||||
copy(b[ndpNSTargetAddessOffset:][:IPv6AddressSize], addr.AsSlice())
|
||||
}
|
||||
|
||||
// Options returns an NDPOptions of the options body.
|
||||
func (b NDPNeighborSolicit) Options() NDPOptions {
|
||||
return NDPOptions(b[ndpNSOptionsOffset:])
|
||||
}
|
||||
1073
internal/gtcpip/header/ndp_options.go
Normal file
1073
internal/gtcpip/header/ndp_options.go
Normal file
File diff suppressed because it is too large
Load diff
204
internal/gtcpip/header/ndp_router_advert.go
Normal file
204
internal/gtcpip/header/ndp_router_advert.go
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
// Copyright 2019 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package header
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
var _ fmt.Stringer = NDPRoutePreference(0)
|
||||
|
||||
// NDPRoutePreference is the preference values for default routers or
|
||||
// more-specific routes.
|
||||
//
|
||||
// As per RFC 4191 section 2.1,
|
||||
//
|
||||
// Default router preferences and preferences for more-specific routes
|
||||
// are encoded the same way.
|
||||
//
|
||||
// Preference values are encoded as a two-bit signed integer, as
|
||||
// follows:
|
||||
//
|
||||
// 01 High
|
||||
// 00 Medium (default)
|
||||
// 11 Low
|
||||
// 10 Reserved - MUST NOT be sent
|
||||
//
|
||||
// Note that implementations can treat the value as a two-bit signed
|
||||
// integer.
|
||||
//
|
||||
// Having just three values reinforces that they are not metrics and
|
||||
// more values do not appear to be necessary for reasonable scenarios.
|
||||
type NDPRoutePreference uint8
|
||||
|
||||
const (
|
||||
// HighRoutePreference indicates a high preference, as per
|
||||
// RFC 4191 section 2.1.
|
||||
HighRoutePreference NDPRoutePreference = 0b01
|
||||
|
||||
// MediumRoutePreference indicates a medium preference, as per
|
||||
// RFC 4191 section 2.1.
|
||||
//
|
||||
// This is the default preference value.
|
||||
MediumRoutePreference = 0b00
|
||||
|
||||
// LowRoutePreference indicates a low preference, as per
|
||||
// RFC 4191 section 2.1.
|
||||
LowRoutePreference = 0b11
|
||||
|
||||
// ReservedRoutePreference is a reserved preference value, as per
|
||||
// RFC 4191 section 2.1.
|
||||
//
|
||||
// It MUST NOT be sent.
|
||||
ReservedRoutePreference = 0b10
|
||||
)
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
func (p NDPRoutePreference) String() string {
|
||||
switch p {
|
||||
case HighRoutePreference:
|
||||
return "HighRoutePreference"
|
||||
case MediumRoutePreference:
|
||||
return "MediumRoutePreference"
|
||||
case LowRoutePreference:
|
||||
return "LowRoutePreference"
|
||||
case ReservedRoutePreference:
|
||||
return "ReservedRoutePreference"
|
||||
default:
|
||||
return fmt.Sprintf("NDPRoutePreference(%d)", p)
|
||||
}
|
||||
}
|
||||
|
||||
// NDPRouterAdvert is an NDP Router Advertisement message. It will only contain
|
||||
// the body of an ICMPv6 packet.
|
||||
//
|
||||
// See RFC 4861 section 4.2 and RFC 4191 section 2.2 for more details.
|
||||
type NDPRouterAdvert []byte
|
||||
|
||||
// As per RFC 4191 section 2.2,
|
||||
//
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Type | Code | Checksum |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Cur Hop Limit |M|O|H|Prf|Resvd| Router Lifetime |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Reachable Time |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Retrans Timer |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Options ...
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-
|
||||
const (
|
||||
// NDPRAMinimumSize is the minimum size of a valid NDP Router
|
||||
// Advertisement message (body of an ICMPv6 packet).
|
||||
NDPRAMinimumSize = 12
|
||||
|
||||
// ndpRACurrHopLimitOffset is the byte of the Curr Hop Limit field
|
||||
// within an NDPRouterAdvert.
|
||||
ndpRACurrHopLimitOffset = 0
|
||||
|
||||
// ndpRAFlagsOffset is the byte with the NDP RA bit-fields/flags
|
||||
// within an NDPRouterAdvert.
|
||||
ndpRAFlagsOffset = 1
|
||||
|
||||
// ndpRAManagedAddrConfFlagMask is the mask of the Managed Address
|
||||
// Configuration flag within the bit-field/flags byte of an
|
||||
// NDPRouterAdvert.
|
||||
ndpRAManagedAddrConfFlagMask = (1 << 7)
|
||||
|
||||
// ndpRAOtherConfFlagMask is the mask of the Other Configuration flag
|
||||
// within the bit-field/flags byte of an NDPRouterAdvert.
|
||||
ndpRAOtherConfFlagMask = (1 << 6)
|
||||
|
||||
// ndpDefaultRouterPreferenceShift is the shift of the Prf (Default Router
|
||||
// Preference) field within the flags byte of an NDPRouterAdvert.
|
||||
ndpDefaultRouterPreferenceShift = 3
|
||||
|
||||
// ndpDefaultRouterPreferenceMask is the mask of the Prf (Default Router
|
||||
// Preference) field within the flags byte of an NDPRouterAdvert.
|
||||
ndpDefaultRouterPreferenceMask = (0b11 << ndpDefaultRouterPreferenceShift)
|
||||
|
||||
// ndpRARouterLifetimeOffset is the start of the 2-byte Router Lifetime
|
||||
// field within an NDPRouterAdvert.
|
||||
ndpRARouterLifetimeOffset = 2
|
||||
|
||||
// ndpRAReachableTimeOffset is the start of the 4-byte Reachable Time
|
||||
// field within an NDPRouterAdvert.
|
||||
ndpRAReachableTimeOffset = 4
|
||||
|
||||
// ndpRARetransTimerOffset is the start of the 4-byte Retrans Timer
|
||||
// field within an NDPRouterAdvert.
|
||||
ndpRARetransTimerOffset = 8
|
||||
|
||||
// ndpRAOptionsOffset is the start of the NDP options in an
|
||||
// NDPRouterAdvert.
|
||||
ndpRAOptionsOffset = 12
|
||||
)
|
||||
|
||||
// CurrHopLimit returns the value of the Curr Hop Limit field.
|
||||
func (b NDPRouterAdvert) CurrHopLimit() uint8 {
|
||||
return b[ndpRACurrHopLimitOffset]
|
||||
}
|
||||
|
||||
// ManagedAddrConfFlag returns the value of the Managed Address Configuration
|
||||
// flag.
|
||||
func (b NDPRouterAdvert) ManagedAddrConfFlag() bool {
|
||||
return b[ndpRAFlagsOffset]&ndpRAManagedAddrConfFlagMask != 0
|
||||
}
|
||||
|
||||
// OtherConfFlag returns the value of the Other Configuration flag.
|
||||
func (b NDPRouterAdvert) OtherConfFlag() bool {
|
||||
return b[ndpRAFlagsOffset]&ndpRAOtherConfFlagMask != 0
|
||||
}
|
||||
|
||||
// DefaultRouterPreference returns the Default Router Preference field.
|
||||
func (b NDPRouterAdvert) DefaultRouterPreference() NDPRoutePreference {
|
||||
return NDPRoutePreference((b[ndpRAFlagsOffset] & ndpDefaultRouterPreferenceMask) >> ndpDefaultRouterPreferenceShift)
|
||||
}
|
||||
|
||||
// RouterLifetime returns the lifetime associated with the default router. A
|
||||
// value of 0 means the source of the Router Advertisement is not a default
|
||||
// router and SHOULD NOT appear on the default router list. Note, a value of 0
|
||||
// only means that the router should not be used as a default router, it does
|
||||
// not apply to other information contained in the Router Advertisement.
|
||||
func (b NDPRouterAdvert) RouterLifetime() time.Duration {
|
||||
// The field is the time in seconds, as per RFC 4861 section 4.2.
|
||||
return time.Second * time.Duration(binary.BigEndian.Uint16(b[ndpRARouterLifetimeOffset:]))
|
||||
}
|
||||
|
||||
// ReachableTime returns the time that a node assumes a neighbor is reachable
|
||||
// after having received a reachability confirmation. A value of 0 means
|
||||
// that it is unspecified by the source of the Router Advertisement message.
|
||||
func (b NDPRouterAdvert) ReachableTime() time.Duration {
|
||||
// The field is the time in milliseconds, as per RFC 4861 section 4.2.
|
||||
return time.Millisecond * time.Duration(binary.BigEndian.Uint32(b[ndpRAReachableTimeOffset:]))
|
||||
}
|
||||
|
||||
// RetransTimer returns the time between retransmitted Neighbor Solicitation
|
||||
// messages. A value of 0 means that it is unspecified by the source of the
|
||||
// Router Advertisement message.
|
||||
func (b NDPRouterAdvert) RetransTimer() time.Duration {
|
||||
// The field is the time in milliseconds, as per RFC 4861 section 4.2.
|
||||
return time.Millisecond * time.Duration(binary.BigEndian.Uint32(b[ndpRARetransTimerOffset:]))
|
||||
}
|
||||
|
||||
// Options returns an NDPOptions of the options body.
|
||||
func (b NDPRouterAdvert) Options() NDPOptions {
|
||||
return NDPOptions(b[ndpRAOptionsOffset:])
|
||||
}
|
||||
36
internal/gtcpip/header/ndp_router_solicit.go
Normal file
36
internal/gtcpip/header/ndp_router_solicit.go
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright 2019 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package header
|
||||
|
||||
// NDPRouterSolicit is an NDP Router Solicitation message. It will only contain
|
||||
// the body of an ICMPv6 packet.
|
||||
//
|
||||
// See RFC 4861 section 4.1 for more details.
|
||||
type NDPRouterSolicit []byte
|
||||
|
||||
const (
|
||||
// NDPRSMinimumSize is the minimum size of a valid NDP Router
|
||||
// Solicitation message (body of an ICMPv6 packet).
|
||||
NDPRSMinimumSize = 4
|
||||
|
||||
// ndpRSOptionsOffset is the start of the NDP options in an
|
||||
// NDPRouterSolicit.
|
||||
ndpRSOptionsOffset = 4
|
||||
)
|
||||
|
||||
// Options returns an NDPOptions of the options body.
|
||||
func (b NDPRouterSolicit) Options() NDPOptions {
|
||||
return NDPOptions(b[ndpRSOptionsOffset:])
|
||||
}
|
||||
58
internal/gtcpip/header/ndpoptionidentifier_string.go
Normal file
58
internal/gtcpip/header/ndpoptionidentifier_string.go
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
// Copyright 2020 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Code generated by "stringer -type ndpOptionIdentifier"; DO NOT EDIT.
|
||||
|
||||
package header
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[ndpSourceLinkLayerAddressOptionType-1]
|
||||
_ = x[ndpTargetLinkLayerAddressOptionType-2]
|
||||
_ = x[ndpPrefixInformationType-3]
|
||||
_ = x[ndpNonceOptionType-14]
|
||||
_ = x[ndpRecursiveDNSServerOptionType-25]
|
||||
_ = x[ndpDNSSearchListOptionType-31]
|
||||
}
|
||||
|
||||
const (
|
||||
_ndpOptionIdentifier_name_0 = "ndpSourceLinkLayerAddressOptionTypendpTargetLinkLayerAddressOptionTypendpPrefixInformationType"
|
||||
_ndpOptionIdentifier_name_1 = "ndpNonceOptionType"
|
||||
_ndpOptionIdentifier_name_2 = "ndpRecursiveDNSServerOptionType"
|
||||
_ndpOptionIdentifier_name_3 = "ndpDNSSearchListOptionType"
|
||||
)
|
||||
|
||||
var (
|
||||
_ndpOptionIdentifier_index_0 = [...]uint8{0, 35, 70, 94}
|
||||
)
|
||||
|
||||
func (i ndpOptionIdentifier) String() string {
|
||||
switch {
|
||||
case 1 <= i && i <= 3:
|
||||
i -= 1
|
||||
return _ndpOptionIdentifier_name_0[_ndpOptionIdentifier_index_0[i]:_ndpOptionIdentifier_index_0[i+1]]
|
||||
case i == 14:
|
||||
return _ndpOptionIdentifier_name_1
|
||||
case i == 25:
|
||||
return _ndpOptionIdentifier_name_2
|
||||
case i == 31:
|
||||
return _ndpOptionIdentifier_name_3
|
||||
default:
|
||||
return "ndpOptionIdentifier(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
}
|
||||
35
internal/gtcpip/header/netip.go
Normal file
35
internal/gtcpip/header/netip.go
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
package header
|
||||
|
||||
import "net/netip"
|
||||
|
||||
func (b IPv4) SourceAddr() netip.Addr {
|
||||
return netip.AddrFrom4([4]byte(b[srcAddr : srcAddr+IPv4AddressSize]))
|
||||
}
|
||||
|
||||
func (b IPv4) DestinationAddr() netip.Addr {
|
||||
return netip.AddrFrom4([4]byte(b[dstAddr : dstAddr+IPv4AddressSize]))
|
||||
}
|
||||
|
||||
func (b IPv4) SetSourceAddr(addr netip.Addr) {
|
||||
copy(b[srcAddr:srcAddr+IPv4AddressSize], addr.AsSlice())
|
||||
}
|
||||
|
||||
func (b IPv4) SetDestinationAddr(addr netip.Addr) {
|
||||
copy(b[dstAddr:dstAddr+IPv4AddressSize], addr.AsSlice())
|
||||
}
|
||||
|
||||
func (b IPv6) SourceAddr() netip.Addr {
|
||||
return netip.AddrFrom16([16]byte(b[v6SrcAddr:][:IPv6AddressSize]))
|
||||
}
|
||||
|
||||
func (b IPv6) DestinationAddr() netip.Addr {
|
||||
return netip.AddrFrom16([16]byte(b[v6DstAddr:][:IPv6AddressSize]))
|
||||
}
|
||||
|
||||
func (b IPv6) SetSourceAddr(addr netip.Addr) {
|
||||
copy(b[v6SrcAddr:][:IPv6AddressSize], addr.AsSlice())
|
||||
}
|
||||
|
||||
func (b IPv6) SetDestinationAddr(addr netip.Addr) {
|
||||
copy(b[v6DstAddr:][:IPv6AddressSize], addr.AsSlice())
|
||||
}
|
||||
727
internal/gtcpip/header/tcp.go
Normal file
727
internal/gtcpip/header/tcp.go
Normal file
|
|
@ -0,0 +1,727 @@
|
|||
// Copyright 2018 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package header
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/sagernet/sing-tun/internal/gtcpip"
|
||||
"github.com/sagernet/sing-tun/internal/gtcpip/checksum"
|
||||
"github.com/sagernet/sing-tun/internal/gtcpip/seqnum"
|
||||
|
||||
"github.com/google/btree"
|
||||
)
|
||||
|
||||
// These constants are the offsets of the respective fields in the TCP header.
|
||||
const (
|
||||
TCPSrcPortOffset = 0
|
||||
TCPDstPortOffset = 2
|
||||
TCPSeqNumOffset = 4
|
||||
TCPAckNumOffset = 8
|
||||
TCPDataOffset = 12
|
||||
TCPFlagsOffset = 13
|
||||
TCPWinSizeOffset = 14
|
||||
TCPChecksumOffset = 16
|
||||
TCPUrgentPtrOffset = 18
|
||||
)
|
||||
|
||||
const (
|
||||
// MaxWndScale is maximum allowed window scaling, as described in
|
||||
// RFC 1323, section 2.3, page 11.
|
||||
MaxWndScale = 14
|
||||
|
||||
// TCPMaxSACKBlocks is the maximum number of SACK blocks that can
|
||||
// be encoded in a TCP option field.
|
||||
TCPMaxSACKBlocks = 4
|
||||
)
|
||||
|
||||
// TCPFlags is the dedicated type for TCP flags.
|
||||
type TCPFlags uint8
|
||||
|
||||
// Intersects returns true iff there are flags common to both f and o.
|
||||
func (f TCPFlags) Intersects(o TCPFlags) bool {
|
||||
return f&o != 0
|
||||
}
|
||||
|
||||
// Contains returns true iff all the flags in o are contained within f.
|
||||
func (f TCPFlags) Contains(o TCPFlags) bool {
|
||||
return f&o == o
|
||||
}
|
||||
|
||||
// String implements Stringer.String.
|
||||
func (f TCPFlags) String() string {
|
||||
flagsStr := []byte("FSRPAUEC")
|
||||
for i := range flagsStr {
|
||||
if f&(1<<uint(i)) == 0 {
|
||||
flagsStr[i] = ' '
|
||||
}
|
||||
}
|
||||
return string(flagsStr)
|
||||
}
|
||||
|
||||
// Flags that may be set in a TCP segment.
|
||||
const (
|
||||
TCPFlagFin TCPFlags = 1 << iota
|
||||
TCPFlagSyn
|
||||
TCPFlagRst
|
||||
TCPFlagPsh
|
||||
TCPFlagAck
|
||||
TCPFlagUrg
|
||||
TCPFlagEce
|
||||
TCPFlagCwr
|
||||
)
|
||||
|
||||
// Options that may be present in a TCP segment.
|
||||
const (
|
||||
TCPOptionEOL = 0
|
||||
TCPOptionNOP = 1
|
||||
TCPOptionMSS = 2
|
||||
TCPOptionWS = 3
|
||||
TCPOptionTS = 8
|
||||
TCPOptionSACKPermitted = 4
|
||||
TCPOptionSACK = 5
|
||||
)
|
||||
|
||||
// Option Lengths.
|
||||
const (
|
||||
TCPOptionMSSLength = 4
|
||||
TCPOptionTSLength = 10
|
||||
TCPOptionWSLength = 3
|
||||
TCPOptionSackPermittedLength = 2
|
||||
)
|
||||
|
||||
// TCPFields contains the fields of a TCP packet. It is used to describe the
|
||||
// fields of a packet that needs to be encoded.
|
||||
type TCPFields struct {
|
||||
// SrcPort is the "source port" field of a TCP packet.
|
||||
SrcPort uint16
|
||||
|
||||
// DstPort is the "destination port" field of a TCP packet.
|
||||
DstPort uint16
|
||||
|
||||
// SeqNum is the "sequence number" field of a TCP packet.
|
||||
SeqNum uint32
|
||||
|
||||
// AckNum is the "acknowledgement number" field of a TCP packet.
|
||||
AckNum uint32
|
||||
|
||||
// DataOffset is the "data offset" field of a TCP packet. It is the length of
|
||||
// the TCP header in bytes.
|
||||
DataOffset uint8
|
||||
|
||||
// Flags is the "flags" field of a TCP packet.
|
||||
Flags TCPFlags
|
||||
|
||||
// WindowSize is the "window size" field of a TCP packet.
|
||||
WindowSize uint16
|
||||
|
||||
// Checksum is the "checksum" field of a TCP packet.
|
||||
Checksum uint16
|
||||
|
||||
// UrgentPointer is the "urgent pointer" field of a TCP packet.
|
||||
UrgentPointer uint16
|
||||
}
|
||||
|
||||
// TCPSynOptions is used to return the parsed TCP Options in a syn
|
||||
// segment.
|
||||
//
|
||||
// +stateify savable
|
||||
type TCPSynOptions struct {
|
||||
// MSS is the maximum segment size provided by the peer in the SYN.
|
||||
MSS uint16
|
||||
|
||||
// WS is the window scale option provided by the peer in the SYN.
|
||||
//
|
||||
// Set to -1 if no window scale option was provided.
|
||||
WS int
|
||||
|
||||
// TS is true if the timestamp option was provided in the syn/syn-ack.
|
||||
TS bool
|
||||
|
||||
// TSVal is the value of the TSVal field in the timestamp option.
|
||||
TSVal uint32
|
||||
|
||||
// TSEcr is the value of the TSEcr field in the timestamp option.
|
||||
TSEcr uint32
|
||||
|
||||
// SACKPermitted is true if the SACK option was provided in the SYN/SYN-ACK.
|
||||
SACKPermitted bool
|
||||
|
||||
// Flags if specified are set on the outgoing SYN. The SYN flag is
|
||||
// always set.
|
||||
Flags TCPFlags
|
||||
}
|
||||
|
||||
// SACKBlock represents a single contiguous SACK block.
|
||||
//
|
||||
// +stateify savable
|
||||
type SACKBlock struct {
|
||||
// Start indicates the lowest sequence number in the block.
|
||||
Start seqnum.Value
|
||||
|
||||
// End indicates the sequence number immediately following the last
|
||||
// sequence number of this block.
|
||||
End seqnum.Value
|
||||
}
|
||||
|
||||
// Less returns true if r.Start < b.Start.
|
||||
func (r SACKBlock) Less(b btree.Item) bool {
|
||||
return r.Start.LessThan(b.(SACKBlock).Start)
|
||||
}
|
||||
|
||||
// Contains returns true if b is completely contained in r.
|
||||
func (r SACKBlock) Contains(b SACKBlock) bool {
|
||||
return r.Start.LessThanEq(b.Start) && b.End.LessThanEq(r.End)
|
||||
}
|
||||
|
||||
// TCPOptions are used to parse and cache the TCP segment options for a non
|
||||
// syn/syn-ack segment.
|
||||
//
|
||||
// +stateify savable
|
||||
type TCPOptions struct {
|
||||
// TS is true if the TimeStamp option is enabled.
|
||||
TS bool
|
||||
|
||||
// TSVal is the value in the TSVal field of the segment.
|
||||
TSVal uint32
|
||||
|
||||
// TSEcr is the value in the TSEcr field of the segment.
|
||||
TSEcr uint32
|
||||
|
||||
// SACKBlocks are the SACK blocks specified in the segment.
|
||||
SACKBlocks []SACKBlock
|
||||
}
|
||||
|
||||
// TCP represents a TCP header stored in a byte array.
|
||||
type TCP []byte
|
||||
|
||||
const (
|
||||
// TCPMinimumSize is the minimum size of a valid TCP packet.
|
||||
TCPMinimumSize = 20
|
||||
|
||||
// TCPOptionsMaximumSize is the maximum size of TCP options.
|
||||
TCPOptionsMaximumSize = 40
|
||||
|
||||
// TCPHeaderMaximumSize is the maximum header size of a TCP packet.
|
||||
TCPHeaderMaximumSize = TCPMinimumSize + TCPOptionsMaximumSize
|
||||
|
||||
// TCPTotalHeaderMaximumSize is the maximum size of headers from all layers in
|
||||
// a TCP packet. It analogous to MAX_TCP_HEADER in Linux.
|
||||
//
|
||||
// TODO(b/319936470): Investigate why this needs to be at least 140 bytes. In
|
||||
// Linux this value is at least 160, but in theory we should be able to use
|
||||
// 138. In practice anything less than 140 starts to break GSO on gVNIC
|
||||
// hardware.
|
||||
TCPTotalHeaderMaximumSize = 160
|
||||
|
||||
// TCPProtocolNumber is TCP's transport protocol number.
|
||||
TCPProtocolNumber tcpip.TransportProtocolNumber = 6
|
||||
|
||||
// TCPMinimumMSS is the minimum acceptable value for MSS. This is the
|
||||
// same as the value TCP_MIN_MSS defined net/tcp.h.
|
||||
TCPMinimumMSS = IPv4MaximumHeaderSize + TCPHeaderMaximumSize + MinIPFragmentPayloadSize - IPv4MinimumSize - TCPMinimumSize
|
||||
|
||||
// TCPMinimumSendMSS is the minimum value for MSS in a sender. This is the
|
||||
// same as the value TCP_MIN_SND_MSS in net/tcp.h.
|
||||
TCPMinimumSendMSS = TCPOptionsMaximumSize + MinIPFragmentPayloadSize
|
||||
|
||||
// TCPMaximumMSS is the maximum acceptable value for MSS.
|
||||
TCPMaximumMSS = 0xffff
|
||||
|
||||
// TCPDefaultMSS is the MSS value that should be used if an MSS option
|
||||
// is not received from the peer. It's also the value returned by
|
||||
// TCP_MAXSEG option for a socket in an unconnected state.
|
||||
//
|
||||
// Per RFC 1122, page 85: "If an MSS option is not received at
|
||||
// connection setup, TCP MUST assume a default send MSS of 536."
|
||||
TCPDefaultMSS = 536
|
||||
)
|
||||
|
||||
// SourcePort returns the "source port" field of the TCP header.
|
||||
func (b TCP) SourcePort() uint16 {
|
||||
return binary.BigEndian.Uint16(b[TCPSrcPortOffset:])
|
||||
}
|
||||
|
||||
// DestinationPort returns the "destination port" field of the TCP header.
|
||||
func (b TCP) DestinationPort() uint16 {
|
||||
return binary.BigEndian.Uint16(b[TCPDstPortOffset:])
|
||||
}
|
||||
|
||||
// SequenceNumber returns the "sequence number" field of the TCP header.
|
||||
func (b TCP) SequenceNumber() uint32 {
|
||||
return binary.BigEndian.Uint32(b[TCPSeqNumOffset:])
|
||||
}
|
||||
|
||||
// AckNumber returns the "ack number" field of the TCP header.
|
||||
func (b TCP) AckNumber() uint32 {
|
||||
return binary.BigEndian.Uint32(b[TCPAckNumOffset:])
|
||||
}
|
||||
|
||||
// DataOffset returns the "data offset" field of the TCP header. The return
|
||||
// value is the length of the TCP header in bytes.
|
||||
func (b TCP) DataOffset() uint8 {
|
||||
return (b[TCPDataOffset] >> 4) * 4
|
||||
}
|
||||
|
||||
// Payload returns the data in the TCP packet.
|
||||
func (b TCP) Payload() []byte {
|
||||
return b[b.DataOffset():]
|
||||
}
|
||||
|
||||
// Flags returns the flags field of the TCP header.
|
||||
func (b TCP) Flags() TCPFlags {
|
||||
return TCPFlags(b[TCPFlagsOffset])
|
||||
}
|
||||
|
||||
// WindowSize returns the "window size" field of the TCP header.
|
||||
func (b TCP) WindowSize() uint16 {
|
||||
return binary.BigEndian.Uint16(b[TCPWinSizeOffset:])
|
||||
}
|
||||
|
||||
// Checksum returns the "checksum" field of the TCP header.
|
||||
func (b TCP) Checksum() uint16 {
|
||||
return binary.BigEndian.Uint16(b[TCPChecksumOffset:])
|
||||
}
|
||||
|
||||
// UrgentPointer returns the "urgent pointer" field of the TCP header.
|
||||
func (b TCP) UrgentPointer() uint16 {
|
||||
return binary.BigEndian.Uint16(b[TCPUrgentPtrOffset:])
|
||||
}
|
||||
|
||||
// SetSourcePort sets the "source port" field of the TCP header.
|
||||
func (b TCP) SetSourcePort(port uint16) {
|
||||
binary.BigEndian.PutUint16(b[TCPSrcPortOffset:], port)
|
||||
}
|
||||
|
||||
// SetDestinationPort sets the "destination port" field of the TCP header.
|
||||
func (b TCP) SetDestinationPort(port uint16) {
|
||||
binary.BigEndian.PutUint16(b[TCPDstPortOffset:], port)
|
||||
}
|
||||
|
||||
// SetChecksum sets the checksum field of the TCP header.
|
||||
func (b TCP) SetChecksum(xsum uint16) {
|
||||
checksum.Put(b[TCPChecksumOffset:], xsum)
|
||||
}
|
||||
|
||||
// SetDataOffset sets the data offset field of the TCP header. headerLen should
|
||||
// be the length of the TCP header in bytes.
|
||||
func (b TCP) SetDataOffset(headerLen uint8) {
|
||||
b[TCPDataOffset] = (headerLen / 4) << 4
|
||||
}
|
||||
|
||||
// SetSequenceNumber sets the sequence number field of the TCP header.
|
||||
func (b TCP) SetSequenceNumber(seqNum uint32) {
|
||||
binary.BigEndian.PutUint32(b[TCPSeqNumOffset:], seqNum)
|
||||
}
|
||||
|
||||
// SetAckNumber sets the ack number field of the TCP header.
|
||||
func (b TCP) SetAckNumber(ackNum uint32) {
|
||||
binary.BigEndian.PutUint32(b[TCPAckNumOffset:], ackNum)
|
||||
}
|
||||
|
||||
// SetFlags sets the flags field of the TCP header.
|
||||
func (b TCP) SetFlags(flags uint8) {
|
||||
b[TCPFlagsOffset] = flags
|
||||
}
|
||||
|
||||
// SetWindowSize sets the window size field of the TCP header.
|
||||
func (b TCP) SetWindowSize(rcvwnd uint16) {
|
||||
binary.BigEndian.PutUint16(b[TCPWinSizeOffset:], rcvwnd)
|
||||
}
|
||||
|
||||
// SetUrgentPointer sets the window size field of the TCP header.
|
||||
func (b TCP) SetUrgentPointer(urgentPointer uint16) {
|
||||
binary.BigEndian.PutUint16(b[TCPUrgentPtrOffset:], urgentPointer)
|
||||
}
|
||||
|
||||
// CalculateChecksum calculates the checksum of the TCP segment.
|
||||
// partialChecksum is the checksum of the network-layer pseudo-header
|
||||
// and the checksum of the segment data.
|
||||
func (b TCP) CalculateChecksum(partialChecksum uint16) uint16 {
|
||||
// Calculate the rest of the checksum.
|
||||
return checksum.Checksum(b[:b.DataOffset()], partialChecksum)
|
||||
}
|
||||
|
||||
// IsChecksumValid returns true iff the TCP header's checksum is valid.
|
||||
func (b TCP) IsChecksumValid(src, dst tcpip.Address, payloadChecksum, payloadLength uint16) bool {
|
||||
xsum := PseudoHeaderChecksum(TCPProtocolNumber, src.AsSlice(), dst.AsSlice(), uint16(b.DataOffset())+payloadLength)
|
||||
xsum = checksum.Combine(xsum, payloadChecksum)
|
||||
return b.CalculateChecksum(xsum) == 0xffff
|
||||
}
|
||||
|
||||
// Options returns a slice that holds the unparsed TCP options in the segment.
|
||||
func (b TCP) Options() []byte {
|
||||
return b[TCPMinimumSize:b.DataOffset()]
|
||||
}
|
||||
|
||||
// ParsedOptions returns a TCPOptions structure which parses and caches the TCP
|
||||
// option values in the TCP segment. NOTE: Invoking this function repeatedly is
|
||||
// expensive as it reparses the options on each invocation.
|
||||
func (b TCP) ParsedOptions() TCPOptions {
|
||||
return ParseTCPOptions(b.Options())
|
||||
}
|
||||
|
||||
func (b TCP) encodeSubset(seq, ack uint32, flags TCPFlags, rcvwnd uint16) {
|
||||
binary.BigEndian.PutUint32(b[TCPSeqNumOffset:], seq)
|
||||
binary.BigEndian.PutUint32(b[TCPAckNumOffset:], ack)
|
||||
b[TCPFlagsOffset] = uint8(flags)
|
||||
binary.BigEndian.PutUint16(b[TCPWinSizeOffset:], rcvwnd)
|
||||
}
|
||||
|
||||
// Encode encodes all the fields of the TCP header.
|
||||
func (b TCP) Encode(t *TCPFields) {
|
||||
b.encodeSubset(t.SeqNum, t.AckNum, t.Flags, t.WindowSize)
|
||||
b.SetSourcePort(t.SrcPort)
|
||||
b.SetDestinationPort(t.DstPort)
|
||||
b.SetDataOffset(t.DataOffset)
|
||||
b.SetChecksum(t.Checksum)
|
||||
b.SetUrgentPointer(t.UrgentPointer)
|
||||
}
|
||||
|
||||
// EncodePartial updates a subset of the fields of the TCP header. It is useful
|
||||
// in cases when similar segments are produced.
|
||||
func (b TCP) EncodePartial(partialChecksum, length uint16, seqnum, acknum uint32, flags TCPFlags, rcvwnd uint16) {
|
||||
// Add the total length and "flags" field contributions to the checksum.
|
||||
// We don't use the flags field directly from the header because it's a
|
||||
// one-byte field with an odd offset, so it would be accounted for
|
||||
// incorrectly by the Checksum routine.
|
||||
tmp := make([]byte, 4)
|
||||
binary.BigEndian.PutUint16(tmp, length)
|
||||
binary.BigEndian.PutUint16(tmp[2:], uint16(flags))
|
||||
xsum := checksum.Checksum(tmp, partialChecksum)
|
||||
|
||||
// Encode the passed-in fields.
|
||||
b.encodeSubset(seqnum, acknum, flags, rcvwnd)
|
||||
|
||||
// Add the contributions of the passed-in fields to the checksum.
|
||||
xsum = checksum.Checksum(b[TCPSeqNumOffset:TCPSeqNumOffset+8], xsum)
|
||||
xsum = checksum.Checksum(b[TCPWinSizeOffset:TCPWinSizeOffset+2], xsum)
|
||||
|
||||
// Encode the checksum.
|
||||
b.SetChecksum(^xsum)
|
||||
}
|
||||
|
||||
// SetSourcePortWithChecksumUpdate implements ChecksummableTransport.
|
||||
func (b TCP) SetSourcePortWithChecksumUpdate(new uint16) {
|
||||
old := b.SourcePort()
|
||||
b.SetSourcePort(new)
|
||||
b.SetChecksum(^checksumUpdate2ByteAlignedUint16(^b.Checksum(), old, new))
|
||||
}
|
||||
|
||||
// SetDestinationPortWithChecksumUpdate implements ChecksummableTransport.
|
||||
func (b TCP) SetDestinationPortWithChecksumUpdate(new uint16) {
|
||||
old := b.DestinationPort()
|
||||
b.SetDestinationPort(new)
|
||||
b.SetChecksum(^checksumUpdate2ByteAlignedUint16(^b.Checksum(), old, new))
|
||||
}
|
||||
|
||||
// UpdateChecksumPseudoHeaderAddress implements ChecksummableTransport.
|
||||
func (b TCP) UpdateChecksumPseudoHeaderAddress(old, new tcpip.Address, fullChecksum bool) {
|
||||
xsum := b.Checksum()
|
||||
if fullChecksum {
|
||||
xsum = ^xsum
|
||||
}
|
||||
|
||||
xsum = checksumUpdate2ByteAlignedAddress(xsum, old, new)
|
||||
if fullChecksum {
|
||||
xsum = ^xsum
|
||||
}
|
||||
|
||||
b.SetChecksum(xsum)
|
||||
}
|
||||
|
||||
// ParseSynOptions parses the options received in a SYN segment and returns the
|
||||
// relevant ones. opts should point to the option part of the TCP header.
|
||||
func ParseSynOptions(opts []byte, isAck bool) TCPSynOptions {
|
||||
limit := len(opts)
|
||||
|
||||
synOpts := TCPSynOptions{
|
||||
// Per RFC 1122, page 85: "If an MSS option is not received at
|
||||
// connection setup, TCP MUST assume a default send MSS of 536."
|
||||
MSS: TCPDefaultMSS,
|
||||
// If no window scale option is specified, WS in options is
|
||||
// returned as -1; this is because the absence of the option
|
||||
// indicates that the we cannot use window scaling on the
|
||||
// receive end either.
|
||||
WS: -1,
|
||||
}
|
||||
|
||||
for i := 0; i < limit; {
|
||||
switch opts[i] {
|
||||
case TCPOptionEOL:
|
||||
i = limit
|
||||
case TCPOptionNOP:
|
||||
i++
|
||||
case TCPOptionMSS:
|
||||
if i+4 > limit || opts[i+1] != 4 {
|
||||
return synOpts
|
||||
}
|
||||
mss := uint16(opts[i+2])<<8 | uint16(opts[i+3])
|
||||
if mss == 0 {
|
||||
return synOpts
|
||||
}
|
||||
synOpts.MSS = mss
|
||||
if mss < TCPMinimumSendMSS {
|
||||
synOpts.MSS = TCPMinimumSendMSS
|
||||
}
|
||||
i += 4
|
||||
|
||||
case TCPOptionWS:
|
||||
if i+3 > limit || opts[i+1] != 3 {
|
||||
return synOpts
|
||||
}
|
||||
ws := int(opts[i+2])
|
||||
if ws > MaxWndScale {
|
||||
ws = MaxWndScale
|
||||
}
|
||||
synOpts.WS = ws
|
||||
i += 3
|
||||
|
||||
case TCPOptionTS:
|
||||
if i+10 > limit || opts[i+1] != 10 {
|
||||
return synOpts
|
||||
}
|
||||
synOpts.TSVal = binary.BigEndian.Uint32(opts[i+2:])
|
||||
if isAck {
|
||||
// If the segment is a SYN-ACK then store the Timestamp Echo Reply
|
||||
// in the segment.
|
||||
synOpts.TSEcr = binary.BigEndian.Uint32(opts[i+6:])
|
||||
}
|
||||
synOpts.TS = true
|
||||
i += 10
|
||||
case TCPOptionSACKPermitted:
|
||||
if i+2 > limit || opts[i+1] != 2 {
|
||||
return synOpts
|
||||
}
|
||||
synOpts.SACKPermitted = true
|
||||
i += 2
|
||||
|
||||
default:
|
||||
// We don't recognize this option, just skip over it.
|
||||
if i+2 > limit {
|
||||
return synOpts
|
||||
}
|
||||
l := int(opts[i+1])
|
||||
// If the length is incorrect or if l+i overflows the
|
||||
// total options length then return false.
|
||||
if l < 2 || i+l > limit {
|
||||
return synOpts
|
||||
}
|
||||
i += l
|
||||
}
|
||||
}
|
||||
|
||||
return synOpts
|
||||
}
|
||||
|
||||
// ParseTCPOptions extracts and stores all known options in the provided byte
|
||||
// slice in a TCPOptions structure.
|
||||
func ParseTCPOptions(b []byte) TCPOptions {
|
||||
opts := TCPOptions{}
|
||||
limit := len(b)
|
||||
for i := 0; i < limit; {
|
||||
switch b[i] {
|
||||
case TCPOptionEOL:
|
||||
i = limit
|
||||
case TCPOptionNOP:
|
||||
i++
|
||||
case TCPOptionTS:
|
||||
if i+10 > limit || (b[i+1] != 10) {
|
||||
return opts
|
||||
}
|
||||
opts.TS = true
|
||||
opts.TSVal = binary.BigEndian.Uint32(b[i+2:])
|
||||
opts.TSEcr = binary.BigEndian.Uint32(b[i+6:])
|
||||
i += 10
|
||||
case TCPOptionSACK:
|
||||
if i+2 > limit {
|
||||
// Malformed SACK block, just return and stop parsing.
|
||||
return opts
|
||||
}
|
||||
sackOptionLen := int(b[i+1])
|
||||
if i+sackOptionLen > limit || (sackOptionLen-2)%8 != 0 {
|
||||
// Malformed SACK block, just return and stop parsing.
|
||||
return opts
|
||||
}
|
||||
numBlocks := (sackOptionLen - 2) / 8
|
||||
opts.SACKBlocks = []SACKBlock{}
|
||||
for j := 0; j < numBlocks; j++ {
|
||||
start := binary.BigEndian.Uint32(b[i+2+j*8:])
|
||||
end := binary.BigEndian.Uint32(b[i+2+j*8+4:])
|
||||
opts.SACKBlocks = append(opts.SACKBlocks, SACKBlock{
|
||||
Start: seqnum.Value(start),
|
||||
End: seqnum.Value(end),
|
||||
})
|
||||
}
|
||||
i += sackOptionLen
|
||||
default:
|
||||
// We don't recognize this option, just skip over it.
|
||||
if i+2 > limit {
|
||||
return opts
|
||||
}
|
||||
l := int(b[i+1])
|
||||
// If the length is incorrect or if l+i overflows the
|
||||
// total options length then return false.
|
||||
if l < 2 || i+l > limit {
|
||||
return opts
|
||||
}
|
||||
i += l
|
||||
}
|
||||
}
|
||||
return opts
|
||||
}
|
||||
|
||||
// EncodeMSSOption encodes the MSS TCP option with the provided MSS values in
|
||||
// the supplied buffer. If the provided buffer is not large enough then it just
|
||||
// returns without encoding anything. It returns the number of bytes written to
|
||||
// the provided buffer.
|
||||
func EncodeMSSOption(mss uint32, b []byte) int {
|
||||
if len(b) < TCPOptionMSSLength {
|
||||
return 0
|
||||
}
|
||||
b[0], b[1], b[2], b[3] = TCPOptionMSS, TCPOptionMSSLength, byte(mss>>8), byte(mss)
|
||||
return TCPOptionMSSLength
|
||||
}
|
||||
|
||||
// EncodeWSOption encodes the WS TCP option with the WS value in the
|
||||
// provided buffer. If the provided buffer is not large enough then it just
|
||||
// returns without encoding anything. It returns the number of bytes written to
|
||||
// the provided buffer.
|
||||
func EncodeWSOption(ws int, b []byte) int {
|
||||
if len(b) < TCPOptionWSLength {
|
||||
return 0
|
||||
}
|
||||
b[0], b[1], b[2] = TCPOptionWS, TCPOptionWSLength, uint8(ws)
|
||||
return int(b[1])
|
||||
}
|
||||
|
||||
// EncodeTSOption encodes the provided tsVal and tsEcr values as a TCP timestamp
|
||||
// option into the provided buffer. If the buffer is smaller than expected it
|
||||
// just returns without encoding anything. It returns the number of bytes
|
||||
// written to the provided buffer.
|
||||
func EncodeTSOption(tsVal, tsEcr uint32, b []byte) int {
|
||||
if len(b) < TCPOptionTSLength {
|
||||
return 0
|
||||
}
|
||||
b[0], b[1] = TCPOptionTS, TCPOptionTSLength
|
||||
binary.BigEndian.PutUint32(b[2:], tsVal)
|
||||
binary.BigEndian.PutUint32(b[6:], tsEcr)
|
||||
return int(b[1])
|
||||
}
|
||||
|
||||
// EncodeSACKPermittedOption encodes a SACKPermitted option into the provided
|
||||
// buffer. If the buffer is smaller than required it just returns without
|
||||
// encoding anything. It returns the number of bytes written to the provided
|
||||
// buffer.
|
||||
func EncodeSACKPermittedOption(b []byte) int {
|
||||
if len(b) < TCPOptionSackPermittedLength {
|
||||
return 0
|
||||
}
|
||||
|
||||
b[0], b[1] = TCPOptionSACKPermitted, TCPOptionSackPermittedLength
|
||||
return int(b[1])
|
||||
}
|
||||
|
||||
// EncodeSACKBlocks encodes the provided SACK blocks as a TCP SACK option block
|
||||
// in the provided slice. It tries to fit in as many blocks as possible based on
|
||||
// number of bytes available in the provided buffer. It returns the number of
|
||||
// bytes written to the provided buffer.
|
||||
func EncodeSACKBlocks(sackBlocks []SACKBlock, b []byte) int {
|
||||
if len(sackBlocks) == 0 {
|
||||
return 0
|
||||
}
|
||||
l := len(sackBlocks)
|
||||
if l > TCPMaxSACKBlocks {
|
||||
l = TCPMaxSACKBlocks
|
||||
}
|
||||
if ll := (len(b) - 2) / 8; ll < l {
|
||||
l = ll
|
||||
}
|
||||
if l == 0 {
|
||||
// There is not enough space in the provided buffer to add
|
||||
// any SACK blocks.
|
||||
return 0
|
||||
}
|
||||
b[0] = TCPOptionSACK
|
||||
b[1] = byte(l*8 + 2)
|
||||
for i := 0; i < l; i++ {
|
||||
binary.BigEndian.PutUint32(b[i*8+2:], uint32(sackBlocks[i].Start))
|
||||
binary.BigEndian.PutUint32(b[i*8+6:], uint32(sackBlocks[i].End))
|
||||
}
|
||||
return int(b[1])
|
||||
}
|
||||
|
||||
// EncodeNOP adds an explicit NOP to the option list.
|
||||
func EncodeNOP(b []byte) int {
|
||||
if len(b) == 0 {
|
||||
return 0
|
||||
}
|
||||
b[0] = TCPOptionNOP
|
||||
return 1
|
||||
}
|
||||
|
||||
// AddTCPOptionPadding adds the required number of TCPOptionNOP to quad align
|
||||
// the option buffer. It adds padding bytes after the offset specified and
|
||||
// returns the number of padding bytes added. The passed in options slice
|
||||
// must have space for the padding bytes.
|
||||
func AddTCPOptionPadding(options []byte, offset int) int {
|
||||
paddingToAdd := -offset & 3
|
||||
// Now add any padding bytes that might be required to quad align the
|
||||
// options.
|
||||
for i := offset; i < offset+paddingToAdd; i++ {
|
||||
options[i] = TCPOptionNOP
|
||||
}
|
||||
return paddingToAdd
|
||||
}
|
||||
|
||||
// Acceptable checks if a segment that starts at segSeq and has length segLen is
|
||||
// "acceptable" for arriving in a receive window that starts at rcvNxt and ends
|
||||
// before rcvAcc, according to the table on page 26 and 69 of RFC 793.
|
||||
func Acceptable(segSeq seqnum.Value, segLen seqnum.Size, rcvNxt, rcvAcc seqnum.Value) bool {
|
||||
if rcvNxt == rcvAcc {
|
||||
return segLen == 0 && segSeq == rcvNxt
|
||||
}
|
||||
if segLen == 0 {
|
||||
// rcvWnd is incremented by 1 because that is Linux's behavior despite the
|
||||
// RFC.
|
||||
return segSeq.InRange(rcvNxt, rcvAcc.Add(1))
|
||||
}
|
||||
// Page 70 of RFC 793 allows packets that can be made "acceptable" by trimming
|
||||
// the payload, so we'll accept any payload that overlaps the receive window.
|
||||
// segSeq < rcvAcc is more correct according to RFC, however, Linux does it
|
||||
// differently, it uses segSeq <= rcvAcc, we'd want to keep the same behavior
|
||||
// as Linux.
|
||||
return rcvNxt.LessThan(segSeq.Add(segLen)) && segSeq.LessThanEq(rcvAcc)
|
||||
}
|
||||
|
||||
// TCPValid returns true if the pkt has a valid TCP header. It checks whether:
|
||||
// - The data offset is too small.
|
||||
// - The data offset is too large.
|
||||
// - The checksum is invalid.
|
||||
//
|
||||
// TCPValid corresponds to net/netfilter/nf_conntrack_proto_tcp.c:tcp_error.
|
||||
func TCPValid(hdr TCP, payloadChecksum func() uint16, payloadSize uint16, srcAddr, dstAddr tcpip.Address, skipChecksumValidation bool) (csum uint16, csumValid, ok bool) {
|
||||
if offset := int(hdr.DataOffset()); offset < TCPMinimumSize || offset > len(hdr) {
|
||||
return
|
||||
}
|
||||
|
||||
if skipChecksumValidation {
|
||||
csumValid = true
|
||||
} else {
|
||||
csum = hdr.Checksum()
|
||||
csumValid = hdr.IsChecksumValid(srcAddr, dstAddr, payloadChecksum(), payloadSize)
|
||||
}
|
||||
return csum, csumValid, true
|
||||
}
|
||||
195
internal/gtcpip/header/udp.go
Normal file
195
internal/gtcpip/header/udp.go
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
// Copyright 2018 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package header
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"math"
|
||||
|
||||
"github.com/sagernet/sing-tun/internal/gtcpip"
|
||||
"github.com/sagernet/sing-tun/internal/gtcpip/checksum"
|
||||
)
|
||||
|
||||
const (
|
||||
udpSrcPort = 0
|
||||
udpDstPort = 2
|
||||
udpLength = 4
|
||||
udpChecksum = 6
|
||||
)
|
||||
|
||||
const (
|
||||
// UDPMaximumPacketSize is the largest possible UDP packet.
|
||||
UDPMaximumPacketSize = 0xffff
|
||||
)
|
||||
|
||||
// UDPFields contains the fields of a UDP packet. It is used to describe the
|
||||
// fields of a packet that needs to be encoded.
|
||||
type UDPFields struct {
|
||||
// SrcPort is the "source port" field of a UDP packet.
|
||||
SrcPort uint16
|
||||
|
||||
// DstPort is the "destination port" field of a UDP packet.
|
||||
DstPort uint16
|
||||
|
||||
// Length is the "length" field of a UDP packet.
|
||||
Length uint16
|
||||
|
||||
// Checksum is the "checksum" field of a UDP packet.
|
||||
Checksum uint16
|
||||
}
|
||||
|
||||
// UDP represents a UDP header stored in a byte array.
|
||||
type UDP []byte
|
||||
|
||||
const (
|
||||
// UDPMinimumSize is the minimum size of a valid UDP packet.
|
||||
UDPMinimumSize = 8
|
||||
|
||||
// UDPMaximumSize is the maximum size of a valid UDP packet. The length field
|
||||
// in the UDP header is 16 bits as per RFC 768.
|
||||
UDPMaximumSize = math.MaxUint16
|
||||
|
||||
// UDPProtocolNumber is UDP's transport protocol number.
|
||||
UDPProtocolNumber tcpip.TransportProtocolNumber = 17
|
||||
)
|
||||
|
||||
// SourcePort returns the "source port" field of the UDP header.
|
||||
func (b UDP) SourcePort() uint16 {
|
||||
return binary.BigEndian.Uint16(b[udpSrcPort:])
|
||||
}
|
||||
|
||||
// DestinationPort returns the "destination port" field of the UDP header.
|
||||
func (b UDP) DestinationPort() uint16 {
|
||||
return binary.BigEndian.Uint16(b[udpDstPort:])
|
||||
}
|
||||
|
||||
// Length returns the "length" field of the UDP header.
|
||||
func (b UDP) Length() uint16 {
|
||||
return binary.BigEndian.Uint16(b[udpLength:])
|
||||
}
|
||||
|
||||
// Payload returns the data contained in the UDP datagram.
|
||||
func (b UDP) Payload() []byte {
|
||||
return b[UDPMinimumSize:]
|
||||
}
|
||||
|
||||
// Checksum returns the "checksum" field of the UDP header.
|
||||
func (b UDP) Checksum() uint16 {
|
||||
return binary.BigEndian.Uint16(b[udpChecksum:])
|
||||
}
|
||||
|
||||
// SetSourcePort sets the "source port" field of the UDP header.
|
||||
func (b UDP) SetSourcePort(port uint16) {
|
||||
binary.BigEndian.PutUint16(b[udpSrcPort:], port)
|
||||
}
|
||||
|
||||
// SetDestinationPort sets the "destination port" field of the UDP header.
|
||||
func (b UDP) SetDestinationPort(port uint16) {
|
||||
binary.BigEndian.PutUint16(b[udpDstPort:], port)
|
||||
}
|
||||
|
||||
// SetChecksum sets the "checksum" field of the UDP header.
|
||||
func (b UDP) SetChecksum(xsum uint16) {
|
||||
checksum.Put(b[udpChecksum:], xsum)
|
||||
}
|
||||
|
||||
// SetLength sets the "length" field of the UDP header.
|
||||
func (b UDP) SetLength(length uint16) {
|
||||
binary.BigEndian.PutUint16(b[udpLength:], length)
|
||||
}
|
||||
|
||||
// CalculateChecksum calculates the checksum of the UDP packet, given the
|
||||
// checksum of the network-layer pseudo-header and the checksum of the payload.
|
||||
func (b UDP) CalculateChecksum(partialChecksum uint16) uint16 {
|
||||
// Calculate the rest of the checksum.
|
||||
return checksum.Checksum(b[:UDPMinimumSize], partialChecksum)
|
||||
}
|
||||
|
||||
// IsChecksumValid returns true iff the UDP header's checksum is valid.
|
||||
func (b UDP) IsChecksumValid(src, dst tcpip.Address, payloadChecksum uint16) bool {
|
||||
xsum := PseudoHeaderChecksum(UDPProtocolNumber, dst.AsSlice(), src.AsSlice(), b.Length())
|
||||
xsum = checksum.Combine(xsum, payloadChecksum)
|
||||
return b.CalculateChecksum(xsum) == 0xffff
|
||||
}
|
||||
|
||||
// Encode encodes all the fields of the UDP header.
|
||||
func (b UDP) Encode(u *UDPFields) {
|
||||
b.SetSourcePort(u.SrcPort)
|
||||
b.SetDestinationPort(u.DstPort)
|
||||
b.SetLength(u.Length)
|
||||
b.SetChecksum(u.Checksum)
|
||||
}
|
||||
|
||||
// SetSourcePortWithChecksumUpdate implements ChecksummableTransport.
|
||||
func (b UDP) SetSourcePortWithChecksumUpdate(new uint16) {
|
||||
old := b.SourcePort()
|
||||
b.SetSourcePort(new)
|
||||
b.SetChecksum(^checksumUpdate2ByteAlignedUint16(^b.Checksum(), old, new))
|
||||
}
|
||||
|
||||
// SetDestinationPortWithChecksumUpdate implements ChecksummableTransport.
|
||||
func (b UDP) SetDestinationPortWithChecksumUpdate(new uint16) {
|
||||
old := b.DestinationPort()
|
||||
b.SetDestinationPort(new)
|
||||
b.SetChecksum(^checksumUpdate2ByteAlignedUint16(^b.Checksum(), old, new))
|
||||
}
|
||||
|
||||
// UpdateChecksumPseudoHeaderAddress implements ChecksummableTransport.
|
||||
func (b UDP) UpdateChecksumPseudoHeaderAddress(old, new tcpip.Address, fullChecksum bool) {
|
||||
xsum := b.Checksum()
|
||||
if fullChecksum {
|
||||
xsum = ^xsum
|
||||
}
|
||||
|
||||
xsum = checksumUpdate2ByteAlignedAddress(xsum, old, new)
|
||||
if fullChecksum {
|
||||
xsum = ^xsum
|
||||
}
|
||||
|
||||
b.SetChecksum(xsum)
|
||||
}
|
||||
|
||||
// UDPValid returns true if the pkt has a valid UDP header. It checks whether:
|
||||
// - The length field is too small.
|
||||
// - The length field is too large.
|
||||
// - The checksum is invalid.
|
||||
//
|
||||
// UDPValid corresponds to net/netfilter/nf_conntrack_proto_udp.c:udp_error.
|
||||
func UDPValid(hdr UDP, payloadChecksum func() uint16, payloadSize uint16, netProto tcpip.NetworkProtocolNumber, srcAddr, dstAddr tcpip.Address, skipChecksumValidation bool) (lengthValid, csumValid bool) {
|
||||
if length := hdr.Length(); length > payloadSize+UDPMinimumSize || length < UDPMinimumSize {
|
||||
return false, false
|
||||
}
|
||||
|
||||
if skipChecksumValidation {
|
||||
return true, true
|
||||
}
|
||||
|
||||
// On IPv4, UDP checksum is optional, and a zero value means the transmitter
|
||||
// omitted the checksum generation, as per RFC 768:
|
||||
//
|
||||
// An all zero transmitted checksum value means that the transmitter
|
||||
// generated no checksum (for debugging or for higher level protocols that
|
||||
// don't care).
|
||||
//
|
||||
// On IPv6, UDP checksum is not optional, as per RFC 2460 Section 8.1:
|
||||
//
|
||||
// Unlike IPv4, when UDP packets are originated by an IPv6 node, the UDP
|
||||
// checksum is not optional.
|
||||
if netProto == IPv4ProtocolNumber && hdr.Checksum() == 0 {
|
||||
return true, true
|
||||
}
|
||||
|
||||
return true, hdr.IsChecksumValid(srcAddr, dstAddr, payloadChecksum())
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue