256 lines
7.7 KiB
Go
256 lines
7.7 KiB
Go
package tun
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"net/netip"
|
|
|
|
"github.com/sagernet/sing-tun/gtcpip/header"
|
|
)
|
|
|
|
type flowKey struct {
|
|
protocol uint8
|
|
source netip.AddrPort
|
|
destination netip.AddrPort
|
|
}
|
|
|
|
func (k flowKey) reversed() flowKey {
|
|
return flowKey{protocol: k.protocol, source: k.destination, destination: k.source}
|
|
}
|
|
|
|
type forwardPacket struct {
|
|
ipVersion uint8
|
|
protocol uint8
|
|
network header.Network
|
|
transport []byte
|
|
source netip.AddrPort
|
|
destination netip.AddrPort
|
|
tcpFlags header.TCPFlags
|
|
icmpType uint8
|
|
fragment bool
|
|
hasFlow bool
|
|
}
|
|
|
|
func (p *forwardPacket) flowKey() flowKey {
|
|
return flowKey{protocol: p.protocol, source: p.source, destination: p.destination}
|
|
}
|
|
|
|
func (p *forwardPacket) isTCPSyn() bool {
|
|
return p.protocol == uint8(header.TCPProtocolNumber) && p.tcpFlags&header.TCPFlagSyn != 0
|
|
}
|
|
|
|
func parseForwardPacket(packet []byte) (forwardPacket, bool) {
|
|
switch header.IPVersion(packet) {
|
|
case header.IPv4Version:
|
|
ipHdr := header.IPv4(packet)
|
|
if !ipHdr.IsValid(len(packet)) {
|
|
return forwardPacket{}, false
|
|
}
|
|
parsed := forwardPacket{
|
|
ipVersion: 4,
|
|
protocol: uint8(ipHdr.TransportProtocol()),
|
|
network: ipHdr,
|
|
source: netip.AddrPortFrom(ipHdr.SourceAddr(), 0),
|
|
destination: netip.AddrPortFrom(ipHdr.DestinationAddr(), 0),
|
|
}
|
|
if ipHdr.More() || ipHdr.FragmentOffset() != 0 {
|
|
parsed.fragment = true
|
|
return parsed, true
|
|
}
|
|
parsed.parseTransport(ipHdr.Payload())
|
|
return parsed, true
|
|
case header.IPv6Version:
|
|
ipHdr := header.IPv6(packet)
|
|
if !ipHdr.IsValid(len(packet)) {
|
|
return forwardPacket{}, false
|
|
}
|
|
protocol, payload, fragment, transportPresent := skipIPv6ExtensionHeaders(uint8(ipHdr.TransportProtocol()), ipHdr.Payload())
|
|
parsed := forwardPacket{
|
|
ipVersion: 6,
|
|
protocol: protocol,
|
|
network: ipHdr,
|
|
source: netip.AddrPortFrom(ipHdr.SourceAddr(), 0),
|
|
destination: netip.AddrPortFrom(ipHdr.DestinationAddr(), 0),
|
|
fragment: fragment,
|
|
}
|
|
if fragment || !transportPresent {
|
|
return parsed, true
|
|
}
|
|
parsed.parseTransport(payload)
|
|
return parsed, true
|
|
default:
|
|
return forwardPacket{}, false
|
|
}
|
|
}
|
|
|
|
func skipIPv6ExtensionHeaders(protocol uint8, payload []byte) (uint8, []byte, bool, bool) {
|
|
for {
|
|
switch header.IPv6ExtensionHeaderIdentifier(protocol) {
|
|
case header.IPv6HopByHopOptionsExtHdrIdentifier, header.IPv6RoutingExtHdrIdentifier, header.IPv6DestinationOptionsExtHdrIdentifier:
|
|
if len(payload) < 2 {
|
|
return protocol, payload, false, false
|
|
}
|
|
extensionLength := (int(payload[1]) + 1) * 8
|
|
if len(payload) < extensionLength {
|
|
return protocol, payload, false, false
|
|
}
|
|
protocol = payload[0]
|
|
payload = payload[extensionLength:]
|
|
case header.IPv6FragmentExtHdrIdentifier:
|
|
return protocol, payload, true, false
|
|
default:
|
|
return protocol, payload, false, true
|
|
}
|
|
}
|
|
}
|
|
|
|
func (p *forwardPacket) parseTransport(payload []byte) {
|
|
p.transport = payload
|
|
switch p.protocol {
|
|
case uint8(header.TCPProtocolNumber):
|
|
if len(payload) < header.TCPMinimumSize {
|
|
return
|
|
}
|
|
tcpHdr := header.TCP(payload)
|
|
p.source = netip.AddrPortFrom(p.source.Addr(), tcpHdr.SourcePort())
|
|
p.destination = netip.AddrPortFrom(p.destination.Addr(), tcpHdr.DestinationPort())
|
|
p.tcpFlags = tcpHdr.Flags()
|
|
p.hasFlow = true
|
|
case uint8(header.UDPProtocolNumber):
|
|
if len(payload) < header.UDPMinimumSize {
|
|
return
|
|
}
|
|
udpHdr := header.UDP(payload)
|
|
p.source = netip.AddrPortFrom(p.source.Addr(), udpHdr.SourcePort())
|
|
p.destination = netip.AddrPortFrom(p.destination.Addr(), udpHdr.DestinationPort())
|
|
p.hasFlow = true
|
|
case uint8(header.ICMPv4ProtocolNumber):
|
|
if len(payload) < header.ICMPv4MinimumSize {
|
|
return
|
|
}
|
|
icmpHdr := header.ICMPv4(payload)
|
|
p.icmpType = uint8(icmpHdr.Type())
|
|
switch icmpHdr.Type() {
|
|
case header.ICMPv4Echo, header.ICMPv4EchoReply:
|
|
identifier := icmpHdr.Ident()
|
|
p.source = netip.AddrPortFrom(p.source.Addr(), identifier)
|
|
p.destination = netip.AddrPortFrom(p.destination.Addr(), identifier)
|
|
p.hasFlow = true
|
|
}
|
|
case uint8(header.ICMPv6ProtocolNumber):
|
|
if len(payload) < header.ICMPv6MinimumSize {
|
|
return
|
|
}
|
|
icmpHdr := header.ICMPv6(payload)
|
|
p.icmpType = uint8(icmpHdr.Type())
|
|
switch icmpHdr.Type() {
|
|
case header.ICMPv6EchoRequest, header.ICMPv6EchoReply:
|
|
identifier := icmpHdr.Ident()
|
|
p.source = netip.AddrPortFrom(p.source.Addr(), identifier)
|
|
p.destination = netip.AddrPortFrom(p.destination.Addr(), identifier)
|
|
p.hasFlow = true
|
|
}
|
|
}
|
|
}
|
|
|
|
func (p *forwardPacket) isICMPError() bool {
|
|
switch p.protocol {
|
|
case uint8(header.ICMPv4ProtocolNumber):
|
|
switch header.ICMPv4Type(p.icmpType) {
|
|
case header.ICMPv4DstUnreachable, header.ICMPv4SrcQuench, header.ICMPv4Redirect, header.ICMPv4TimeExceeded, header.ICMPv4ParamProblem:
|
|
return true
|
|
}
|
|
return false
|
|
case uint8(header.ICMPv6ProtocolNumber):
|
|
return header.ICMPv6Type(p.icmpType).IsErrorType()
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func (p *forwardPacket) icmpErrorInner() ([]byte, bool) {
|
|
var innerOffset int
|
|
switch p.protocol {
|
|
case uint8(header.ICMPv4ProtocolNumber):
|
|
innerOffset = header.ICMPv4MinimumSize
|
|
case uint8(header.ICMPv6ProtocolNumber):
|
|
innerOffset = header.ICMPv6ErrorHeaderSize
|
|
default:
|
|
return nil, false
|
|
}
|
|
if len(p.transport) <= innerOffset {
|
|
return nil, false
|
|
}
|
|
return p.transport[innerOffset:], true
|
|
}
|
|
|
|
type embeddedPacket struct {
|
|
network header.Network
|
|
payload []byte
|
|
protocol uint8
|
|
source netip.AddrPort
|
|
destination netip.AddrPort
|
|
}
|
|
|
|
func (p *embeddedPacket) flowKey() flowKey {
|
|
return flowKey{protocol: p.protocol, source: p.source, destination: p.destination}
|
|
}
|
|
|
|
func parseEmbedded(inner []byte) (embeddedPacket, bool) {
|
|
switch header.IPVersion(inner) {
|
|
case header.IPv4Version:
|
|
if len(inner) < header.IPv4MinimumSize {
|
|
return embeddedPacket{}, false
|
|
}
|
|
ipHdr := header.IPv4(inner)
|
|
headerLength := int(ipHdr.HeaderLength())
|
|
if headerLength < header.IPv4MinimumSize || headerLength > len(inner) {
|
|
return embeddedPacket{}, false
|
|
}
|
|
return parseEmbeddedTransport(ipHdr, inner[headerLength:], uint8(ipHdr.TransportProtocol()), ipHdr.SourceAddr(), ipHdr.DestinationAddr())
|
|
case header.IPv6Version:
|
|
if len(inner) < header.IPv6MinimumSize {
|
|
return embeddedPacket{}, false
|
|
}
|
|
ipHdr := header.IPv6(inner)
|
|
protocol, payload, _, transportPresent := skipIPv6ExtensionHeaders(uint8(ipHdr.TransportProtocol()), inner[header.IPv6MinimumSize:])
|
|
if !transportPresent {
|
|
return embeddedPacket{}, false
|
|
}
|
|
return parseEmbeddedTransport(ipHdr, payload, protocol, ipHdr.SourceAddr(), ipHdr.DestinationAddr())
|
|
default:
|
|
return embeddedPacket{}, false
|
|
}
|
|
}
|
|
|
|
func parseEmbeddedTransport(network header.Network, payload []byte, protocol uint8, source, destination netip.Addr) (embeddedPacket, bool) {
|
|
embedded := embeddedPacket{
|
|
network: network,
|
|
payload: payload,
|
|
protocol: protocol,
|
|
}
|
|
switch protocol {
|
|
case uint8(header.TCPProtocolNumber), uint8(header.UDPProtocolNumber):
|
|
if len(payload) < 4 {
|
|
return embeddedPacket{}, false
|
|
}
|
|
embedded.source = netip.AddrPortFrom(source, binary.BigEndian.Uint16(payload[0:]))
|
|
embedded.destination = netip.AddrPortFrom(destination, binary.BigEndian.Uint16(payload[2:]))
|
|
case uint8(header.ICMPv4ProtocolNumber):
|
|
if len(payload) < header.ICMPv4MinimumSize {
|
|
return embeddedPacket{}, false
|
|
}
|
|
identifier := header.ICMPv4(payload).Ident()
|
|
embedded.source = netip.AddrPortFrom(source, identifier)
|
|
embedded.destination = netip.AddrPortFrom(destination, identifier)
|
|
case uint8(header.ICMPv6ProtocolNumber):
|
|
if len(payload) < header.ICMPv6MinimumSize {
|
|
return embeddedPacket{}, false
|
|
}
|
|
identifier := header.ICMPv6(payload).Ident()
|
|
embedded.source = netip.AddrPortFrom(source, identifier)
|
|
embedded.destination = netip.AddrPortFrom(destination, identifier)
|
|
default:
|
|
return embeddedPacket{}, false
|
|
}
|
|
return embedded, true
|
|
}
|