mirror of
https://github.com/SagerNet/sing-tun.git
synced 2025-03-31 10:27:39 +03:00
Migrate clashtcpip to gVisor tcpip copied
This commit is contained in:
parent
7f8e556bb0
commit
1793988a6d
43 changed files with 7370 additions and 1340 deletions
|
@ -1,40 +0,0 @@
|
|||
package clashtcpip
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
type ICMPType = byte
|
||||
|
||||
const (
|
||||
ICMPTypePingRequest byte = 0x8
|
||||
ICMPTypePingResponse byte = 0x0
|
||||
)
|
||||
|
||||
type ICMPPacket []byte
|
||||
|
||||
func (p ICMPPacket) Type() ICMPType {
|
||||
return p[0]
|
||||
}
|
||||
|
||||
func (p ICMPPacket) SetType(v ICMPType) {
|
||||
p[0] = v
|
||||
}
|
||||
|
||||
func (p ICMPPacket) Code() byte {
|
||||
return p[1]
|
||||
}
|
||||
|
||||
func (p ICMPPacket) Checksum() uint16 {
|
||||
return binary.BigEndian.Uint16(p[2:])
|
||||
}
|
||||
|
||||
func (p ICMPPacket) SetChecksum(sum [2]byte) {
|
||||
p[2] = sum[0]
|
||||
p[3] = sum[1]
|
||||
}
|
||||
|
||||
func (p ICMPPacket) ResetChecksum() {
|
||||
p.SetChecksum(zeroChecksum)
|
||||
p.SetChecksum(Checksum(0, p))
|
||||
}
|
|
@ -1,172 +0,0 @@
|
|||
package clashtcpip
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
type ICMPv6Packet []byte
|
||||
|
||||
const (
|
||||
ICMPv6HeaderSize = 4
|
||||
|
||||
ICMPv6MinimumSize = 8
|
||||
|
||||
ICMPv6PayloadOffset = 8
|
||||
|
||||
ICMPv6EchoMinimumSize = 8
|
||||
|
||||
ICMPv6ErrorHeaderSize = 8
|
||||
|
||||
ICMPv6DstUnreachableMinimumSize = ICMPv6MinimumSize
|
||||
|
||||
ICMPv6PacketTooBigMinimumSize = ICMPv6MinimumSize
|
||||
|
||||
ICMPv6ChecksumOffset = 2
|
||||
|
||||
icmpv6PointerOffset = 4
|
||||
|
||||
icmpv6MTUOffset = 4
|
||||
|
||||
icmpv6IdentOffset = 4
|
||||
|
||||
icmpv6SequenceOffset = 6
|
||||
|
||||
NDPHopLimit = 255
|
||||
)
|
||||
|
||||
type ICMPv6Type byte
|
||||
|
||||
const (
|
||||
ICMPv6DstUnreachable ICMPv6Type = 1
|
||||
ICMPv6PacketTooBig ICMPv6Type = 2
|
||||
ICMPv6TimeExceeded ICMPv6Type = 3
|
||||
ICMPv6ParamProblem ICMPv6Type = 4
|
||||
ICMPv6EchoRequest ICMPv6Type = 128
|
||||
ICMPv6EchoReply ICMPv6Type = 129
|
||||
|
||||
ICMPv6RouterSolicit ICMPv6Type = 133
|
||||
ICMPv6RouterAdvert ICMPv6Type = 134
|
||||
ICMPv6NeighborSolicit ICMPv6Type = 135
|
||||
ICMPv6NeighborAdvert ICMPv6Type = 136
|
||||
ICMPv6RedirectMsg ICMPv6Type = 137
|
||||
|
||||
ICMPv6MulticastListenerQuery ICMPv6Type = 130
|
||||
ICMPv6MulticastListenerReport ICMPv6Type = 131
|
||||
ICMPv6MulticastListenerDone ICMPv6Type = 132
|
||||
)
|
||||
|
||||
func (typ ICMPv6Type) IsErrorType() bool {
|
||||
return typ&0x80 == 0
|
||||
}
|
||||
|
||||
type ICMPv6Code byte
|
||||
|
||||
const (
|
||||
ICMPv6NetworkUnreachable ICMPv6Code = 0
|
||||
ICMPv6Prohibited ICMPv6Code = 1
|
||||
ICMPv6BeyondScope ICMPv6Code = 2
|
||||
ICMPv6AddressUnreachable ICMPv6Code = 3
|
||||
ICMPv6PortUnreachable ICMPv6Code = 4
|
||||
ICMPv6Policy ICMPv6Code = 5
|
||||
ICMPv6RejectRoute ICMPv6Code = 6
|
||||
)
|
||||
|
||||
const (
|
||||
ICMPv6HopLimitExceeded ICMPv6Code = 0
|
||||
ICMPv6ReassemblyTimeout ICMPv6Code = 1
|
||||
)
|
||||
|
||||
const (
|
||||
ICMPv6ErroneousHeader ICMPv6Code = 0
|
||||
|
||||
ICMPv6UnknownHeader ICMPv6Code = 1
|
||||
|
||||
ICMPv6UnknownOption ICMPv6Code = 2
|
||||
)
|
||||
|
||||
const ICMPv6UnusedCode ICMPv6Code = 0
|
||||
|
||||
func (b ICMPv6Packet) Type() ICMPv6Type {
|
||||
return ICMPv6Type(b[0])
|
||||
}
|
||||
|
||||
func (b ICMPv6Packet) SetType(t ICMPv6Type) {
|
||||
b[0] = byte(t)
|
||||
}
|
||||
|
||||
func (b ICMPv6Packet) Code() ICMPv6Code {
|
||||
return ICMPv6Code(b[1])
|
||||
}
|
||||
|
||||
func (b ICMPv6Packet) SetCode(c ICMPv6Code) {
|
||||
b[1] = byte(c)
|
||||
}
|
||||
|
||||
func (b ICMPv6Packet) TypeSpecific() uint32 {
|
||||
return binary.BigEndian.Uint32(b[icmpv6PointerOffset:])
|
||||
}
|
||||
|
||||
func (b ICMPv6Packet) SetTypeSpecific(val uint32) {
|
||||
binary.BigEndian.PutUint32(b[icmpv6PointerOffset:], val)
|
||||
}
|
||||
|
||||
func (b ICMPv6Packet) Checksum() uint16 {
|
||||
return binary.BigEndian.Uint16(b[ICMPv6ChecksumOffset:])
|
||||
}
|
||||
|
||||
func (b ICMPv6Packet) SetChecksum(sum [2]byte) {
|
||||
_ = b[ICMPv6ChecksumOffset+1]
|
||||
b[ICMPv6ChecksumOffset] = sum[0]
|
||||
b[ICMPv6ChecksumOffset+1] = sum[1]
|
||||
}
|
||||
|
||||
func (ICMPv6Packet) SourcePort() uint16 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (ICMPv6Packet) DestinationPort() uint16 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (ICMPv6Packet) SetSourcePort(uint16) {
|
||||
}
|
||||
|
||||
func (ICMPv6Packet) SetDestinationPort(uint16) {
|
||||
}
|
||||
|
||||
func (b ICMPv6Packet) MTU() uint32 {
|
||||
return binary.BigEndian.Uint32(b[icmpv6MTUOffset:])
|
||||
}
|
||||
|
||||
func (b ICMPv6Packet) SetMTU(mtu uint32) {
|
||||
binary.BigEndian.PutUint32(b[icmpv6MTUOffset:], mtu)
|
||||
}
|
||||
|
||||
func (b ICMPv6Packet) Ident() uint16 {
|
||||
return binary.BigEndian.Uint16(b[icmpv6IdentOffset:])
|
||||
}
|
||||
|
||||
func (b ICMPv6Packet) SetIdent(ident uint16) {
|
||||
binary.BigEndian.PutUint16(b[icmpv6IdentOffset:], ident)
|
||||
}
|
||||
|
||||
func (b ICMPv6Packet) Sequence() uint16 {
|
||||
return binary.BigEndian.Uint16(b[icmpv6SequenceOffset:])
|
||||
}
|
||||
|
||||
func (b ICMPv6Packet) SetSequence(sequence uint16) {
|
||||
binary.BigEndian.PutUint16(b[icmpv6SequenceOffset:], sequence)
|
||||
}
|
||||
|
||||
func (b ICMPv6Packet) MessageBody() []byte {
|
||||
return b[ICMPv6HeaderSize:]
|
||||
}
|
||||
|
||||
func (b ICMPv6Packet) Payload() []byte {
|
||||
return b[ICMPv6PayloadOffset:]
|
||||
}
|
||||
|
||||
func (b ICMPv6Packet) ResetChecksum(psum uint32) {
|
||||
b.SetChecksum(zeroChecksum)
|
||||
b.SetChecksum(Checksum(psum, b))
|
||||
}
|
|
@ -1,209 +0,0 @@
|
|||
package clashtcpip
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"net/netip"
|
||||
)
|
||||
|
||||
type IPProtocol = byte
|
||||
|
||||
type IP interface {
|
||||
Payload() []byte
|
||||
SourceIP() netip.Addr
|
||||
DestinationIP() netip.Addr
|
||||
SetSourceIP(ip netip.Addr)
|
||||
SetDestinationIP(ip netip.Addr)
|
||||
Protocol() IPProtocol
|
||||
DecTimeToLive()
|
||||
ResetChecksum()
|
||||
PseudoSum() uint32
|
||||
}
|
||||
|
||||
// IPProtocol type
|
||||
const (
|
||||
ICMP IPProtocol = 0x01
|
||||
TCP IPProtocol = 0x06
|
||||
UDP IPProtocol = 0x11
|
||||
ICMPv6 IPProtocol = 0x3a
|
||||
)
|
||||
|
||||
const (
|
||||
FlagDontFragment = 1 << 1
|
||||
FlagMoreFragment = 1 << 2
|
||||
)
|
||||
|
||||
const (
|
||||
IPv4HeaderSize = 20
|
||||
|
||||
IPv4Version = 4
|
||||
|
||||
IPv4OptionsOffset = 20
|
||||
IPv4PacketMinLength = IPv4OptionsOffset
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidLength = errors.New("invalid packet length")
|
||||
ErrInvalidIPVersion = errors.New("invalid ip version")
|
||||
ErrInvalidChecksum = errors.New("invalid checksum")
|
||||
)
|
||||
|
||||
type IPv4Packet []byte
|
||||
|
||||
func (p IPv4Packet) TotalLen() uint16 {
|
||||
return binary.BigEndian.Uint16(p[2:])
|
||||
}
|
||||
|
||||
func (p IPv4Packet) SetTotalLength(length uint16) {
|
||||
binary.BigEndian.PutUint16(p[2:], length)
|
||||
}
|
||||
|
||||
func (p IPv4Packet) HeaderLen() uint16 {
|
||||
return uint16(p[0]&0xf) * 4
|
||||
}
|
||||
|
||||
func (p IPv4Packet) SetHeaderLen(length uint16) {
|
||||
p[0] &= 0xF0
|
||||
p[0] |= byte(length / 4)
|
||||
}
|
||||
|
||||
func (p IPv4Packet) TypeOfService() byte {
|
||||
return p[1]
|
||||
}
|
||||
|
||||
func (p IPv4Packet) SetTypeOfService(tos byte) {
|
||||
p[1] = tos
|
||||
}
|
||||
|
||||
func (p IPv4Packet) Identification() uint16 {
|
||||
return binary.BigEndian.Uint16(p[4:])
|
||||
}
|
||||
|
||||
func (p IPv4Packet) SetIdentification(id uint16) {
|
||||
binary.BigEndian.PutUint16(p[4:], id)
|
||||
}
|
||||
|
||||
func (p IPv4Packet) FragmentOffset() uint16 {
|
||||
return binary.BigEndian.Uint16([]byte{p[6] & 0x7, p[7]}) * 8
|
||||
}
|
||||
|
||||
func (p IPv4Packet) SetFragmentOffset(offset uint32) {
|
||||
flags := p.Flags()
|
||||
binary.BigEndian.PutUint16(p[6:], uint16(offset/8))
|
||||
p.SetFlags(flags)
|
||||
}
|
||||
|
||||
func (p IPv4Packet) DataLen() uint16 {
|
||||
return p.TotalLen() - p.HeaderLen()
|
||||
}
|
||||
|
||||
func (p IPv4Packet) Payload() []byte {
|
||||
return p[p.HeaderLen():p.TotalLen()]
|
||||
}
|
||||
|
||||
func (p IPv4Packet) Protocol() IPProtocol {
|
||||
return p[9]
|
||||
}
|
||||
|
||||
func (p IPv4Packet) SetProtocol(protocol IPProtocol) {
|
||||
p[9] = protocol
|
||||
}
|
||||
|
||||
func (p IPv4Packet) Flags() byte {
|
||||
return p[6] >> 5
|
||||
}
|
||||
|
||||
func (p IPv4Packet) SetFlags(flags byte) {
|
||||
p[6] &= 0x1F
|
||||
p[6] |= flags << 5
|
||||
}
|
||||
|
||||
func (p IPv4Packet) SourceIP() netip.Addr {
|
||||
return netip.AddrFrom4([4]byte{p[12], p[13], p[14], p[15]})
|
||||
}
|
||||
|
||||
func (p IPv4Packet) SetSourceIP(ip netip.Addr) {
|
||||
if ip.Is4() {
|
||||
copy(p[12:16], ip.AsSlice())
|
||||
}
|
||||
}
|
||||
|
||||
func (p IPv4Packet) DestinationIP() netip.Addr {
|
||||
return netip.AddrFrom4([4]byte{p[16], p[17], p[18], p[19]})
|
||||
}
|
||||
|
||||
func (p IPv4Packet) SetDestinationIP(ip netip.Addr) {
|
||||
if ip.Is4() {
|
||||
copy(p[16:20], ip.AsSlice())
|
||||
}
|
||||
}
|
||||
|
||||
func (p IPv4Packet) Checksum() uint16 {
|
||||
return binary.BigEndian.Uint16(p[10:])
|
||||
}
|
||||
|
||||
func (p IPv4Packet) SetChecksum(sum [2]byte) {
|
||||
p[10] = sum[0]
|
||||
p[11] = sum[1]
|
||||
}
|
||||
|
||||
func (p IPv4Packet) TimeToLive() uint8 {
|
||||
return p[8]
|
||||
}
|
||||
|
||||
func (p IPv4Packet) SetTimeToLive(ttl uint8) {
|
||||
p[8] = ttl
|
||||
}
|
||||
|
||||
func (p IPv4Packet) DecTimeToLive() {
|
||||
p[8] = p[8] - uint8(1)
|
||||
}
|
||||
|
||||
func (p IPv4Packet) ResetChecksum() {
|
||||
p.SetChecksum(zeroChecksum)
|
||||
p.SetChecksum(Checksum(0, p[:p.HeaderLen()]))
|
||||
}
|
||||
|
||||
// PseudoSum for tcp checksum
|
||||
func (p IPv4Packet) PseudoSum() uint32 {
|
||||
sum := Sum(p[12:20])
|
||||
sum += uint32(p.Protocol())
|
||||
sum += uint32(p.DataLen())
|
||||
return sum
|
||||
}
|
||||
|
||||
func (p IPv4Packet) Valid() bool {
|
||||
return len(p) >= IPv4HeaderSize && p.TotalLen() >= p.HeaderLen() && uint16(len(p)) >= p.TotalLen()
|
||||
}
|
||||
|
||||
func (p IPv4Packet) Verify() error {
|
||||
if len(p) < IPv4PacketMinLength {
|
||||
return ErrInvalidLength
|
||||
}
|
||||
|
||||
checksum := []byte{p[10], p[11]}
|
||||
headerLength := uint16(p[0]&0xF) * 4
|
||||
packetLength := binary.BigEndian.Uint16(p[2:])
|
||||
|
||||
if p[0]>>4 != 4 {
|
||||
return ErrInvalidIPVersion
|
||||
}
|
||||
|
||||
if uint16(len(p)) < packetLength || packetLength < headerLength {
|
||||
return ErrInvalidLength
|
||||
}
|
||||
|
||||
p[10] = 0
|
||||
p[11] = 0
|
||||
defer copy(p[10:12], checksum)
|
||||
|
||||
answer := Checksum(0, p[:headerLength])
|
||||
|
||||
if answer[0] != checksum[0] || answer[1] != checksum[1] {
|
||||
return ErrInvalidChecksum
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ IP = (*IPv4Packet)(nil)
|
|
@ -1,141 +0,0 @@
|
|||
package clashtcpip
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"net/netip"
|
||||
)
|
||||
|
||||
const (
|
||||
versTCFL = 0
|
||||
|
||||
IPv6PayloadLenOffset = 4
|
||||
|
||||
IPv6NextHeaderOffset = 6
|
||||
hopLimit = 7
|
||||
v6SrcAddr = 8
|
||||
v6DstAddr = v6SrcAddr + IPv6AddressSize
|
||||
|
||||
IPv6FixedHeaderSize = v6DstAddr + IPv6AddressSize
|
||||
)
|
||||
|
||||
const (
|
||||
versIHL = 0
|
||||
tos = 1
|
||||
ipVersionShift = 4
|
||||
ipIHLMask = 0x0f
|
||||
IPv4IHLStride = 4
|
||||
)
|
||||
|
||||
type IPv6Packet []byte
|
||||
|
||||
const (
|
||||
IPv6MinimumSize = IPv6FixedHeaderSize
|
||||
|
||||
IPv6AddressSize = 16
|
||||
|
||||
IPv6Version = 6
|
||||
|
||||
IPv6MinimumMTU = 1280
|
||||
)
|
||||
|
||||
func (b IPv6Packet) PayloadLength() uint16 {
|
||||
return binary.BigEndian.Uint16(b[IPv6PayloadLenOffset:])
|
||||
}
|
||||
|
||||
func (b IPv6Packet) HopLimit() uint8 {
|
||||
return b[hopLimit]
|
||||
}
|
||||
|
||||
func (b IPv6Packet) NextHeader() byte {
|
||||
return b[IPv6NextHeaderOffset]
|
||||
}
|
||||
|
||||
func (b IPv6Packet) Protocol() IPProtocol {
|
||||
return b.NextHeader()
|
||||
}
|
||||
|
||||
func (b IPv6Packet) Payload() []byte {
|
||||
return b[IPv6MinimumSize:][:b.PayloadLength()]
|
||||
}
|
||||
|
||||
func (b IPv6Packet) SourceIP() netip.Addr {
|
||||
addr, _ := netip.AddrFromSlice(b[v6SrcAddr:][:IPv6AddressSize])
|
||||
return addr
|
||||
}
|
||||
|
||||
func (b IPv6Packet) DestinationIP() netip.Addr {
|
||||
addr, _ := netip.AddrFromSlice(b[v6DstAddr:][:IPv6AddressSize])
|
||||
return addr
|
||||
}
|
||||
|
||||
func (IPv6Packet) Checksum() uint16 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (b IPv6Packet) TOS() (uint8, uint32) {
|
||||
v := binary.BigEndian.Uint32(b[versTCFL:])
|
||||
return uint8(v >> 20), v & 0xfffff
|
||||
}
|
||||
|
||||
func (b IPv6Packet) SetTOS(t uint8, l uint32) {
|
||||
vtf := (6 << 28) | (uint32(t) << 20) | (l & 0xfffff)
|
||||
binary.BigEndian.PutUint32(b[versTCFL:], vtf)
|
||||
}
|
||||
|
||||
func (b IPv6Packet) SetPayloadLength(payloadLength uint16) {
|
||||
binary.BigEndian.PutUint16(b[IPv6PayloadLenOffset:], payloadLength)
|
||||
}
|
||||
|
||||
func (b IPv6Packet) SetSourceIP(addr netip.Addr) {
|
||||
if addr.Is6() {
|
||||
copy(b[v6SrcAddr:][:IPv6AddressSize], addr.AsSlice())
|
||||
}
|
||||
}
|
||||
|
||||
func (b IPv6Packet) SetDestinationIP(addr netip.Addr) {
|
||||
if addr.Is6() {
|
||||
copy(b[v6DstAddr:][:IPv6AddressSize], addr.AsSlice())
|
||||
}
|
||||
}
|
||||
|
||||
func (b IPv6Packet) SetHopLimit(v uint8) {
|
||||
b[hopLimit] = v
|
||||
}
|
||||
|
||||
func (b IPv6Packet) SetNextHeader(v byte) {
|
||||
b[IPv6NextHeaderOffset] = v
|
||||
}
|
||||
|
||||
func (b IPv6Packet) SetProtocol(p IPProtocol) {
|
||||
b.SetNextHeader(p)
|
||||
}
|
||||
|
||||
func (b IPv6Packet) DecTimeToLive() {
|
||||
b[hopLimit] = b[hopLimit] - uint8(1)
|
||||
}
|
||||
|
||||
func (IPv6Packet) SetChecksum(uint16) {
|
||||
}
|
||||
|
||||
func (IPv6Packet) ResetChecksum() {
|
||||
}
|
||||
|
||||
func (b IPv6Packet) PseudoSum() uint32 {
|
||||
sum := Sum(b[v6SrcAddr:IPv6FixedHeaderSize])
|
||||
sum += uint32(b.Protocol())
|
||||
sum += uint32(b.PayloadLength())
|
||||
return sum
|
||||
}
|
||||
|
||||
func (b IPv6Packet) Valid() bool {
|
||||
return len(b) >= IPv6MinimumSize && len(b) >= int(b.PayloadLength())+IPv6MinimumSize
|
||||
}
|
||||
|
||||
func IPVersion(b []byte) int {
|
||||
if len(b) < versIHL+1 {
|
||||
return -1
|
||||
}
|
||||
return int(b[versIHL] >> ipVersionShift)
|
||||
}
|
||||
|
||||
var _ IP = (*IPv6Packet)(nil)
|
|
@ -1,94 +0,0 @@
|
|||
package clashtcpip
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"net"
|
||||
)
|
||||
|
||||
const (
|
||||
TCPFin uint16 = 1 << 0
|
||||
TCPSyn uint16 = 1 << 1
|
||||
TCPRst uint16 = 1 << 2
|
||||
TCPPuh uint16 = 1 << 3
|
||||
TCPAck uint16 = 1 << 4
|
||||
TCPUrg uint16 = 1 << 5
|
||||
TCPEce uint16 = 1 << 6
|
||||
TCPEwr uint16 = 1 << 7
|
||||
TCPNs uint16 = 1 << 8
|
||||
)
|
||||
|
||||
const TCPHeaderSize = 20
|
||||
|
||||
type TCPPacket []byte
|
||||
|
||||
func (p TCPPacket) SourcePort() uint16 {
|
||||
return binary.BigEndian.Uint16(p)
|
||||
}
|
||||
|
||||
func (p TCPPacket) SetSourcePort(port uint16) {
|
||||
binary.BigEndian.PutUint16(p, port)
|
||||
}
|
||||
|
||||
func (p TCPPacket) DestinationPort() uint16 {
|
||||
return binary.BigEndian.Uint16(p[2:])
|
||||
}
|
||||
|
||||
func (p TCPPacket) SetDestinationPort(port uint16) {
|
||||
binary.BigEndian.PutUint16(p[2:], port)
|
||||
}
|
||||
|
||||
func (p TCPPacket) Flags() uint16 {
|
||||
return uint16(p[13] | (p[12] & 0x1))
|
||||
}
|
||||
|
||||
func (p TCPPacket) Checksum() uint16 {
|
||||
return binary.BigEndian.Uint16(p[16:])
|
||||
}
|
||||
|
||||
func (p TCPPacket) SetChecksum(sum [2]byte) {
|
||||
p[16] = sum[0]
|
||||
p[17] = sum[1]
|
||||
}
|
||||
|
||||
func (p TCPPacket) OffloadChecksum() {
|
||||
p.SetChecksum(zeroChecksum)
|
||||
}
|
||||
|
||||
func (p TCPPacket) ResetChecksum(psum uint32) {
|
||||
p.SetChecksum(zeroChecksum)
|
||||
p.SetChecksum(Checksum(psum, p))
|
||||
}
|
||||
|
||||
func (p TCPPacket) Valid() bool {
|
||||
return len(p) >= TCPHeaderSize
|
||||
}
|
||||
|
||||
func (p TCPPacket) Verify(sourceAddress net.IP, targetAddress net.IP) error {
|
||||
var checksum [2]byte
|
||||
checksum[0] = p[16]
|
||||
checksum[1] = p[17]
|
||||
|
||||
// reset checksum
|
||||
p[16] = 0
|
||||
p[17] = 0
|
||||
|
||||
// restore checksum
|
||||
defer func() {
|
||||
p[16] = checksum[0]
|
||||
p[17] = checksum[1]
|
||||
}()
|
||||
|
||||
// check checksum
|
||||
s := uint32(0)
|
||||
s += Sum(sourceAddress)
|
||||
s += Sum(targetAddress)
|
||||
s += uint32(TCP)
|
||||
s += uint32(len(p))
|
||||
|
||||
check := Checksum(s, p)
|
||||
if checksum[0] != check[0] || checksum[1] != check[1] {
|
||||
return ErrInvalidChecksum
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
package clashtcpip
|
||||
|
||||
var zeroChecksum = [2]byte{0x00, 0x00}
|
||||
|
||||
var SumFnc = SumCompat
|
||||
|
||||
func Sum(b []byte) uint32 {
|
||||
return SumFnc(b)
|
||||
}
|
||||
|
||||
// Checksum for Internet Protocol family headers
|
||||
func Checksum(sum uint32, b []byte) (answer [2]byte) {
|
||||
sum += Sum(b)
|
||||
sum = (sum >> 16) + (sum & 0xffff)
|
||||
sum += sum >> 16
|
||||
sum = ^sum
|
||||
answer[0] = byte(sum >> 8)
|
||||
answer[1] = byte(sum)
|
||||
return
|
||||
}
|
||||
|
||||
func SetIPv4(packet []byte) {
|
||||
packet[0] = (packet[0] & 0x0f) | (4 << 4)
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
//go:build !noasm
|
||||
|
||||
package clashtcpip
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/cpu"
|
||||
)
|
||||
|
||||
//go:noescape
|
||||
func sumAsmAvx2(data unsafe.Pointer, length uintptr) uintptr
|
||||
|
||||
func SumAVX2(data []byte) uint32 {
|
||||
if len(data) == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
return uint32(sumAsmAvx2(unsafe.Pointer(&data[0]), uintptr(len(data))))
|
||||
}
|
||||
|
||||
func init() {
|
||||
if cpu.X86.HasAVX2 {
|
||||
SumFnc = SumAVX2
|
||||
}
|
||||
}
|
|
@ -1,140 +0,0 @@
|
|||
#include "textflag.h"
|
||||
|
||||
DATA endian_swap_mask<>+0(SB)/8, $0x607040502030001
|
||||
DATA endian_swap_mask<>+8(SB)/8, $0xE0F0C0D0A0B0809
|
||||
DATA endian_swap_mask<>+16(SB)/8, $0x607040502030001
|
||||
DATA endian_swap_mask<>+24(SB)/8, $0xE0F0C0D0A0B0809
|
||||
GLOBL endian_swap_mask<>(SB), RODATA, $32
|
||||
|
||||
// func sumAsmAvx2(data unsafe.Pointer, length uintptr) uintptr
|
||||
//
|
||||
// args (8 bytes aligned):
|
||||
// data unsafe.Pointer - 8 bytes - 0 offset
|
||||
// length uintptr - 8 bytes - 8 offset
|
||||
// result uintptr - 8 bytes - 16 offset
|
||||
#define PDATA AX
|
||||
#define LENGTH CX
|
||||
#define RESULT BX
|
||||
TEXT ·sumAsmAvx2(SB),NOSPLIT,$0-24
|
||||
MOVQ data+0(FP), PDATA
|
||||
MOVQ length+8(FP), LENGTH
|
||||
XORQ RESULT, RESULT
|
||||
|
||||
#define VSUM Y0
|
||||
#define ENDIAN_SWAP_MASK Y1
|
||||
BEGIN:
|
||||
VMOVDQU endian_swap_mask<>(SB), ENDIAN_SWAP_MASK
|
||||
VPXOR VSUM, VSUM, VSUM
|
||||
|
||||
#define LOADED_0 Y2
|
||||
#define LOADED_1 Y3
|
||||
#define LOADED_2 Y4
|
||||
#define LOADED_3 Y5
|
||||
BATCH_64:
|
||||
CMPQ LENGTH, $64
|
||||
JB BATCH_32
|
||||
VPMOVZXWD (PDATA), LOADED_0
|
||||
VPMOVZXWD 16(PDATA), LOADED_1
|
||||
VPMOVZXWD 32(PDATA), LOADED_2
|
||||
VPMOVZXWD 48(PDATA), LOADED_3
|
||||
VPSHUFB ENDIAN_SWAP_MASK, LOADED_0, LOADED_0
|
||||
VPSHUFB ENDIAN_SWAP_MASK, LOADED_1, LOADED_1
|
||||
VPSHUFB ENDIAN_SWAP_MASK, LOADED_2, LOADED_2
|
||||
VPSHUFB ENDIAN_SWAP_MASK, LOADED_3, LOADED_3
|
||||
VPADDD LOADED_0, VSUM, VSUM
|
||||
VPADDD LOADED_1, VSUM, VSUM
|
||||
VPADDD LOADED_2, VSUM, VSUM
|
||||
VPADDD LOADED_3, VSUM, VSUM
|
||||
ADDQ $-64, LENGTH
|
||||
ADDQ $64, PDATA
|
||||
JMP BATCH_64
|
||||
#undef LOADED_0
|
||||
#undef LOADED_1
|
||||
#undef LOADED_2
|
||||
#undef LOADED_3
|
||||
|
||||
#define LOADED_0 Y2
|
||||
#define LOADED_1 Y3
|
||||
BATCH_32:
|
||||
CMPQ LENGTH, $32
|
||||
JB BATCH_16
|
||||
VPMOVZXWD (PDATA), LOADED_0
|
||||
VPMOVZXWD 16(PDATA), LOADED_1
|
||||
VPSHUFB ENDIAN_SWAP_MASK, LOADED_0, LOADED_0
|
||||
VPSHUFB ENDIAN_SWAP_MASK, LOADED_1, LOADED_1
|
||||
VPADDD LOADED_0, VSUM, VSUM
|
||||
VPADDD LOADED_1, VSUM, VSUM
|
||||
ADDQ $-32, LENGTH
|
||||
ADDQ $32, PDATA
|
||||
JMP BATCH_32
|
||||
#undef LOADED_0
|
||||
#undef LOADED_1
|
||||
|
||||
#define LOADED Y2
|
||||
BATCH_16:
|
||||
CMPQ LENGTH, $16
|
||||
JB COLLECT
|
||||
VPMOVZXWD (PDATA), LOADED
|
||||
VPSHUFB ENDIAN_SWAP_MASK, LOADED, LOADED
|
||||
VPADDD LOADED, VSUM, VSUM
|
||||
ADDQ $-16, LENGTH
|
||||
ADDQ $16, PDATA
|
||||
JMP BATCH_16
|
||||
#undef LOADED
|
||||
|
||||
#define EXTRACTED Y2
|
||||
#define EXTRACTED_128 X2
|
||||
#define TEMP_64 DX
|
||||
COLLECT:
|
||||
VEXTRACTI128 $0, VSUM, EXTRACTED_128
|
||||
VPEXTRD $0, EXTRACTED_128, TEMP_64
|
||||
ADDL TEMP_64, RESULT
|
||||
VPEXTRD $1, EXTRACTED_128, TEMP_64
|
||||
ADDL TEMP_64, RESULT
|
||||
VPEXTRD $2, EXTRACTED_128, TEMP_64
|
||||
ADDL TEMP_64, RESULT
|
||||
VPEXTRD $3, EXTRACTED_128, TEMP_64
|
||||
ADDL TEMP_64, RESULT
|
||||
VEXTRACTI128 $1, VSUM, EXTRACTED_128
|
||||
VPEXTRD $0, EXTRACTED_128, TEMP_64
|
||||
ADDL TEMP_64, RESULT
|
||||
VPEXTRD $1, EXTRACTED_128, TEMP_64
|
||||
ADDL TEMP_64, RESULT
|
||||
VPEXTRD $2, EXTRACTED_128, TEMP_64
|
||||
ADDL TEMP_64, RESULT
|
||||
VPEXTRD $3, EXTRACTED_128, TEMP_64
|
||||
ADDL TEMP_64, RESULT
|
||||
#undef EXTRACTED
|
||||
#undef EXTRACTED_128
|
||||
#undef TEMP_64
|
||||
|
||||
#define TEMP DX
|
||||
#define TEMP2 SI
|
||||
BATCH_2:
|
||||
CMPQ LENGTH, $2
|
||||
JB BATCH_1
|
||||
XORQ TEMP, TEMP
|
||||
MOVW (PDATA), TEMP
|
||||
MOVQ TEMP, TEMP2
|
||||
SHRW $8, TEMP2
|
||||
SHLW $8, TEMP
|
||||
ORW TEMP2, TEMP
|
||||
ADDL TEMP, RESULT
|
||||
ADDQ $-2, LENGTH
|
||||
ADDQ $2, PDATA
|
||||
JMP BATCH_2
|
||||
#undef TEMP
|
||||
|
||||
#define TEMP DX
|
||||
BATCH_1:
|
||||
CMPQ LENGTH, $0
|
||||
JZ RETURN
|
||||
XORQ TEMP, TEMP
|
||||
MOVB (PDATA), TEMP
|
||||
SHLW $8, TEMP
|
||||
ADDL TEMP, RESULT
|
||||
#undef TEMP
|
||||
|
||||
RETURN:
|
||||
MOVQ RESULT, result+16(FP)
|
||||
RET
|
|
@ -1,51 +0,0 @@
|
|||
package clashtcpip
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/sys/cpu"
|
||||
)
|
||||
|
||||
func Test_SumAVX2(t *testing.T) {
|
||||
if !cpu.X86.HasAVX2 {
|
||||
t.Skipf("AVX2 unavailable")
|
||||
}
|
||||
|
||||
bytes := make([]byte, chunkSize)
|
||||
|
||||
for size := 0; size <= chunkSize; size++ {
|
||||
for count := 0; count < chunkCount; count++ {
|
||||
_, err := rand.Reader.Read(bytes[:size])
|
||||
if err != nil {
|
||||
t.Skipf("Rand read failed: %v", err)
|
||||
}
|
||||
|
||||
compat := SumCompat(bytes[:size])
|
||||
avx := SumAVX2(bytes[:size])
|
||||
|
||||
if compat != avx {
|
||||
t.Errorf("Sum of length=%d mismatched", size)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_SumAVX2(b *testing.B) {
|
||||
if !cpu.X86.HasAVX2 {
|
||||
b.Skipf("AVX2 unavailable")
|
||||
}
|
||||
|
||||
bytes := make([]byte, chunkSize)
|
||||
|
||||
_, err := rand.Reader.Read(bytes)
|
||||
if err != nil {
|
||||
b.Skipf("Rand read failed: %v", err)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
SumAVX2(bytes)
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
package clashtcpip
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/cpu"
|
||||
)
|
||||
|
||||
//go:noescape
|
||||
func sumAsmNeon(data unsafe.Pointer, length uintptr) uintptr
|
||||
|
||||
func SumNeon(data []byte) uint32 {
|
||||
if len(data) == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
return uint32(sumAsmNeon(unsafe.Pointer(&data[0]), uintptr(len(data))))
|
||||
}
|
||||
|
||||
func init() {
|
||||
if cpu.ARM64.HasASIMD {
|
||||
SumFnc = SumNeon
|
||||
}
|
||||
}
|
|
@ -1,118 +0,0 @@
|
|||
#include "textflag.h"
|
||||
|
||||
// func sumAsmNeon(data unsafe.Pointer, length uintptr) uintptr
|
||||
//
|
||||
// args (8 bytes aligned):
|
||||
// data unsafe.Pointer - 8 bytes - 0 offset
|
||||
// length uintptr - 8 bytes - 8 offset
|
||||
// result uintptr - 8 bytes - 16 offset
|
||||
#define PDATA R0
|
||||
#define LENGTH R1
|
||||
#define RESULT R2
|
||||
#define VSUM V0
|
||||
TEXT ·sumAsmNeon(SB),NOSPLIT,$0-24
|
||||
MOVD data+0(FP), PDATA
|
||||
MOVD length+8(FP), LENGTH
|
||||
MOVD $0, RESULT
|
||||
VMOVQ $0, $0, VSUM
|
||||
|
||||
#define LOADED_0 V1
|
||||
#define LOADED_1 V2
|
||||
#define LOADED_2 V3
|
||||
#define LOADED_3 V4
|
||||
BATCH_32:
|
||||
CMP $32, LENGTH
|
||||
BLO BATCH_16
|
||||
VLD1 (PDATA), [LOADED_0.B8, LOADED_1.B8, LOADED_2.B8, LOADED_3.B8]
|
||||
VREV16 LOADED_0.B8, LOADED_0.B8
|
||||
VREV16 LOADED_1.B8, LOADED_1.B8
|
||||
VREV16 LOADED_2.B8, LOADED_2.B8
|
||||
VREV16 LOADED_3.B8, LOADED_3.B8
|
||||
VUSHLL $0, LOADED_0.H4, LOADED_0.S4
|
||||
VUSHLL $0, LOADED_1.H4, LOADED_1.S4
|
||||
VUSHLL $0, LOADED_2.H4, LOADED_2.S4
|
||||
VUSHLL $0, LOADED_3.H4, LOADED_3.S4
|
||||
VADD LOADED_0.S4, VSUM.S4, VSUM.S4
|
||||
VADD LOADED_1.S4, VSUM.S4, VSUM.S4
|
||||
VADD LOADED_2.S4, VSUM.S4, VSUM.S4
|
||||
VADD LOADED_3.S4, VSUM.S4, VSUM.S4
|
||||
ADD $-32, LENGTH
|
||||
ADD $32, PDATA
|
||||
B BATCH_32
|
||||
#undef LOADED_0
|
||||
#undef LOADED_1
|
||||
#undef LOADED_2
|
||||
#undef LOADED_3
|
||||
|
||||
#define LOADED_0 V1
|
||||
#define LOADED_1 V2
|
||||
BATCH_16:
|
||||
CMP $16, LENGTH
|
||||
BLO BATCH_8
|
||||
VLD1 (PDATA), [LOADED_0.B8, LOADED_1.B8]
|
||||
VREV16 LOADED_0.B8, LOADED_0.B8
|
||||
VREV16 LOADED_1.B8, LOADED_1.B8
|
||||
VUSHLL $0, LOADED_0.H4, LOADED_0.S4
|
||||
VUSHLL $0, LOADED_1.H4, LOADED_1.S4
|
||||
VADD LOADED_0.S4, VSUM.S4, VSUM.S4
|
||||
VADD LOADED_1.S4, VSUM.S4, VSUM.S4
|
||||
ADD $-16, LENGTH
|
||||
ADD $16, PDATA
|
||||
B BATCH_16
|
||||
#undef LOADED_0
|
||||
#undef LOADED_1
|
||||
|
||||
#define LOADED_0 V1
|
||||
BATCH_8:
|
||||
CMP $8, LENGTH
|
||||
BLO BATCH_2
|
||||
VLD1 (PDATA), [LOADED_0.B8]
|
||||
VREV16 LOADED_0.B8, LOADED_0.B8
|
||||
VUSHLL $0, LOADED_0.H4, LOADED_0.S4
|
||||
VADD LOADED_0.S4, VSUM.S4, VSUM.S4
|
||||
ADD $-8, LENGTH
|
||||
ADD $8, PDATA
|
||||
B BATCH_8
|
||||
#undef LOADED_0
|
||||
|
||||
#define LOADED_L R3
|
||||
#define LOADED_H R4
|
||||
BATCH_2:
|
||||
CMP $2, LENGTH
|
||||
BLO BATCH_1
|
||||
MOVBU (PDATA), LOADED_H
|
||||
MOVBU 1(PDATA), LOADED_L
|
||||
LSL $8, LOADED_H
|
||||
ORR LOADED_H, LOADED_L, LOADED_L
|
||||
ADD LOADED_L, RESULT, RESULT
|
||||
ADD $2, PDATA
|
||||
ADD $-2, LENGTH
|
||||
B BATCH_2
|
||||
#undef LOADED_H
|
||||
#undef LOADED_L
|
||||
|
||||
#define LOADED R3
|
||||
BATCH_1:
|
||||
CMP $1, LENGTH
|
||||
BLO COLLECT
|
||||
MOVBU (PDATA), LOADED
|
||||
LSL $8, LOADED
|
||||
ADD LOADED, RESULT, RESULT
|
||||
|
||||
#define EXTRACTED R3
|
||||
COLLECT:
|
||||
VMOV VSUM.S[0], EXTRACTED
|
||||
ADD EXTRACTED, RESULT
|
||||
VMOV VSUM.S[1], EXTRACTED
|
||||
ADD EXTRACTED, RESULT
|
||||
VMOV VSUM.S[2], EXTRACTED
|
||||
ADD EXTRACTED, RESULT
|
||||
VMOV VSUM.S[3], EXTRACTED
|
||||
ADD EXTRACTED, RESULT
|
||||
#undef VSUM
|
||||
#undef PDATA
|
||||
#undef LENGTH
|
||||
|
||||
RETURN:
|
||||
MOVD RESULT, result+16(FP)
|
||||
RET
|
|
@ -1,51 +0,0 @@
|
|||
package clashtcpip
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/sys/cpu"
|
||||
)
|
||||
|
||||
func Test_SumNeon(t *testing.T) {
|
||||
if !cpu.ARM64.HasASIMD {
|
||||
t.Skipf("Neon unavailable")
|
||||
}
|
||||
|
||||
bytes := make([]byte, chunkSize)
|
||||
|
||||
for size := 0; size <= chunkSize; size++ {
|
||||
for count := 0; count < chunkCount; count++ {
|
||||
_, err := rand.Reader.Read(bytes[:size])
|
||||
if err != nil {
|
||||
t.Skipf("Rand read failed: %v", err)
|
||||
}
|
||||
|
||||
compat := SumCompat(bytes[:size])
|
||||
neon := SumNeon(bytes[:size])
|
||||
|
||||
if compat != neon {
|
||||
t.Errorf("Sum of length=%d mismatched", size)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_SumNeon(b *testing.B) {
|
||||
if !cpu.ARM64.HasASIMD {
|
||||
b.Skipf("Neon unavailable")
|
||||
}
|
||||
|
||||
bytes := make([]byte, chunkSize)
|
||||
|
||||
_, err := rand.Reader.Read(bytes)
|
||||
if err != nil {
|
||||
b.Skipf("Rand read failed: %v", err)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
SumNeon(bytes)
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
package clashtcpip
|
||||
|
||||
func SumCompat(b []byte) (sum uint32) {
|
||||
n := len(b)
|
||||
if n&1 != 0 {
|
||||
n--
|
||||
sum += uint32(b[n]) << 8
|
||||
}
|
||||
|
||||
for i := 0; i < n; i += 2 {
|
||||
sum += (uint32(b[i]) << 8) | uint32(b[i+1])
|
||||
}
|
||||
return
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
package clashtcpip
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
chunkSize = 9000
|
||||
chunkCount = 10
|
||||
)
|
||||
|
||||
func Benchmark_SumCompat(b *testing.B) {
|
||||
bytes := make([]byte, chunkSize)
|
||||
|
||||
_, err := rand.Reader.Read(bytes)
|
||||
if err != nil {
|
||||
b.Skipf("Rand read failed: %v", err)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
SumCompat(bytes)
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
package clashtcpip
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
const UDPHeaderSize = 8
|
||||
|
||||
type UDPPacket []byte
|
||||
|
||||
func (p UDPPacket) Length() uint16 {
|
||||
return binary.BigEndian.Uint16(p[4:])
|
||||
}
|
||||
|
||||
func (p UDPPacket) SetLength(length uint16) {
|
||||
binary.BigEndian.PutUint16(p[4:], length)
|
||||
}
|
||||
|
||||
func (p UDPPacket) SourcePort() uint16 {
|
||||
return binary.BigEndian.Uint16(p)
|
||||
}
|
||||
|
||||
func (p UDPPacket) SetSourcePort(port uint16) {
|
||||
binary.BigEndian.PutUint16(p, port)
|
||||
}
|
||||
|
||||
func (p UDPPacket) DestinationPort() uint16 {
|
||||
return binary.BigEndian.Uint16(p[2:])
|
||||
}
|
||||
|
||||
func (p UDPPacket) SetDestinationPort(port uint16) {
|
||||
binary.BigEndian.PutUint16(p[2:], port)
|
||||
}
|
||||
|
||||
func (p UDPPacket) Payload() []byte {
|
||||
return p[UDPHeaderSize:p.Length()]
|
||||
}
|
||||
|
||||
func (p UDPPacket) Checksum() uint16 {
|
||||
return binary.BigEndian.Uint16(p[6:])
|
||||
}
|
||||
|
||||
func (p UDPPacket) SetChecksum(sum [2]byte) {
|
||||
p[6] = sum[0]
|
||||
p[7] = sum[1]
|
||||
}
|
||||
|
||||
func (p UDPPacket) OffloadChecksum() {
|
||||
p.SetChecksum(zeroChecksum)
|
||||
}
|
||||
|
||||
func (p UDPPacket) ResetChecksum(psum uint32) {
|
||||
p.SetChecksum(zeroChecksum)
|
||||
p.SetChecksum(Checksum(psum, p))
|
||||
}
|
||||
|
||||
func (p UDPPacket) Valid() bool {
|
||||
return len(p) >= UDPHeaderSize && uint16(len(p)) >= p.Length()
|
||||
}
|
4
internal/gtcpip/README.md
Normal file
4
internal/gtcpip/README.md
Normal file
|
@ -0,0 +1,4 @@
|
|||
# gtcpip
|
||||
|
||||
Minimal tcpip package kanged from gvisor
|
||||
Version 20241007.0
|
68
internal/gtcpip/checksum/checksum.go
Normal file
68
internal/gtcpip/checksum/checksum.go
Normal file
|
@ -0,0 +1,68 @@
|
|||
// 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 checksum provides the implementation of the encoding and decoding of
|
||||
// network protocol headers.
|
||||
package checksum
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
// Size is the size of a checksum.
|
||||
//
|
||||
// The checksum is held in a uint16 which is 2 bytes.
|
||||
const Size = 2
|
||||
|
||||
// Put puts the checksum in the provided byte slice.
|
||||
func Put(b []byte, xsum uint16) {
|
||||
binary.BigEndian.PutUint16(b, xsum)
|
||||
}
|
||||
|
||||
// Checksum calculates the checksum (as defined in RFC 1071) of the bytes in the
|
||||
// given byte array. This function uses an optimized version of the checksum
|
||||
// algorithm.
|
||||
//
|
||||
// The initial checksum must have been computed on an even number of bytes.
|
||||
func Checksum(buf []byte, initial uint16) uint16 {
|
||||
s, _ := calculateChecksum(buf, false, initial)
|
||||
return s
|
||||
}
|
||||
|
||||
// Checksumer calculates checksum defined in RFC 1071.
|
||||
type Checksumer struct {
|
||||
sum uint16
|
||||
odd bool
|
||||
}
|
||||
|
||||
// Add adds b to checksum.
|
||||
func (c *Checksumer) Add(b []byte) {
|
||||
if len(b) > 0 {
|
||||
c.sum, c.odd = calculateChecksum(b, c.odd, c.sum)
|
||||
}
|
||||
}
|
||||
|
||||
// Checksum returns the latest checksum value.
|
||||
func (c *Checksumer) Checksum() uint16 {
|
||||
return c.sum
|
||||
}
|
||||
|
||||
// Combine combines the two uint16 to form their checksum. This is done
|
||||
// by adding them and the carry.
|
||||
//
|
||||
// Note that checksum a must have been computed on an even number of bytes.
|
||||
func Combine(a, b uint16) uint16 {
|
||||
v := uint32(a) + uint32(b)
|
||||
return uint16(v + v>>16)
|
||||
}
|
182
internal/gtcpip/checksum/checksum_unsafe.go
Normal file
182
internal/gtcpip/checksum/checksum_unsafe.go
Normal file
|
@ -0,0 +1,182 @@
|
|||
// Copyright 2023 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 checksum
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"math/bits"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Note: odd indicates whether initial is a partial checksum over an odd number
|
||||
// of bytes.
|
||||
func calculateChecksum(buf []byte, odd bool, initial uint16) (uint16, bool) {
|
||||
// Use a larger-than-uint16 accumulator to benefit from parallel summation
|
||||
// as described in RFC 1071 1.2.C.
|
||||
acc := uint64(initial)
|
||||
|
||||
// Handle an odd number of previously-summed bytes, and get the return
|
||||
// value for odd.
|
||||
if odd {
|
||||
acc += uint64(buf[0])
|
||||
buf = buf[1:]
|
||||
}
|
||||
odd = len(buf)&1 != 0
|
||||
|
||||
// Aligning &buf[0] below is much simpler if len(buf) >= 8; special-case
|
||||
// smaller bufs.
|
||||
if len(buf) < 8 {
|
||||
if len(buf) >= 4 {
|
||||
acc += (uint64(buf[0]) << 8) + uint64(buf[1])
|
||||
acc += (uint64(buf[2]) << 8) + uint64(buf[3])
|
||||
buf = buf[4:]
|
||||
}
|
||||
if len(buf) >= 2 {
|
||||
acc += (uint64(buf[0]) << 8) + uint64(buf[1])
|
||||
buf = buf[2:]
|
||||
}
|
||||
if len(buf) >= 1 {
|
||||
acc += uint64(buf[0]) << 8
|
||||
// buf = buf[1:] is skipped because it's unused and nogo will
|
||||
// complain.
|
||||
}
|
||||
return reduce(acc), odd
|
||||
}
|
||||
|
||||
// On little-endian architectures, multi-byte loads from buf will load
|
||||
// bytes in the wrong order. Rather than byte-swap after each load (slow),
|
||||
// we byte-swap the accumulator before summing any bytes and byte-swap it
|
||||
// back before returning, which still produces the correct result as
|
||||
// described in RFC 1071 1.2.B "Byte Order Independence".
|
||||
//
|
||||
// acc is at most a uint16 + a uint8, so its upper 32 bits must be 0s. We
|
||||
// preserve this property by byte-swapping only the lower 32 bits of acc,
|
||||
// so that additions to acc performed during alignment can't overflow.
|
||||
acc = uint64(bswapIfLittleEndian32(uint32(acc)))
|
||||
|
||||
// Align &buf[0] to an 8-byte boundary.
|
||||
bswapped := false
|
||||
if sliceAddr(buf)&1 != 0 {
|
||||
// Compute the rest of the partial checksum with bytes swapped, and
|
||||
// swap back before returning; see the last paragraph of
|
||||
// RFC 1071 1.2.B.
|
||||
acc = uint64(bits.ReverseBytes32(uint32(acc)))
|
||||
bswapped = true
|
||||
// No `<< 8` here due to the byte swap we just did.
|
||||
acc += uint64(bswapIfLittleEndian16(uint16(buf[0])))
|
||||
buf = buf[1:]
|
||||
}
|
||||
if sliceAddr(buf)&2 != 0 {
|
||||
acc += uint64(*(*uint16)(unsafe.Pointer(&buf[0])))
|
||||
buf = buf[2:]
|
||||
}
|
||||
if sliceAddr(buf)&4 != 0 {
|
||||
acc += uint64(*(*uint32)(unsafe.Pointer(&buf[0])))
|
||||
buf = buf[4:]
|
||||
}
|
||||
|
||||
// Sum 64 bytes at a time. Beyond this point, additions to acc may
|
||||
// overflow, so we have to handle carrying.
|
||||
for len(buf) >= 64 {
|
||||
var carry uint64
|
||||
acc, carry = bits.Add64(acc, *(*uint64)(unsafe.Pointer(&buf[0])), 0)
|
||||
acc, carry = bits.Add64(acc, *(*uint64)(unsafe.Pointer(&buf[8])), carry)
|
||||
acc, carry = bits.Add64(acc, *(*uint64)(unsafe.Pointer(&buf[16])), carry)
|
||||
acc, carry = bits.Add64(acc, *(*uint64)(unsafe.Pointer(&buf[24])), carry)
|
||||
acc, carry = bits.Add64(acc, *(*uint64)(unsafe.Pointer(&buf[32])), carry)
|
||||
acc, carry = bits.Add64(acc, *(*uint64)(unsafe.Pointer(&buf[40])), carry)
|
||||
acc, carry = bits.Add64(acc, *(*uint64)(unsafe.Pointer(&buf[48])), carry)
|
||||
acc, carry = bits.Add64(acc, *(*uint64)(unsafe.Pointer(&buf[56])), carry)
|
||||
acc, _ = bits.Add64(acc, 0, carry)
|
||||
buf = buf[64:]
|
||||
}
|
||||
|
||||
// Sum the remaining 0-63 bytes.
|
||||
if len(buf) >= 32 {
|
||||
var carry uint64
|
||||
acc, carry = bits.Add64(acc, *(*uint64)(unsafe.Pointer(&buf[0])), 0)
|
||||
acc, carry = bits.Add64(acc, *(*uint64)(unsafe.Pointer(&buf[8])), carry)
|
||||
acc, carry = bits.Add64(acc, *(*uint64)(unsafe.Pointer(&buf[16])), carry)
|
||||
acc, carry = bits.Add64(acc, *(*uint64)(unsafe.Pointer(&buf[24])), carry)
|
||||
acc, _ = bits.Add64(acc, 0, carry)
|
||||
buf = buf[32:]
|
||||
}
|
||||
if len(buf) >= 16 {
|
||||
var carry uint64
|
||||
acc, carry = bits.Add64(acc, *(*uint64)(unsafe.Pointer(&buf[0])), 0)
|
||||
acc, carry = bits.Add64(acc, *(*uint64)(unsafe.Pointer(&buf[8])), carry)
|
||||
acc, _ = bits.Add64(acc, 0, carry)
|
||||
buf = buf[16:]
|
||||
}
|
||||
if len(buf) >= 8 {
|
||||
var carry uint64
|
||||
acc, carry = bits.Add64(acc, *(*uint64)(unsafe.Pointer(&buf[0])), 0)
|
||||
acc, _ = bits.Add64(acc, 0, carry)
|
||||
buf = buf[8:]
|
||||
}
|
||||
if len(buf) >= 4 {
|
||||
var carry uint64
|
||||
acc, carry = bits.Add64(acc, uint64(*(*uint32)(unsafe.Pointer(&buf[0]))), 0)
|
||||
acc, _ = bits.Add64(acc, 0, carry)
|
||||
buf = buf[4:]
|
||||
}
|
||||
if len(buf) >= 2 {
|
||||
var carry uint64
|
||||
acc, carry = bits.Add64(acc, uint64(*(*uint16)(unsafe.Pointer(&buf[0]))), 0)
|
||||
acc, _ = bits.Add64(acc, 0, carry)
|
||||
buf = buf[2:]
|
||||
}
|
||||
if len(buf) >= 1 {
|
||||
// bswapIfBigEndian16(buf[0]) == bswapIfLittleEndian16(buf[0]<<8).
|
||||
var carry uint64
|
||||
acc, carry = bits.Add64(acc, uint64(bswapIfBigEndian16(uint16(buf[0]))), 0)
|
||||
acc, _ = bits.Add64(acc, 0, carry)
|
||||
// buf = buf[1:] is skipped because it's unused and nogo will complain.
|
||||
}
|
||||
|
||||
// Reduce the checksum to 16 bits and undo byte swaps before returning.
|
||||
acc16 := bswapIfLittleEndian16(reduce(acc))
|
||||
if bswapped {
|
||||
acc16 = bits.ReverseBytes16(acc16)
|
||||
}
|
||||
return acc16, odd
|
||||
}
|
||||
|
||||
func reduce(acc uint64) uint16 {
|
||||
// Ideally we would do:
|
||||
// return uint16(acc>>48) +' uint16(acc>>32) +' uint16(acc>>16) +' uint16(acc)
|
||||
// for more instruction-level parallelism; however, there is no
|
||||
// bits.Add16().
|
||||
acc = (acc >> 32) + (acc & 0xffff_ffff) // at most 0x1_ffff_fffe
|
||||
acc32 := uint32(acc>>32 + acc) // at most 0xffff_ffff
|
||||
acc32 = (acc32 >> 16) + (acc32 & 0xffff) // at most 0x1_fffe
|
||||
return uint16(acc32>>16 + acc32) // at most 0xffff
|
||||
}
|
||||
|
||||
func bswapIfLittleEndian32(val uint32) uint32 {
|
||||
return binary.BigEndian.Uint32((*[4]byte)(unsafe.Pointer(&val))[:])
|
||||
}
|
||||
|
||||
func bswapIfLittleEndian16(val uint16) uint16 {
|
||||
return binary.BigEndian.Uint16((*[2]byte)(unsafe.Pointer(&val))[:])
|
||||
}
|
||||
|
||||
func bswapIfBigEndian16(val uint16) uint16 {
|
||||
return binary.LittleEndian.Uint16((*[2]byte)(unsafe.Pointer(&val))[:])
|
||||
}
|
||||
|
||||
func sliceAddr(buf []byte) uintptr {
|
||||
return uintptr(unsafe.Pointer(unsafe.SliceData(buf)))
|
||||
}
|
46
internal/gtcpip/errors.go
Normal file
46
internal/gtcpip/errors.go
Normal file
|
@ -0,0 +1,46 @@
|
|||
// Copyright 2021 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 tcpip
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Error represents an error in the netstack error space.
|
||||
//
|
||||
// The error interface is intentionally omitted to avoid loss of type
|
||||
// information that would occur if these errors were passed as error.
|
||||
type Error interface {
|
||||
isError()
|
||||
|
||||
// IgnoreStats indicates whether this error should be included in failure
|
||||
// counts in tcpip.Stats structs.
|
||||
IgnoreStats() bool
|
||||
|
||||
fmt.Stringer
|
||||
}
|
||||
|
||||
// ErrBadAddress indicates a bad address was provided.
|
||||
//
|
||||
// +stateify savable
|
||||
type ErrBadAddress struct{}
|
||||
|
||||
func (*ErrBadAddress) isError() {}
|
||||
|
||||
// IgnoreStats implements Error.
|
||||
func (*ErrBadAddress) IgnoreStats() bool {
|
||||
return false
|
||||
}
|
||||
func (*ErrBadAddress) String() string { return "bad address" }
|
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())
|
||||
}
|
62
internal/gtcpip/seqnum/seqnum.go
Normal file
62
internal/gtcpip/seqnum/seqnum.go
Normal file
|
@ -0,0 +1,62 @@
|
|||
// 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 seqnum defines the types and methods for TCP sequence numbers such
|
||||
// that they fit in 32-bit words and work properly when overflows occur.
|
||||
package seqnum
|
||||
|
||||
// Value represents the value of a sequence number.
|
||||
type Value uint32
|
||||
|
||||
// Size represents the size (length) of a sequence number window.
|
||||
type Size uint32
|
||||
|
||||
// LessThan checks if v is before w, i.e., v < w.
|
||||
func (v Value) LessThan(w Value) bool {
|
||||
return int32(v-w) < 0
|
||||
}
|
||||
|
||||
// LessThanEq returns true if v==w or v is before i.e., v < w.
|
||||
func (v Value) LessThanEq(w Value) bool {
|
||||
if v == w {
|
||||
return true
|
||||
}
|
||||
return v.LessThan(w)
|
||||
}
|
||||
|
||||
// InRange checks if v is in the range [a,b), i.e., a <= v < b.
|
||||
func (v Value) InRange(a, b Value) bool {
|
||||
return v-a < b-a
|
||||
}
|
||||
|
||||
// InWindow checks if v is in the window that starts at 'first' and spans 'size'
|
||||
// sequence numbers.
|
||||
func (v Value) InWindow(first Value, size Size) bool {
|
||||
return v.InRange(first, first.Add(size))
|
||||
}
|
||||
|
||||
// Add calculates the sequence number following the [v, v+s) window.
|
||||
func (v Value) Add(s Size) Value {
|
||||
return v + Value(s)
|
||||
}
|
||||
|
||||
// Size calculates the size of the window defined by [v, w).
|
||||
func (v Value) Size(w Value) Size {
|
||||
return Size(w - v)
|
||||
}
|
||||
|
||||
// UpdateForward updates v such that it becomes v + s.
|
||||
func (v *Value) UpdateForward(s Size) {
|
||||
*v += Value(s)
|
||||
}
|
573
internal/gtcpip/tcpip.go
Normal file
573
internal/gtcpip/tcpip.go
Normal file
|
@ -0,0 +1,573 @@
|
|||
// Copyright 2024 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 tcpip
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/bits"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Using the header package here would cause an import cycle.
|
||||
const (
|
||||
ipv4AddressSize = 4
|
||||
ipv4ProtocolNumber = 0x0800
|
||||
ipv6AddressSize = 16
|
||||
ipv6ProtocolNumber = 0x86dd
|
||||
)
|
||||
|
||||
const (
|
||||
// LinkAddressSize is the size of a MAC address.
|
||||
LinkAddressSize = 6
|
||||
)
|
||||
|
||||
// Known IP address.
|
||||
var (
|
||||
IPv4Zero = []byte{0, 0, 0, 0}
|
||||
IPv6Zero = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
)
|
||||
|
||||
// Errors related to Subnet
|
||||
var (
|
||||
errSubnetLengthMismatch = errors.New("subnet length of address and mask differ")
|
||||
errSubnetAddressMasked = errors.New("subnet address has bits set outside the mask")
|
||||
)
|
||||
|
||||
// TransportProtocolNumber is the number of a transport protocol.
|
||||
type TransportProtocolNumber uint32
|
||||
|
||||
// NetworkProtocolNumber is the EtherType of a network protocol in an Ethernet
|
||||
// frame.
|
||||
//
|
||||
// See: https://www.iana.org/assignments/ieee-802-numbers/ieee-802-numbers.xhtml
|
||||
type NetworkProtocolNumber uint32
|
||||
|
||||
// MonotonicTime is a monotonic clock reading.
|
||||
//
|
||||
// +stateify savable
|
||||
type MonotonicTime struct {
|
||||
nanoseconds int64
|
||||
}
|
||||
|
||||
// String implements Stringer.
|
||||
func (mt MonotonicTime) String() string {
|
||||
return strconv.FormatInt(mt.nanoseconds, 10)
|
||||
}
|
||||
|
||||
// MonotonicTimeInfinite returns the monotonic timestamp as far away in the
|
||||
// future as possible.
|
||||
func MonotonicTimeInfinite() MonotonicTime {
|
||||
return MonotonicTime{nanoseconds: math.MaxInt64}
|
||||
}
|
||||
|
||||
// Before reports whether the monotonic clock reading mt is before u.
|
||||
func (mt MonotonicTime) Before(u MonotonicTime) bool {
|
||||
return mt.nanoseconds < u.nanoseconds
|
||||
}
|
||||
|
||||
// After reports whether the monotonic clock reading mt is after u.
|
||||
func (mt MonotonicTime) After(u MonotonicTime) bool {
|
||||
return mt.nanoseconds > u.nanoseconds
|
||||
}
|
||||
|
||||
// Add returns the monotonic clock reading mt+d.
|
||||
func (mt MonotonicTime) Add(d time.Duration) MonotonicTime {
|
||||
return MonotonicTime{
|
||||
nanoseconds: time.Unix(0, mt.nanoseconds).Add(d).Sub(time.Unix(0, 0)).Nanoseconds(),
|
||||
}
|
||||
}
|
||||
|
||||
// Sub returns the duration mt-u. If the result exceeds the maximum (or minimum)
|
||||
// value that can be stored in a Duration, the maximum (or minimum) duration
|
||||
// will be returned. To compute t-d for a duration d, use t.Add(-d).
|
||||
func (mt MonotonicTime) Sub(u MonotonicTime) time.Duration {
|
||||
return time.Unix(0, mt.nanoseconds).Sub(time.Unix(0, u.nanoseconds))
|
||||
}
|
||||
|
||||
// Milliseconds returns the time in milliseconds.
|
||||
func (mt MonotonicTime) Milliseconds() int64 {
|
||||
return mt.nanoseconds / 1e6
|
||||
}
|
||||
|
||||
// A Clock provides the current time and schedules work for execution.
|
||||
//
|
||||
// Times returned by a Clock should always be used for application-visible
|
||||
// time. Only monotonic times should be used for netstack internal timekeeping.
|
||||
type Clock interface {
|
||||
// Now returns the current local time.
|
||||
Now() time.Time
|
||||
|
||||
// NowMonotonic returns the current monotonic clock reading.
|
||||
NowMonotonic() MonotonicTime
|
||||
|
||||
// AfterFunc waits for the duration to elapse and then calls f in its own
|
||||
// goroutine. It returns a Timer that can be used to cancel the call using
|
||||
// its Stop method.
|
||||
AfterFunc(d time.Duration, f func()) Timer
|
||||
}
|
||||
|
||||
// Timer represents a single event. A Timer must be created with
|
||||
// Clock.AfterFunc.
|
||||
type Timer interface {
|
||||
// Stop prevents the Timer from firing. It returns true if the call stops the
|
||||
// timer, false if the timer has already expired or been stopped.
|
||||
//
|
||||
// If Stop returns false, then the timer has already expired and the function
|
||||
// f of Clock.AfterFunc(d, f) has been started in its own goroutine; Stop
|
||||
// does not wait for f to complete before returning. If the caller needs to
|
||||
// know whether f is completed, it must coordinate with f explicitly.
|
||||
Stop() bool
|
||||
|
||||
// Reset changes the timer to expire after duration d.
|
||||
//
|
||||
// Reset should be invoked only on stopped or expired timers. If the timer is
|
||||
// known to have expired, Reset can be used directly. Otherwise, the caller
|
||||
// must coordinate with the function f of Clock.AfterFunc(d, f).
|
||||
Reset(d time.Duration)
|
||||
}
|
||||
|
||||
// Address is a byte slice cast as a string that represents the address of a
|
||||
// network node. Or, in the case of unix endpoints, it may represent a path.
|
||||
//
|
||||
// +stateify savable
|
||||
type Address struct {
|
||||
addr [16]byte
|
||||
length int
|
||||
}
|
||||
|
||||
// AddrFrom4 converts addr to an Address.
|
||||
func AddrFrom4(addr [4]byte) Address {
|
||||
ret := Address{
|
||||
length: 4,
|
||||
}
|
||||
// It's guaranteed that copy will return 4.
|
||||
copy(ret.addr[:], addr[:])
|
||||
return ret
|
||||
}
|
||||
|
||||
// AddrFrom4Slice converts addr to an Address. It panics if len(addr) != 4.
|
||||
func AddrFrom4Slice(addr []byte) Address {
|
||||
if len(addr) != 4 {
|
||||
panic(fmt.Sprintf("bad address length for address %v", addr))
|
||||
}
|
||||
ret := Address{
|
||||
length: 4,
|
||||
}
|
||||
// It's guaranteed that copy will return 4.
|
||||
copy(ret.addr[:], addr)
|
||||
return ret
|
||||
}
|
||||
|
||||
// AddrFrom16 converts addr to an Address.
|
||||
func AddrFrom16(addr [16]byte) Address {
|
||||
ret := Address{
|
||||
length: 16,
|
||||
}
|
||||
// It's guaranteed that copy will return 16.
|
||||
copy(ret.addr[:], addr[:])
|
||||
return ret
|
||||
}
|
||||
|
||||
// AddrFrom16Slice converts addr to an Address. It panics if len(addr) != 16.
|
||||
func AddrFrom16Slice(addr []byte) Address {
|
||||
if len(addr) != 16 {
|
||||
panic(fmt.Sprintf("bad address length for address %v", addr))
|
||||
}
|
||||
ret := Address{
|
||||
length: 16,
|
||||
}
|
||||
// It's guaranteed that copy will return 16.
|
||||
copy(ret.addr[:], addr)
|
||||
return ret
|
||||
}
|
||||
|
||||
// AddrFromSlice converts addr to an Address. It returns the Address zero value
|
||||
// if len(addr) != 4 or 16.
|
||||
func AddrFromSlice(addr []byte) Address {
|
||||
switch len(addr) {
|
||||
case ipv4AddressSize:
|
||||
return AddrFrom4Slice(addr)
|
||||
case ipv6AddressSize:
|
||||
return AddrFrom16Slice(addr)
|
||||
}
|
||||
return Address{}
|
||||
}
|
||||
|
||||
// As4 returns a as a 4 byte array. It panics if the address length is not 4.
|
||||
func (a Address) As4() [4]byte {
|
||||
if a.Len() != 4 {
|
||||
panic(fmt.Sprintf("bad address length for address %v", a.addr))
|
||||
}
|
||||
return [4]byte(a.addr[:4])
|
||||
}
|
||||
|
||||
// As16 returns a as a 16 byte array. It panics if the address length is not 16.
|
||||
func (a Address) As16() [16]byte {
|
||||
if a.Len() != 16 {
|
||||
panic(fmt.Sprintf("bad address length for address %v", a.addr))
|
||||
}
|
||||
return [16]byte(a.addr[:16])
|
||||
}
|
||||
|
||||
// AsSlice returns a as a byte slice. Callers should be careful as it can
|
||||
// return a window into existing memory.
|
||||
//
|
||||
// +checkescape
|
||||
func (a *Address) AsSlice() []byte {
|
||||
return a.addr[:a.length]
|
||||
}
|
||||
|
||||
// BitLen returns the length in bits of a.
|
||||
func (a Address) BitLen() int {
|
||||
return a.Len() * 8
|
||||
}
|
||||
|
||||
// Len returns the length in bytes of a.
|
||||
func (a Address) Len() int {
|
||||
return a.length
|
||||
}
|
||||
|
||||
// WithPrefix returns the address with a prefix that represents a point subnet.
|
||||
func (a Address) WithPrefix() AddressWithPrefix {
|
||||
return AddressWithPrefix{
|
||||
Address: a,
|
||||
PrefixLen: a.BitLen(),
|
||||
}
|
||||
}
|
||||
|
||||
// Unspecified returns true if the address is unspecified.
|
||||
func (a Address) Unspecified() bool {
|
||||
for _, b := range a.addr {
|
||||
if b != 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Equal returns whether a and other are equal. It exists for use by the cmp
|
||||
// library.
|
||||
func (a Address) Equal(other Address) bool {
|
||||
return a == other
|
||||
}
|
||||
|
||||
// MatchingPrefix returns the matching prefix length in bits.
|
||||
//
|
||||
// Panics if b and a have different lengths.
|
||||
func (a Address) MatchingPrefix(b Address) uint8 {
|
||||
const bitsInAByte = 8
|
||||
|
||||
if a.Len() != b.Len() {
|
||||
panic(fmt.Sprintf("addresses %s and %s do not have the same length", a, b))
|
||||
}
|
||||
|
||||
var prefix uint8
|
||||
for i := 0; i < a.length; i++ {
|
||||
aByte := a.addr[i]
|
||||
bByte := b.addr[i]
|
||||
|
||||
if aByte == bByte {
|
||||
prefix += bitsInAByte
|
||||
continue
|
||||
}
|
||||
|
||||
// Count the remaining matching bits in the byte from MSbit to LSBbit.
|
||||
mask := uint8(1) << (bitsInAByte - 1)
|
||||
for {
|
||||
if aByte&mask == bByte&mask {
|
||||
prefix++
|
||||
mask >>= 1
|
||||
continue
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
return prefix
|
||||
}
|
||||
|
||||
// AddressMask is a bitmask for an address.
|
||||
//
|
||||
// +stateify savable
|
||||
type AddressMask struct {
|
||||
mask [16]byte
|
||||
length int
|
||||
}
|
||||
|
||||
// MaskFrom returns a Mask based on str.
|
||||
//
|
||||
// MaskFrom may allocate, and so should not be in hot paths.
|
||||
func MaskFrom(str string) AddressMask {
|
||||
mask := AddressMask{length: len(str)}
|
||||
copy(mask.mask[:], str)
|
||||
return mask
|
||||
}
|
||||
|
||||
// MaskFromBytes returns a Mask based on bs.
|
||||
func MaskFromBytes(bs []byte) AddressMask {
|
||||
mask := AddressMask{length: len(bs)}
|
||||
copy(mask.mask[:], bs)
|
||||
return mask
|
||||
}
|
||||
|
||||
// String implements Stringer.
|
||||
func (m AddressMask) String() string {
|
||||
return fmt.Sprintf("%x", m.mask)
|
||||
}
|
||||
|
||||
// AsSlice returns a as a byte slice. Callers should be careful as it can
|
||||
// return a window into existing memory.
|
||||
func (m *AddressMask) AsSlice() []byte {
|
||||
return []byte(m.mask[:m.length])
|
||||
}
|
||||
|
||||
// BitLen returns the length of the mask in bits.
|
||||
func (m AddressMask) BitLen() int {
|
||||
return m.length * 8
|
||||
}
|
||||
|
||||
// Len returns the length of the mask in bytes.
|
||||
func (m AddressMask) Len() int {
|
||||
return m.length
|
||||
}
|
||||
|
||||
// Prefix returns the number of bits before the first host bit.
|
||||
func (m AddressMask) Prefix() int {
|
||||
p := 0
|
||||
for _, b := range m.mask[:m.length] {
|
||||
p += bits.LeadingZeros8(^b)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// Equal returns whether m and other are equal. It exists for use by the cmp
|
||||
// library.
|
||||
func (m AddressMask) Equal(other AddressMask) bool {
|
||||
return m == other
|
||||
}
|
||||
|
||||
// Subnet is a subnet defined by its address and mask.
|
||||
//
|
||||
// +stateify savable
|
||||
type Subnet struct {
|
||||
address Address
|
||||
mask AddressMask
|
||||
}
|
||||
|
||||
// NewSubnet creates a new Subnet, checking that the address and mask are the same length.
|
||||
func NewSubnet(a Address, m AddressMask) (Subnet, error) {
|
||||
if a.Len() != m.Len() {
|
||||
return Subnet{}, errSubnetLengthMismatch
|
||||
}
|
||||
for i := 0; i < a.Len(); i++ {
|
||||
if a.addr[i]&^m.mask[i] != 0 {
|
||||
return Subnet{}, errSubnetAddressMasked
|
||||
}
|
||||
}
|
||||
return Subnet{a, m}, nil
|
||||
}
|
||||
|
||||
// String implements Stringer.
|
||||
func (s Subnet) String() string {
|
||||
return fmt.Sprintf("%s/%d", s.ID(), s.Prefix())
|
||||
}
|
||||
|
||||
// Contains returns true iff the address is of the same length and matches the
|
||||
// subnet address and mask.
|
||||
func (s *Subnet) Contains(a Address) bool {
|
||||
if a.Len() != s.address.Len() {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < a.Len(); i++ {
|
||||
if a.addr[i]&s.mask.mask[i] != s.address.addr[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ID returns the subnet ID.
|
||||
func (s *Subnet) ID() Address {
|
||||
return s.address
|
||||
}
|
||||
|
||||
// Bits returns the number of ones (network bits) and zeros (host bits) in the
|
||||
// subnet mask.
|
||||
func (s *Subnet) Bits() (ones int, zeros int) {
|
||||
ones = s.mask.Prefix()
|
||||
return ones, s.mask.BitLen() - ones
|
||||
}
|
||||
|
||||
// Prefix returns the number of bits before the first host bit.
|
||||
func (s *Subnet) Prefix() int {
|
||||
return s.mask.Prefix()
|
||||
}
|
||||
|
||||
// Mask returns the subnet mask.
|
||||
func (s *Subnet) Mask() AddressMask {
|
||||
return s.mask
|
||||
}
|
||||
|
||||
// Broadcast returns the subnet's broadcast address.
|
||||
func (s *Subnet) Broadcast() Address {
|
||||
addrCopy := s.address
|
||||
for i := 0; i < addrCopy.Len(); i++ {
|
||||
addrCopy.addr[i] |= ^s.mask.mask[i]
|
||||
}
|
||||
return addrCopy
|
||||
}
|
||||
|
||||
// IsBroadcast returns true if the address is considered a broadcast address.
|
||||
func (s *Subnet) IsBroadcast(address Address) bool {
|
||||
// Only IPv4 supports the notion of a broadcast address.
|
||||
if address.Len() != ipv4AddressSize {
|
||||
return false
|
||||
}
|
||||
|
||||
// Normally, we would just compare address with the subnet's broadcast
|
||||
// address but there is an exception where a simple comparison is not
|
||||
// correct. This exception is for /31 and /32 IPv4 subnets where all
|
||||
// addresses are considered valid host addresses.
|
||||
//
|
||||
// For /31 subnets, the case is easy. RFC 3021 Section 2.1 states that
|
||||
// both addresses in a /31 subnet "MUST be interpreted as host addresses."
|
||||
//
|
||||
// For /32, the case is a bit more vague. RFC 3021 makes no mention of /32
|
||||
// subnets. However, the same reasoning applies - if an exception is not
|
||||
// made, then there do not exist any host addresses in a /32 subnet. RFC
|
||||
// 4632 Section 3.1 also vaguely implies this interpretation by referring
|
||||
// to addresses in /32 subnets as "host routes."
|
||||
return s.Prefix() <= 30 && s.Broadcast() == address
|
||||
}
|
||||
|
||||
// Equal returns true if this Subnet is equal to the given Subnet.
|
||||
func (s Subnet) Equal(o Subnet) bool {
|
||||
// If this changes, update Route.Equal accordingly.
|
||||
return s == o
|
||||
}
|
||||
|
||||
// LinkAddress is a byte slice cast as a string that represents a link address.
|
||||
// It is typically a 6-byte MAC address.
|
||||
type LinkAddress string
|
||||
|
||||
// String implements the fmt.Stringer interface.
|
||||
func (a LinkAddress) String() string {
|
||||
switch len(a) {
|
||||
case 6:
|
||||
return fmt.Sprintf("%02x:%02x:%02x:%02x:%02x:%02x", a[0], a[1], a[2], a[3], a[4], a[5])
|
||||
default:
|
||||
return fmt.Sprintf("%x", []byte(a))
|
||||
}
|
||||
}
|
||||
|
||||
// ParseMACAddress parses an IEEE 802 address.
|
||||
//
|
||||
// It must be in the format aa:bb:cc:dd:ee:ff or aa-bb-cc-dd-ee-ff.
|
||||
func ParseMACAddress(s string) (LinkAddress, error) {
|
||||
parts := strings.FieldsFunc(s, func(c rune) bool {
|
||||
return c == ':' || c == '-'
|
||||
})
|
||||
if len(parts) != LinkAddressSize {
|
||||
return "", fmt.Errorf("inconsistent parts: %s", s)
|
||||
}
|
||||
addr := make([]byte, 0, len(parts))
|
||||
for _, part := range parts {
|
||||
u, err := strconv.ParseUint(part, 16, 8)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("invalid hex digits: %s", s)
|
||||
}
|
||||
addr = append(addr, byte(u))
|
||||
}
|
||||
return LinkAddress(addr), nil
|
||||
}
|
||||
|
||||
// GetRandMacAddr returns a mac address that can be used for local virtual devices.
|
||||
func GetRandMacAddr() LinkAddress {
|
||||
mac := make(net.HardwareAddr, LinkAddressSize)
|
||||
rand.Read(mac) // Fill with random data.
|
||||
mac[0] &^= 0x1 // Clear multicast bit.
|
||||
mac[0] |= 0x2 // Set local assignment bit (IEEE802).
|
||||
return LinkAddress(mac)
|
||||
}
|
||||
|
||||
// AddressWithPrefix is an address with its subnet prefix length.
|
||||
//
|
||||
// +stateify savable
|
||||
type AddressWithPrefix struct {
|
||||
// Address is a network address.
|
||||
Address Address
|
||||
|
||||
// PrefixLen is the subnet prefix length.
|
||||
PrefixLen int
|
||||
}
|
||||
|
||||
// String implements the fmt.Stringer interface.
|
||||
func (a AddressWithPrefix) String() string {
|
||||
return fmt.Sprintf("%s/%d", a.Address, a.PrefixLen)
|
||||
}
|
||||
|
||||
// Subnet converts the address and prefix into a Subnet value and returns it.
|
||||
func (a AddressWithPrefix) Subnet() Subnet {
|
||||
addrLen := a.Address.length
|
||||
if a.PrefixLen <= 0 {
|
||||
return Subnet{
|
||||
address: Address{length: addrLen},
|
||||
mask: AddressMask{length: addrLen},
|
||||
}
|
||||
}
|
||||
if a.PrefixLen >= addrLen*8 {
|
||||
sub := Subnet{
|
||||
address: a.Address,
|
||||
mask: AddressMask{length: addrLen},
|
||||
}
|
||||
for i := 0; i < addrLen; i++ {
|
||||
sub.mask.mask[i] = 0xff
|
||||
}
|
||||
return sub
|
||||
}
|
||||
|
||||
sa := Address{length: addrLen}
|
||||
sm := AddressMask{length: addrLen}
|
||||
n := uint(a.PrefixLen)
|
||||
for i := 0; i < addrLen; i++ {
|
||||
if n >= 8 {
|
||||
sa.addr[i] = a.Address.addr[i]
|
||||
sm.mask[i] = 0xff
|
||||
n -= 8
|
||||
continue
|
||||
}
|
||||
sm.mask[i] = ^byte(0xff >> n)
|
||||
sa.addr[i] = a.Address.addr[i] & sm.mask[i]
|
||||
n = 0
|
||||
}
|
||||
|
||||
// For extra caution, call NewSubnet rather than directly creating the Subnet
|
||||
// value. If that fails it indicates a serious bug in this code, so panic is
|
||||
// in order.
|
||||
s, err := NewSubnet(sa, sm)
|
||||
if err != nil {
|
||||
panic("invalid subnet: " + err.Error())
|
||||
}
|
||||
return s
|
||||
}
|
|
@ -3,20 +3,21 @@ package tun
|
|||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/sagernet/sing-tun/internal/clashtcpip"
|
||||
"github.com/sagernet/sing-tun/internal/gtcpip"
|
||||
"github.com/sagernet/sing-tun/internal/gtcpip/header"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
func NetworkName(network uint8) string {
|
||||
switch network {
|
||||
case clashtcpip.TCP:
|
||||
switch tcpip.TransportProtocolNumber(network) {
|
||||
case header.TCPProtocolNumber:
|
||||
return N.NetworkTCP
|
||||
case clashtcpip.UDP:
|
||||
case header.UDPProtocolNumber:
|
||||
return N.NetworkUDP
|
||||
case clashtcpip.ICMP:
|
||||
case header.ICMPv4ProtocolNumber:
|
||||
return N.NetworkICMPv4
|
||||
case clashtcpip.ICMPv6:
|
||||
case header.ICMPv6ProtocolNumber:
|
||||
return N.NetworkICMPv6
|
||||
}
|
||||
return F.ToString(network)
|
||||
|
@ -25,13 +26,13 @@ func NetworkName(network uint8) string {
|
|||
func NetworkFromName(name string) uint8 {
|
||||
switch name {
|
||||
case N.NetworkTCP:
|
||||
return clashtcpip.TCP
|
||||
return uint8(header.TCPProtocolNumber)
|
||||
case N.NetworkUDP:
|
||||
return clashtcpip.UDP
|
||||
return uint8(header.UDPProtocolNumber)
|
||||
case N.NetworkICMPv4:
|
||||
return clashtcpip.ICMP
|
||||
return uint8(header.ICMPv4ProtocolNumber)
|
||||
case N.NetworkICMPv6:
|
||||
return clashtcpip.ICMPv6
|
||||
return uint8(header.ICMPv6ProtocolNumber)
|
||||
}
|
||||
parseNetwork, err := strconv.ParseUint(name, 10, 8)
|
||||
if err != nil {
|
||||
|
|
|
@ -79,6 +79,11 @@ func (t *GVisor) Start() error {
|
|||
tcpForwarder := tcp.NewForwarder(ipStack, 0, 1024, func(r *tcp.ForwarderRequest) {
|
||||
source := M.SocksaddrFrom(AddrFromAddress(r.ID().RemoteAddress), r.ID().RemotePort)
|
||||
destination := M.SocksaddrFrom(AddrFromAddress(r.ID().LocalAddress), r.ID().LocalPort)
|
||||
pErr := t.handler.PrepareConnection(source, destination)
|
||||
if pErr != nil {
|
||||
r.Complete(gWriteUnreachable(t.stack, r.Packet(), err) == os.ErrInvalid)
|
||||
return
|
||||
}
|
||||
conn := &gLazyConn{
|
||||
parentCtx: t.ctx,
|
||||
stack: t.stack,
|
||||
|
@ -86,11 +91,6 @@ func (t *GVisor) Start() error {
|
|||
localAddr: source.TCPAddr(),
|
||||
remoteAddr: destination.TCPAddr(),
|
||||
}
|
||||
pErr := t.handler.PrepareConnection(source, destination)
|
||||
if pErr != nil {
|
||||
r.Complete(gWriteUnreachable(t.stack, r.Packet(), err) == os.ErrInvalid)
|
||||
return
|
||||
}
|
||||
go t.handler.NewConnectionEx(t.ctx, conn, source, destination, nil)
|
||||
})
|
||||
ipStack.SetTransportProtocolHandler(tcp.ProtocolNumber, tcpForwarder.HandlePacket)
|
||||
|
|
|
@ -6,13 +6,14 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/buffer"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/adapters/gonet"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/header"
|
||||
gHdr "github.com/sagernet/gvisor/pkg/tcpip/header"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/link/channel"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/transport/udp"
|
||||
"github.com/sagernet/gvisor/pkg/waiter"
|
||||
"github.com/sagernet/sing-tun/internal/clashtcpip"
|
||||
"github.com/sagernet/sing-tun/internal/gtcpip/header"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
"github.com/sagernet/sing/common/canceler"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
|
@ -104,7 +105,7 @@ func (m *Mixed) tunLoop() {
|
|||
}
|
||||
m.logger.Error(E.Cause(err, "read packet"))
|
||||
}
|
||||
if n < clashtcpip.IPv4PacketMinLength {
|
||||
if n < header.IPv4MinimumSize {
|
||||
continue
|
||||
}
|
||||
rawPacket := packetBuffer[:n]
|
||||
|
@ -124,7 +125,7 @@ func (m *Mixed) wintunLoop(winTun WinTun) {
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(packet) < clashtcpip.IPv4PacketMinLength {
|
||||
if len(packet) < header.IPv4MinimumSize {
|
||||
release()
|
||||
continue
|
||||
}
|
||||
|
@ -158,7 +159,7 @@ func (m *Mixed) batchLoop(linuxTUN LinuxTUN, batchSize int) {
|
|||
}
|
||||
for i := 0; i < n; i++ {
|
||||
packetSize := packetSizes[i]
|
||||
if packetSize < clashtcpip.IPv4PacketMinLength {
|
||||
if packetSize < header.IPv4MinimumSize {
|
||||
continue
|
||||
}
|
||||
packetBuffer := packetBuffers[i]
|
||||
|
@ -197,48 +198,48 @@ func (m *Mixed) processPacket(packet []byte) bool {
|
|||
return writeBack
|
||||
}
|
||||
|
||||
func (m *Mixed) processIPv4(packet clashtcpip.IPv4Packet) (writeBack bool, err error) {
|
||||
func (m *Mixed) processIPv4(ipHdr header.IPv4) (writeBack bool, err error) {
|
||||
writeBack = true
|
||||
destination := packet.DestinationIP()
|
||||
destination := ipHdr.DestinationAddr()
|
||||
if destination == m.broadcastAddr || !destination.IsGlobalUnicast() {
|
||||
return
|
||||
}
|
||||
switch packet.Protocol() {
|
||||
case clashtcpip.TCP:
|
||||
err = m.processIPv4TCP(packet, packet.Payload())
|
||||
case clashtcpip.UDP:
|
||||
switch ipHdr.TransportProtocol() {
|
||||
case header.TCPProtocolNumber:
|
||||
writeBack, err = m.processIPv4TCP(ipHdr, ipHdr.Payload())
|
||||
case header.UDPProtocolNumber:
|
||||
writeBack = false
|
||||
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
Payload: buffer.MakeWithData(packet),
|
||||
Payload: buffer.MakeWithData(ipHdr),
|
||||
IsForwardedPacket: true,
|
||||
})
|
||||
m.endpoint.InjectInbound(header.IPv4ProtocolNumber, pkt)
|
||||
m.endpoint.InjectInbound(gHdr.IPv4ProtocolNumber, pkt)
|
||||
pkt.DecRef()
|
||||
return
|
||||
case clashtcpip.ICMP:
|
||||
err = m.processIPv4ICMP(packet, packet.Payload())
|
||||
case header.ICMPv4ProtocolNumber:
|
||||
err = m.processIPv4ICMP(ipHdr, ipHdr.Payload())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (m *Mixed) processIPv6(packet clashtcpip.IPv6Packet) (writeBack bool, err error) {
|
||||
func (m *Mixed) processIPv6(ipHdr header.IPv6) (writeBack bool, err error) {
|
||||
writeBack = true
|
||||
if !packet.DestinationIP().IsGlobalUnicast() {
|
||||
if !ipHdr.DestinationAddr().IsGlobalUnicast() {
|
||||
return
|
||||
}
|
||||
switch packet.Protocol() {
|
||||
case clashtcpip.TCP:
|
||||
err = m.processIPv6TCP(packet, packet.Payload())
|
||||
case clashtcpip.UDP:
|
||||
switch ipHdr.TransportProtocol() {
|
||||
case header.TCPProtocolNumber:
|
||||
err = m.processIPv6TCP(ipHdr, ipHdr.Payload())
|
||||
case header.UDPProtocolNumber:
|
||||
writeBack = false
|
||||
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
Payload: buffer.MakeWithData(packet),
|
||||
Payload: buffer.MakeWithData(ipHdr),
|
||||
IsForwardedPacket: true,
|
||||
})
|
||||
m.endpoint.InjectInbound(header.IPv6ProtocolNumber, pkt)
|
||||
m.endpoint.InjectInbound(tcpip.NetworkProtocolNumber(header.IPv6ProtocolNumber), pkt)
|
||||
pkt.DecRef()
|
||||
case clashtcpip.ICMPv6:
|
||||
err = m.processIPv6ICMP(packet, packet.Payload())
|
||||
case header.ICMPv6ProtocolNumber:
|
||||
err = m.processIPv6ICMP(ipHdr, ipHdr.Payload())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
283
stack_system.go
283
stack_system.go
|
@ -7,7 +7,8 @@ import (
|
|||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-tun/internal/clashtcpip"
|
||||
"github.com/sagernet/sing-tun/internal/gtcpip/checksum"
|
||||
"github.com/sagernet/sing-tun/internal/gtcpip/header"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
|
@ -179,7 +180,7 @@ func (s *System) tunLoop() {
|
|||
}
|
||||
s.logger.Error(E.Cause(err, "read packet"))
|
||||
}
|
||||
if n < clashtcpip.IPv4PacketMinLength {
|
||||
if n < header.IPv4MinimumSize {
|
||||
continue
|
||||
}
|
||||
rawPacket := packetBuffer[:n]
|
||||
|
@ -199,7 +200,7 @@ func (s *System) wintunLoop(winTun WinTun) {
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(packet) < clashtcpip.IPv4PacketMinLength {
|
||||
if len(packet) < header.IPv4MinimumSize {
|
||||
release()
|
||||
continue
|
||||
}
|
||||
|
@ -233,7 +234,7 @@ func (s *System) batchLoop(linuxTUN LinuxTUN, batchSize int) {
|
|||
}
|
||||
for i := 0; i < n; i++ {
|
||||
packetSize := packetSizes[i]
|
||||
if packetSize < clashtcpip.IPv4PacketMinLength {
|
||||
if packetSize < header.IPv4MinimumSize {
|
||||
continue
|
||||
}
|
||||
packetBuffer := packetBuffers[i]
|
||||
|
@ -300,83 +301,89 @@ func (s *System) acceptLoop(listener net.Listener) {
|
|||
}
|
||||
}
|
||||
}
|
||||
go s.handler.NewConnectionEx(s.ctx, conn, M.SocksaddrFromNet(conn.RemoteAddr()), destination, nil)
|
||||
go s.handler.NewConnectionEx(s.ctx, conn, M.SocksaddrFromNetIP(session.Source), destination, nil)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *System) processIPv4(packet clashtcpip.IPv4Packet) (writeBack bool, err error) {
|
||||
func (s *System) processIPv4(ipHdr header.IPv4) (writeBack bool, err error) {
|
||||
writeBack = true
|
||||
destination := packet.DestinationIP()
|
||||
destination := ipHdr.DestinationAddr()
|
||||
if destination == s.broadcastAddr || !destination.IsGlobalUnicast() {
|
||||
return
|
||||
}
|
||||
switch packet.Protocol() {
|
||||
case clashtcpip.TCP:
|
||||
err = s.processIPv4TCP(packet, packet.Payload())
|
||||
case clashtcpip.UDP:
|
||||
switch ipHdr.TransportProtocol() {
|
||||
case header.TCPProtocolNumber:
|
||||
writeBack, err = s.processIPv4TCP(ipHdr, ipHdr.Payload())
|
||||
case header.UDPProtocolNumber:
|
||||
writeBack = false
|
||||
err = s.processIPv4UDP(packet, packet.Payload())
|
||||
case clashtcpip.ICMP:
|
||||
err = s.processIPv4ICMP(packet, packet.Payload())
|
||||
err = s.processIPv4UDP(ipHdr, ipHdr.Payload())
|
||||
case header.ICMPv4ProtocolNumber:
|
||||
err = s.processIPv4ICMP(ipHdr, ipHdr.Payload())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *System) processIPv6(packet clashtcpip.IPv6Packet) (writeBack bool, err error) {
|
||||
func (s *System) processIPv6(ipHdr header.IPv6) (writeBack bool, err error) {
|
||||
writeBack = true
|
||||
if !packet.DestinationIP().IsGlobalUnicast() {
|
||||
if !ipHdr.DestinationAddr().IsGlobalUnicast() {
|
||||
return
|
||||
}
|
||||
switch packet.Protocol() {
|
||||
case clashtcpip.TCP:
|
||||
err = s.processIPv6TCP(packet, packet.Payload())
|
||||
case clashtcpip.UDP:
|
||||
writeBack = false
|
||||
err = s.processIPv6UDP(packet, packet.Payload())
|
||||
case clashtcpip.ICMPv6:
|
||||
err = s.processIPv6ICMP(packet, packet.Payload())
|
||||
switch ipHdr.TransportProtocol() {
|
||||
case header.TCPProtocolNumber:
|
||||
err = s.processIPv6TCP(ipHdr, ipHdr.Payload())
|
||||
case header.UDPProtocolNumber:
|
||||
err = s.processIPv6UDP(ipHdr, ipHdr.Payload())
|
||||
case header.ICMPv6ProtocolNumber:
|
||||
err = s.processIPv6ICMP(ipHdr, ipHdr.Payload())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *System) processIPv4TCP(packet clashtcpip.IPv4Packet, header clashtcpip.TCPPacket) error {
|
||||
source := netip.AddrPortFrom(packet.SourceIP(), header.SourcePort())
|
||||
destination := netip.AddrPortFrom(packet.DestinationIP(), header.DestinationPort())
|
||||
func (s *System) processIPv4TCP(ipHdr header.IPv4, tcpHdr header.TCP) (bool, error) {
|
||||
source := netip.AddrPortFrom(ipHdr.SourceAddr(), tcpHdr.SourcePort())
|
||||
destination := netip.AddrPortFrom(ipHdr.DestinationAddr(), tcpHdr.DestinationPort())
|
||||
if !destination.Addr().IsGlobalUnicast() {
|
||||
return nil
|
||||
return true, nil
|
||||
} else if source.Addr() == s.inet4ServerAddress && source.Port() == s.tcpPort {
|
||||
session := s.tcpNat.LookupBack(destination.Port())
|
||||
if session == nil {
|
||||
return E.New("ipv4: tcp: session not found: ", destination.Port())
|
||||
return false, E.New("ipv4: tcp: session not found: ", destination.Port())
|
||||
}
|
||||
packet.SetSourceIP(session.Destination.Addr())
|
||||
header.SetSourcePort(session.Destination.Port())
|
||||
packet.SetDestinationIP(session.Source.Addr())
|
||||
header.SetDestinationPort(session.Source.Port())
|
||||
ipHdr.SetSourceAddr(session.Destination.Addr())
|
||||
tcpHdr.SetSourcePort(session.Destination.Port())
|
||||
ipHdr.SetDestinationAddr(session.Source.Addr())
|
||||
tcpHdr.SetDestinationPort(session.Source.Port())
|
||||
} else {
|
||||
natPort, err := s.tcpNat.Lookup(source, destination, s.handler)
|
||||
if err != nil {
|
||||
// TODO: implement rejects
|
||||
return nil
|
||||
// TODO: implement ICMP port unreachable
|
||||
return false, nil
|
||||
}
|
||||
packet.SetSourceIP(s.inet4Address)
|
||||
header.SetSourcePort(natPort)
|
||||
packet.SetDestinationIP(s.inet4ServerAddress)
|
||||
header.SetDestinationPort(s.tcpPort)
|
||||
ipHdr.SetSourceAddr(s.inet4Address)
|
||||
tcpHdr.SetSourcePort(natPort)
|
||||
ipHdr.SetDestinationAddr(s.inet4ServerAddress)
|
||||
tcpHdr.SetDestinationPort(s.tcpPort)
|
||||
}
|
||||
if !s.txChecksumOffload {
|
||||
header.ResetChecksum(packet.PseudoSum())
|
||||
packet.ResetChecksum()
|
||||
tcpHdr.SetChecksum(0)
|
||||
tcpHdr.SetChecksum(^checksum.Checksum(tcpHdr.Payload(), tcpHdr.CalculateChecksum(
|
||||
header.PseudoHeaderChecksum(header.TCPProtocolNumber, ipHdr.SourceAddressSlice(), ipHdr.DestinationAddressSlice(), ipHdr.PayloadLength()),
|
||||
)))
|
||||
} else {
|
||||
header.OffloadChecksum()
|
||||
packet.ResetChecksum()
|
||||
tcpHdr.SetChecksum(0)
|
||||
}
|
||||
ipHdr.SetChecksum(0)
|
||||
ipHdr.SetChecksum(^ipHdr.CalculateChecksum())
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (s *System) resetIPv4TCP(packet header.IPv4, header header.TCP) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *System) processIPv6TCP(packet clashtcpip.IPv6Packet, header clashtcpip.TCPPacket) error {
|
||||
source := netip.AddrPortFrom(packet.SourceIP(), header.SourcePort())
|
||||
destination := netip.AddrPortFrom(packet.DestinationIP(), header.DestinationPort())
|
||||
func (s *System) processIPv6TCP(ipHdr header.IPv6, tcpHdr header.TCP) error {
|
||||
source := netip.AddrPortFrom(ipHdr.SourceAddr(), tcpHdr.SourcePort())
|
||||
destination := netip.AddrPortFrom(ipHdr.DestinationAddr(), tcpHdr.DestinationPort())
|
||||
if !destination.Addr().IsGlobalUnicast() {
|
||||
return nil
|
||||
} else if source.Addr() == s.inet6ServerAddress && source.Port() == s.tcpPort6 {
|
||||
|
@ -384,58 +391,55 @@ func (s *System) processIPv6TCP(packet clashtcpip.IPv6Packet, header clashtcpip.
|
|||
if session == nil {
|
||||
return E.New("ipv6: tcp: session not found: ", destination.Port())
|
||||
}
|
||||
packet.SetSourceIP(session.Destination.Addr())
|
||||
header.SetSourcePort(session.Destination.Port())
|
||||
packet.SetDestinationIP(session.Source.Addr())
|
||||
header.SetDestinationPort(session.Source.Port())
|
||||
ipHdr.SetSourceAddr(session.Destination.Addr())
|
||||
tcpHdr.SetSourcePort(session.Destination.Port())
|
||||
ipHdr.SetSourceAddr(session.Source.Addr())
|
||||
tcpHdr.SetDestinationPort(session.Source.Port())
|
||||
} else {
|
||||
natPort, err := s.tcpNat.Lookup(source, destination, s.handler)
|
||||
if err != nil {
|
||||
// TODO: implement rejects
|
||||
// TODO: implement ICMP port unreachable
|
||||
return nil
|
||||
}
|
||||
packet.SetSourceIP(s.inet6Address)
|
||||
header.SetSourcePort(natPort)
|
||||
packet.SetDestinationIP(s.inet6ServerAddress)
|
||||
header.SetDestinationPort(s.tcpPort6)
|
||||
ipHdr.SetSourceAddr(s.inet6Address)
|
||||
tcpHdr.SetSourcePort(natPort)
|
||||
ipHdr.SetSourceAddr(s.inet6ServerAddress)
|
||||
tcpHdr.SetDestinationPort(s.tcpPort6)
|
||||
}
|
||||
if !s.txChecksumOffload {
|
||||
header.ResetChecksum(packet.PseudoSum())
|
||||
tcpHdr.SetChecksum(0)
|
||||
tcpHdr.SetChecksum(^checksum.Checksum(tcpHdr.Payload(), tcpHdr.CalculateChecksum(
|
||||
header.PseudoHeaderChecksum(header.TCPProtocolNumber, ipHdr.SourceAddressSlice(), ipHdr.DestinationAddressSlice(), ipHdr.PayloadLength()),
|
||||
)))
|
||||
} else {
|
||||
header.OffloadChecksum()
|
||||
tcpHdr.SetChecksum(0)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *System) processIPv4UDP(packet clashtcpip.IPv4Packet, header clashtcpip.UDPPacket) error {
|
||||
if packet.Flags()&clashtcpip.FlagMoreFragment != 0 {
|
||||
func (s *System) processIPv4UDP(ipHdr header.IPv4, udpHdr header.UDP) error {
|
||||
if ipHdr.Flags()&header.IPv4FlagMoreFragments != 0 {
|
||||
return E.New("ipv4: fragment dropped")
|
||||
}
|
||||
if packet.FragmentOffset() != 0 {
|
||||
if ipHdr.FragmentOffset() != 0 {
|
||||
return E.New("ipv4: udp: fragment dropped")
|
||||
}
|
||||
if !header.Valid() {
|
||||
return E.New("ipv4: udp: invalid packet")
|
||||
}
|
||||
source := M.SocksaddrFrom(packet.SourceIP(), header.SourcePort())
|
||||
destination := M.SocksaddrFrom(packet.DestinationIP(), header.DestinationPort())
|
||||
source := M.SocksaddrFrom(ipHdr.SourceAddr(), udpHdr.SourcePort())
|
||||
destination := M.SocksaddrFrom(ipHdr.DestinationAddr(), udpHdr.DestinationPort())
|
||||
if !destination.Addr.IsGlobalUnicast() {
|
||||
return nil
|
||||
}
|
||||
s.udpNat.NewPacket([][]byte{header.Payload()}, source, destination, packet)
|
||||
s.udpNat.NewPacket([][]byte{udpHdr.Payload()}, source, destination, ipHdr)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *System) processIPv6UDP(packet clashtcpip.IPv6Packet, header clashtcpip.UDPPacket) error {
|
||||
if !header.Valid() {
|
||||
return E.New("ipv6: udp: invalid packet")
|
||||
}
|
||||
source := M.SocksaddrFrom(packet.SourceIP(), header.SourcePort())
|
||||
destination := M.SocksaddrFrom(packet.DestinationIP(), header.DestinationPort())
|
||||
func (s *System) processIPv6UDP(ipHdr header.IPv6, udpHdr header.UDP) error {
|
||||
source := M.SocksaddrFrom(ipHdr.SourceAddr(), udpHdr.SourcePort())
|
||||
destination := M.SocksaddrFrom(ipHdr.DestinationAddr(), udpHdr.DestinationPort())
|
||||
if !destination.Addr.IsGlobalUnicast() {
|
||||
return nil
|
||||
}
|
||||
s.udpNat.NewPacket([][]byte{header.Payload()}, source, destination, packet)
|
||||
s.udpNat.NewPacket([][]byte{udpHdr.Payload()}, source, destination, ipHdr)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -447,8 +451,8 @@ func (s *System) preparePacketConnection(source M.Socksaddr, destination M.Socks
|
|||
}
|
||||
var writer N.PacketWriter
|
||||
if source.IsIPv4() {
|
||||
packet := userData.(clashtcpip.IPv4Packet)
|
||||
headerLen := packet.HeaderLen() + clashtcpip.UDPHeaderSize
|
||||
packet := userData.(header.IPv4)
|
||||
headerLen := packet.HeaderLength() + header.UDPMinimumSize
|
||||
headerCopy := make([]byte, headerLen)
|
||||
copy(headerCopy, packet[:headerLen])
|
||||
writer = &systemUDPPacketWriter4{
|
||||
|
@ -459,8 +463,8 @@ func (s *System) preparePacketConnection(source M.Socksaddr, destination M.Socks
|
|||
s.txChecksumOffload,
|
||||
}
|
||||
} else {
|
||||
packet := userData.(clashtcpip.IPv6Packet)
|
||||
headerLen := len(packet) - int(packet.PayloadLength()) + clashtcpip.UDPHeaderSize
|
||||
packet := userData.(header.IPv6)
|
||||
headerLen := len(packet) - int(packet.PayloadLength()) + header.UDPMinimumSize
|
||||
headerCopy := make([]byte, headerLen)
|
||||
copy(headerCopy, packet[:headerLen])
|
||||
writer = &systemUDPPacketWriter6{
|
||||
|
@ -474,32 +478,87 @@ func (s *System) preparePacketConnection(source M.Socksaddr, destination M.Socks
|
|||
return true, s.ctx, writer, nil
|
||||
}
|
||||
|
||||
func (s *System) processIPv4ICMP(packet clashtcpip.IPv4Packet, header clashtcpip.ICMPPacket) error {
|
||||
if header.Type() != clashtcpip.ICMPTypePingRequest || header.Code() != 0 {
|
||||
func (s *System) processIPv4ICMP(ipHdr header.IPv4, icmpHdr header.ICMPv4) error {
|
||||
if icmpHdr.Type() != header.ICMPv4Echo || icmpHdr.Code() != 0 {
|
||||
return nil
|
||||
}
|
||||
header.SetType(clashtcpip.ICMPTypePingResponse)
|
||||
sourceAddress := packet.SourceIP()
|
||||
packet.SetSourceIP(packet.DestinationIP())
|
||||
packet.SetDestinationIP(sourceAddress)
|
||||
header.ResetChecksum()
|
||||
packet.ResetChecksum()
|
||||
icmpHdr.SetType(header.ICMPv4EchoReply)
|
||||
sourceAddress := ipHdr.SourceAddr()
|
||||
ipHdr.SetSourceAddr(ipHdr.DestinationAddr())
|
||||
ipHdr.SetDestinationAddr(sourceAddress)
|
||||
icmpHdr.SetChecksum(header.ICMPv4Checksum(icmpHdr, checksum.Checksum(icmpHdr.Payload(), 0)))
|
||||
ipHdr.SetChecksum(0)
|
||||
ipHdr.SetChecksum(^ipHdr.CalculateChecksum())
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *System) processIPv6ICMP(packet clashtcpip.IPv6Packet, header clashtcpip.ICMPv6Packet) error {
|
||||
if header.Type() != clashtcpip.ICMPv6EchoRequest || header.Code() != 0 {
|
||||
func (s *System) processIPv6ICMP(ipHdr header.IPv6, icmpHdr header.ICMPv6) error {
|
||||
if icmpHdr.Type() != header.ICMPv6EchoRequest || icmpHdr.Code() != 0 {
|
||||
return nil
|
||||
}
|
||||
header.SetType(clashtcpip.ICMPv6EchoReply)
|
||||
sourceAddress := packet.SourceIP()
|
||||
packet.SetSourceIP(packet.DestinationIP())
|
||||
packet.SetDestinationIP(sourceAddress)
|
||||
header.ResetChecksum(packet.PseudoSum())
|
||||
packet.ResetChecksum()
|
||||
icmpHdr.SetType(header.ICMPv6EchoReply)
|
||||
sourceAddress := ipHdr.SourceAddr()
|
||||
ipHdr.SetSourceAddr(ipHdr.DestinationAddr())
|
||||
ipHdr.SetDestinationAddr(sourceAddress)
|
||||
icmpHdr.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{
|
||||
Header: icmpHdr,
|
||||
Src: ipHdr.SourceAddress(),
|
||||
Dst: ipHdr.DestinationAddress(),
|
||||
}))
|
||||
return nil
|
||||
}
|
||||
|
||||
/*func (s *System) WritePacket4(buffer *buf.Buffer, source netip.AddrPort, destination netip.AddrPort) error {
|
||||
packet := buf.Get(header.IPv4MinimumSize + header.UDPMinimumSize + buffer.Len())
|
||||
ipHdr := header.IPv4(packet)
|
||||
ipHdr.Encode(&header.IPv4Fields{
|
||||
TotalLength: uint16(len(packet)),
|
||||
Protocol: uint8(header.UDPProtocolNumber),
|
||||
SrcAddr: source.Addr(),
|
||||
DstAddr: destination.Addr(),
|
||||
})
|
||||
ipHdr.SetHeaderLength(header.IPv4MinimumSize)
|
||||
udpHdr := header.UDP(ipHdr.Payload())
|
||||
udpHdr.Encode(&header.UDPFields{
|
||||
SrcPort: source.Port(),
|
||||
DstPort: destination.Port(),
|
||||
Length: uint16(header.UDPMinimumSize + buffer.Len()),
|
||||
})
|
||||
copy(udpHdr.Payload(), buffer.Bytes())
|
||||
if !s.txChecksumOffload {
|
||||
...
|
||||
} else {
|
||||
udpHdr.SetChecksum(0)
|
||||
}
|
||||
ipHdr.SetChecksum(0)
|
||||
ipHdr.SetChecksum(^ipHdr.CalculateChecksum())
|
||||
return common.Error(s.tun.Write(packet))
|
||||
}
|
||||
|
||||
func (s *System) WritePacket6(buffer *buf.Buffer, source netip.AddrPort, destination netip.AddrPort) error {
|
||||
packet := buf.Get(header.IPv6MinimumSize + header.UDPMinimumSize + buffer.Len())
|
||||
ipHdr := header.IPv6(packet)
|
||||
ipHdr.Encode(&header.IPv6Fields{
|
||||
PayloadLength: uint16(header.UDPMinimumSize + buffer.Len()),
|
||||
TransportProtocol: header.UDPProtocolNumber,
|
||||
SrcAddr: source.Addr(),
|
||||
DstAddr: destination.Addr(),
|
||||
})
|
||||
udpHdr := header.UDP(ipHdr.Payload())
|
||||
udpHdr.Encode(&header.UDPFields{
|
||||
SrcPort: source.Port(),
|
||||
DstPort: destination.Port(),
|
||||
Length: uint16(header.UDPMinimumSize + buffer.Len()),
|
||||
})
|
||||
copy(udpHdr.Payload(), buffer.Bytes())
|
||||
if !s.txChecksumOffload {
|
||||
...
|
||||
} else {
|
||||
udpHdr.SetChecksum(0)
|
||||
}
|
||||
return common.Error(s.tun.Write(packet))
|
||||
}*/
|
||||
|
||||
type systemUDPPacketWriter4 struct {
|
||||
tun Tun
|
||||
frontHeadroom int
|
||||
|
@ -514,21 +573,24 @@ func (w *systemUDPPacketWriter4) WritePacket(buffer *buf.Buffer, destination M.S
|
|||
newPacket.Resize(w.frontHeadroom, 0)
|
||||
newPacket.Write(w.header)
|
||||
newPacket.Write(buffer.Bytes())
|
||||
ipHdr := clashtcpip.IPv4Packet(newPacket.Bytes())
|
||||
ipHdr := header.IPv4(newPacket.Bytes())
|
||||
ipHdr.SetTotalLength(uint16(newPacket.Len()))
|
||||
ipHdr.SetDestinationIP(ipHdr.SourceIP())
|
||||
ipHdr.SetSourceIP(destination.Addr)
|
||||
udpHdr := clashtcpip.UDPPacket(ipHdr.Payload())
|
||||
ipHdr.SetSourceAddress(ipHdr.SourceAddress())
|
||||
ipHdr.SetSourceAddr(destination.Addr)
|
||||
udpHdr := header.UDP(ipHdr.Payload())
|
||||
udpHdr.SetDestinationPort(udpHdr.SourcePort())
|
||||
udpHdr.SetSourcePort(destination.Port)
|
||||
udpHdr.SetLength(uint16(buffer.Len() + clashtcpip.UDPHeaderSize))
|
||||
udpHdr.SetLength(uint16(buffer.Len() + header.UDPMinimumSize))
|
||||
if !w.txChecksumOffload {
|
||||
udpHdr.ResetChecksum(ipHdr.PseudoSum())
|
||||
ipHdr.ResetChecksum()
|
||||
udpHdr.SetChecksum(0)
|
||||
udpHdr.SetChecksum(^checksum.Checksum(udpHdr.Payload(), udpHdr.CalculateChecksum(
|
||||
header.PseudoHeaderChecksum(header.UDPProtocolNumber, ipHdr.SourceAddressSlice(), ipHdr.DestinationAddressSlice(), ipHdr.PayloadLength()),
|
||||
)))
|
||||
} else {
|
||||
udpHdr.OffloadChecksum()
|
||||
ipHdr.ResetChecksum()
|
||||
udpHdr.SetChecksum(0)
|
||||
}
|
||||
ipHdr.SetChecksum(0)
|
||||
ipHdr.SetChecksum(^ipHdr.CalculateChecksum())
|
||||
if PacketOffset > 0 {
|
||||
newPacket.ExtendHeader(PacketOffset)[3] = syscall.AF_INET
|
||||
} else {
|
||||
|
@ -551,19 +613,22 @@ func (w *systemUDPPacketWriter6) WritePacket(buffer *buf.Buffer, destination M.S
|
|||
newPacket.Resize(w.frontHeadroom, 0)
|
||||
newPacket.Write(w.header)
|
||||
newPacket.Write(buffer.Bytes())
|
||||
ipHdr := clashtcpip.IPv6Packet(newPacket.Bytes())
|
||||
udpLen := uint16(clashtcpip.UDPHeaderSize + buffer.Len())
|
||||
ipHdr := header.IPv6(newPacket.Bytes())
|
||||
udpLen := uint16(header.UDPMinimumSize + buffer.Len())
|
||||
ipHdr.SetPayloadLength(udpLen)
|
||||
ipHdr.SetDestinationIP(ipHdr.SourceIP())
|
||||
ipHdr.SetSourceIP(destination.Addr)
|
||||
udpHdr := clashtcpip.UDPPacket(ipHdr.Payload())
|
||||
ipHdr.SetDestinationAddress(ipHdr.SourceAddress())
|
||||
ipHdr.SetSourceAddr(destination.Addr)
|
||||
udpHdr := header.UDP(ipHdr.Payload())
|
||||
udpHdr.SetDestinationPort(udpHdr.SourcePort())
|
||||
udpHdr.SetSourcePort(destination.Port)
|
||||
udpHdr.SetLength(udpLen)
|
||||
if !w.txChecksumOffload {
|
||||
udpHdr.ResetChecksum(ipHdr.PseudoSum())
|
||||
udpHdr.SetChecksum(0)
|
||||
udpHdr.SetChecksum(^checksum.Checksum(udpHdr.Payload(), udpHdr.CalculateChecksum(
|
||||
header.PseudoHeaderChecksum(header.UDPProtocolNumber, ipHdr.SourceAddressSlice(), ipHdr.DestinationAddressSlice(), ipHdr.PayloadLength()),
|
||||
)))
|
||||
} else {
|
||||
udpHdr.OffloadChecksum()
|
||||
udpHdr.SetChecksum(0)
|
||||
}
|
||||
if PacketOffset > 0 {
|
||||
newPacket.ExtendHeader(PacketOffset)[3] = syscall.AF_INET6
|
||||
|
|
|
@ -15,7 +15,7 @@ import (
|
|||
"io"
|
||||
"unsafe"
|
||||
|
||||
"github.com/sagernet/sing-tun/internal/clashtcpip"
|
||||
"github.com/sagernet/sing-tun/internal/gtcpip/checksum"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
@ -746,7 +746,7 @@ func handleVirtioRead(in []byte, bufs [][]byte, sizes []int, offset int) (int, e
|
|||
}
|
||||
|
||||
func checksumNoFold(b []byte, initial uint64) uint64 {
|
||||
return initial + uint64(clashtcpip.Sum(b))
|
||||
return uint64(checksum.Checksum(b, uint16(initial)))
|
||||
}
|
||||
|
||||
func checksumFold(b []byte, initial uint64) uint16 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue