mirror of
https://github.com/XTLS/Xray-core.git
synced 2026-10-12 18:45:49 +00:00
https://github.com/XTLS/Xray-core/pull/6771#issuecomment-5752154561 https://github.com/XTLS/Xray-core/pull/6795#issuecomment-6095740152 https://github.com/XTLS/Xray-core/pull/7130#issuecomment-6116258188
162 lines
4.2 KiB
Go
162 lines
4.2 KiB
Go
package tun
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"math/bits"
|
|
"net/netip"
|
|
)
|
|
|
|
const (
|
|
ipv4HeaderSize = 20
|
|
ipv6HeaderSize = 40
|
|
tcpHeaderSize = 20
|
|
udpHeaderSize = 8
|
|
|
|
protoICMPv4 = 1
|
|
protoTCP = 6
|
|
protoUDP = 17
|
|
protoICMPv6 = 58
|
|
)
|
|
|
|
// packet is an IP packet with its header read
|
|
type packet struct {
|
|
proto byte
|
|
src, dst netip.Addr
|
|
// payload is what follows the IP header
|
|
payload []byte
|
|
}
|
|
|
|
// parsePacket reads the IP header of b. Fragments are not accepted: what the system sends to the tun fits its MTU
|
|
func parsePacket(b []byte) (p packet, ok bool) {
|
|
if len(b) < ipv4HeaderSize {
|
|
return p, false
|
|
}
|
|
switch b[0] >> 4 {
|
|
case 4:
|
|
size := int(b[0]&0xf) * 4
|
|
total := int(binary.BigEndian.Uint16(b[2:]))
|
|
if size < ipv4HeaderSize || total < size || total > len(b) || binary.BigEndian.Uint16(b[6:])&0x3fff != 0 {
|
|
return p, false
|
|
}
|
|
p.proto = b[9]
|
|
p.src = netip.AddrFrom4([4]byte(b[12:16]))
|
|
p.dst = netip.AddrFrom4([4]byte(b[16:20]))
|
|
p.payload = b[size:total]
|
|
case 6:
|
|
if len(b) < ipv6HeaderSize {
|
|
return p, false
|
|
}
|
|
total := ipv6HeaderSize + int(binary.BigEndian.Uint16(b[4:]))
|
|
if total > len(b) {
|
|
return p, false
|
|
}
|
|
p.proto = b[6]
|
|
p.src = netip.AddrFrom16([16]byte(b[8:24]))
|
|
p.dst = netip.AddrFrom16([16]byte(b[24:40]))
|
|
p.payload = b[ipv6HeaderSize:total]
|
|
// hop-by-hop options, routing, destination options
|
|
for p.proto == 0 || p.proto == 43 || p.proto == 60 {
|
|
if len(p.payload) < 8 {
|
|
return p, false
|
|
}
|
|
size := int(p.payload[1])*8 + 8
|
|
if size > len(p.payload) {
|
|
return p, false
|
|
}
|
|
p.proto, p.payload = p.payload[0], p.payload[size:]
|
|
}
|
|
default:
|
|
return p, false
|
|
}
|
|
return p, true
|
|
}
|
|
|
|
// putIPHeader writes the header of a packet from src to dst into b, without its length, and returns the size of the header
|
|
func putIPHeader(b []byte, proto byte, src, dst netip.Addr) int {
|
|
if src.Is4() {
|
|
s, d := src.As4(), dst.As4()
|
|
clear(b[:ipv4HeaderSize])
|
|
b[0] = 0x45
|
|
if proto == protoTCP {
|
|
b[6] = 0x40 // don't fragment
|
|
}
|
|
b[8] = 64
|
|
b[9] = proto
|
|
copy(b[12:], s[:])
|
|
copy(b[16:], d[:])
|
|
return ipv4HeaderSize
|
|
}
|
|
s, d := src.As16(), dst.As16()
|
|
clear(b[:ipv6HeaderSize])
|
|
b[0] = 0x60
|
|
b[6] = proto
|
|
b[7] = 64
|
|
copy(b[8:], s[:])
|
|
copy(b[24:], d[:])
|
|
return ipv6HeaderSize
|
|
}
|
|
|
|
// setIPLength completes the header ip of a packet that is size bytes long with it
|
|
func setIPLength(ip []byte, size int) {
|
|
if len(ip) == ipv4HeaderSize {
|
|
binary.BigEndian.PutUint16(ip[2:], uint16(size))
|
|
ip[10], ip[11] = 0, 0
|
|
binary.BigEndian.PutUint16(ip[10:], checksum(sum(ip, 0)))
|
|
return
|
|
}
|
|
binary.BigEndian.PutUint16(ip[4:], uint16(size-ipv6HeaderSize))
|
|
}
|
|
|
|
// transportChecksum is the checksum of a TCP or UDP segment, header and payload, of a packet with the header ip.
|
|
// The checksum field of header is zero
|
|
func transportChecksum(ip []byte, proto byte, header, payload []byte) uint16 {
|
|
return checksum(sum(payload, sum(header, pseudoSum(ip, proto, len(header)+len(payload)))))
|
|
}
|
|
|
|
// pseudoSum is what the header ip of a packet adds to the checksum of its TCP or UDP segment of size bytes
|
|
func pseudoSum(ip []byte, proto byte, size int) uint64 {
|
|
addresses := ip[12:ipv4HeaderSize]
|
|
if len(ip) == ipv6HeaderSize {
|
|
addresses = ip[8:]
|
|
}
|
|
return sum(addresses, uint64(proto)+uint64(size))
|
|
}
|
|
|
|
// sum adds the 16-bit words of b to s, with the carry of every addition added back
|
|
func sum(b []byte, s uint64) uint64 {
|
|
var c uint64
|
|
for len(b) >= 32 {
|
|
s, c = bits.Add64(s, binary.BigEndian.Uint64(b), c)
|
|
s, c = bits.Add64(s, binary.BigEndian.Uint64(b[8:]), c)
|
|
s, c = bits.Add64(s, binary.BigEndian.Uint64(b[16:]), c)
|
|
s, c = bits.Add64(s, binary.BigEndian.Uint64(b[24:]), c)
|
|
b = b[32:]
|
|
}
|
|
for len(b) >= 8 {
|
|
s, c = bits.Add64(s, binary.BigEndian.Uint64(b), c)
|
|
b = b[8:]
|
|
}
|
|
if len(b) >= 4 {
|
|
s, c = bits.Add64(s, uint64(binary.BigEndian.Uint32(b)), c)
|
|
b = b[4:]
|
|
}
|
|
if len(b) >= 2 {
|
|
s, c = bits.Add64(s, uint64(binary.BigEndian.Uint16(b)), c)
|
|
b = b[2:]
|
|
}
|
|
if len(b) == 1 {
|
|
s, c = bits.Add64(s, uint64(b[0])<<8, c)
|
|
}
|
|
s, c = bits.Add64(s, 0, c)
|
|
return s + c
|
|
}
|
|
|
|
// checksum folds s into the 16 bits that go into a header
|
|
func checksum(s uint64) uint16 {
|
|
s = s>>32 + s&0xffffffff
|
|
s = s>>16 + s&0xffff
|
|
s = s>>16 + s&0xffff
|
|
s = s>>16 + s&0xffff
|
|
return ^uint16(s)
|
|
}
|