Add flow dispatcher
This commit is contained in:
parent
47bdde06c3
commit
ed63adda33
27 changed files with 2469 additions and 963 deletions
350
flow_rewrite.go
Normal file
350
flow_rewrite.go
Normal file
|
|
@ -0,0 +1,350 @@
|
|||
package tun
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/sagernet/sing-tun/gtcpip"
|
||||
"github.com/sagernet/sing-tun/gtcpip/checksum"
|
||||
"github.com/sagernet/sing-tun/gtcpip/header"
|
||||
)
|
||||
|
||||
type rewriteRule struct {
|
||||
sourceAddress tcpip.Address
|
||||
sourcePort uint16
|
||||
rewriteSourcePort bool
|
||||
destinationAddress tcpip.Address
|
||||
destinationPort uint16
|
||||
rewriteDestinationPort bool
|
||||
}
|
||||
|
||||
func applyRewrite(packet *forwardPacket, rule *rewriteRule) {
|
||||
oldSource := packet.network.SourceAddress()
|
||||
oldDestination := packet.network.DestinationAddress()
|
||||
newSource := oldSource
|
||||
newDestination := oldDestination
|
||||
if rule.sourceAddress.Len() > 0 {
|
||||
newSource = rule.sourceAddress
|
||||
}
|
||||
if rule.destinationAddress.Len() > 0 {
|
||||
newDestination = rule.destinationAddress
|
||||
}
|
||||
if ipHdr, isIPv4 := packet.network.(header.IPv4); isIPv4 {
|
||||
if newSource != oldSource {
|
||||
ipHdr.SetSourceAddressWithChecksumUpdate(newSource)
|
||||
}
|
||||
if newDestination != oldDestination {
|
||||
ipHdr.SetDestinationAddressWithChecksumUpdate(newDestination)
|
||||
}
|
||||
} else {
|
||||
if newSource != oldSource {
|
||||
packet.network.SetSourceAddress(newSource)
|
||||
}
|
||||
if newDestination != oldDestination {
|
||||
packet.network.SetDestinationAddress(newDestination)
|
||||
}
|
||||
}
|
||||
transport := packet.transport
|
||||
switch packet.protocol {
|
||||
case uint8(header.TCPProtocolNumber):
|
||||
if len(transport) < header.TCPMinimumSize {
|
||||
return
|
||||
}
|
||||
tcpHdr := header.TCP(transport)
|
||||
if newSource != oldSource {
|
||||
tcpHdr.UpdateChecksumPseudoHeaderAddress(oldSource, newSource, true)
|
||||
}
|
||||
if newDestination != oldDestination {
|
||||
tcpHdr.UpdateChecksumPseudoHeaderAddress(oldDestination, newDestination, true)
|
||||
}
|
||||
if rule.rewriteSourcePort {
|
||||
tcpHdr.SetSourcePortWithChecksumUpdate(rule.sourcePort)
|
||||
}
|
||||
if rule.rewriteDestinationPort {
|
||||
tcpHdr.SetDestinationPortWithChecksumUpdate(rule.destinationPort)
|
||||
}
|
||||
case uint8(header.UDPProtocolNumber):
|
||||
if len(transport) < header.UDPMinimumSize {
|
||||
return
|
||||
}
|
||||
udpHdr := header.UDP(transport)
|
||||
if packet.ipVersion == 4 && udpHdr.Checksum() == 0 {
|
||||
if rule.rewriteSourcePort {
|
||||
udpHdr.SetSourcePort(rule.sourcePort)
|
||||
}
|
||||
if rule.rewriteDestinationPort {
|
||||
udpHdr.SetDestinationPort(rule.destinationPort)
|
||||
}
|
||||
return
|
||||
}
|
||||
if newSource != oldSource {
|
||||
udpHdr.UpdateChecksumPseudoHeaderAddress(oldSource, newSource, true)
|
||||
}
|
||||
if newDestination != oldDestination {
|
||||
udpHdr.UpdateChecksumPseudoHeaderAddress(oldDestination, newDestination, true)
|
||||
}
|
||||
if rule.rewriteSourcePort {
|
||||
udpHdr.SetSourcePortWithChecksumUpdate(rule.sourcePort)
|
||||
}
|
||||
if rule.rewriteDestinationPort {
|
||||
udpHdr.SetDestinationPortWithChecksumUpdate(rule.destinationPort)
|
||||
}
|
||||
case uint8(header.ICMPv4ProtocolNumber):
|
||||
if len(transport) < header.ICMPv4MinimumSize {
|
||||
return
|
||||
}
|
||||
icmpHdr := header.ICMPv4(transport)
|
||||
if rule.rewriteSourcePort {
|
||||
icmpHdr.SetIdentWithChecksumUpdate(rule.sourcePort)
|
||||
} else if rule.rewriteDestinationPort {
|
||||
icmpHdr.SetIdentWithChecksumUpdate(rule.destinationPort)
|
||||
}
|
||||
case uint8(header.ICMPv6ProtocolNumber):
|
||||
if len(transport) < header.ICMPv6MinimumSize {
|
||||
return
|
||||
}
|
||||
icmpHdr := header.ICMPv6(transport)
|
||||
if newSource != oldSource {
|
||||
icmpHdr.UpdateChecksumPseudoHeaderAddress(oldSource, newSource)
|
||||
}
|
||||
if newDestination != oldDestination {
|
||||
icmpHdr.UpdateChecksumPseudoHeaderAddress(oldDestination, newDestination)
|
||||
}
|
||||
if rule.rewriteSourcePort {
|
||||
icmpHdr.SetIdentWithChecksumUpdate(rule.sourcePort)
|
||||
} else if rule.rewriteDestinationPort {
|
||||
icmpHdr.SetIdentWithChecksumUpdate(rule.destinationPort)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func applyRewriteRaw(packet *forwardPacket, rule *rewriteRule) {
|
||||
if rule.sourceAddress.Len() > 0 {
|
||||
packet.network.SetSourceAddress(rule.sourceAddress)
|
||||
}
|
||||
if rule.destinationAddress.Len() > 0 {
|
||||
packet.network.SetDestinationAddress(rule.destinationAddress)
|
||||
}
|
||||
transport := packet.transport
|
||||
switch packet.protocol {
|
||||
case uint8(header.TCPProtocolNumber):
|
||||
if len(transport) < header.TCPMinimumSize {
|
||||
return
|
||||
}
|
||||
tcpHdr := header.TCP(transport)
|
||||
if rule.rewriteSourcePort {
|
||||
tcpHdr.SetSourcePort(rule.sourcePort)
|
||||
}
|
||||
if rule.rewriteDestinationPort {
|
||||
tcpHdr.SetDestinationPort(rule.destinationPort)
|
||||
}
|
||||
case uint8(header.UDPProtocolNumber):
|
||||
if len(transport) < header.UDPMinimumSize {
|
||||
return
|
||||
}
|
||||
udpHdr := header.UDP(transport)
|
||||
if rule.rewriteSourcePort {
|
||||
udpHdr.SetSourcePort(rule.sourcePort)
|
||||
}
|
||||
if rule.rewriteDestinationPort {
|
||||
udpHdr.SetDestinationPort(rule.destinationPort)
|
||||
}
|
||||
case uint8(header.ICMPv4ProtocolNumber):
|
||||
if len(transport) < header.ICMPv4MinimumSize {
|
||||
return
|
||||
}
|
||||
icmpHdr := header.ICMPv4(transport)
|
||||
if rule.rewriteSourcePort {
|
||||
icmpHdr.SetIdent(rule.sourcePort)
|
||||
} else if rule.rewriteDestinationPort {
|
||||
icmpHdr.SetIdent(rule.destinationPort)
|
||||
}
|
||||
case uint8(header.ICMPv6ProtocolNumber):
|
||||
if len(transport) < header.ICMPv6MinimumSize {
|
||||
return
|
||||
}
|
||||
icmpHdr := header.ICMPv6(transport)
|
||||
if rule.rewriteSourcePort {
|
||||
icmpHdr.SetIdent(rule.sourcePort)
|
||||
} else if rule.rewriteDestinationPort {
|
||||
icmpHdr.SetIdent(rule.destinationPort)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func recomputeChecksums(packet *forwardPacket) {
|
||||
if ipHdr, isIPv4 := packet.network.(header.IPv4); isIPv4 {
|
||||
ipHdr.SetChecksum(0)
|
||||
ipHdr.SetChecksum(^ipHdr.CalculateChecksum())
|
||||
}
|
||||
transport := packet.transport
|
||||
switch packet.protocol {
|
||||
case uint8(header.TCPProtocolNumber):
|
||||
if len(transport) < header.TCPMinimumSize {
|
||||
return
|
||||
}
|
||||
tcpHdr := header.TCP(transport)
|
||||
tcpHdr.SetChecksum(0)
|
||||
payloadChecksum := checksum.Checksum(tcpHdr.Payload(), 0)
|
||||
pseudoChecksum := header.PseudoHeaderChecksum(header.TCPProtocolNumber, packet.network.SourceAddressSlice(), packet.network.DestinationAddressSlice(), uint16(len(transport)))
|
||||
tcpHdr.SetChecksum(^tcpHdr.CalculateChecksum(checksum.Combine(pseudoChecksum, payloadChecksum)))
|
||||
case uint8(header.UDPProtocolNumber):
|
||||
if len(transport) < header.UDPMinimumSize {
|
||||
return
|
||||
}
|
||||
udpHdr := header.UDP(transport)
|
||||
if packet.ipVersion == 4 && udpHdr.Checksum() == 0 {
|
||||
return
|
||||
}
|
||||
udpHdr.SetChecksum(0)
|
||||
payloadChecksum := checksum.Checksum(udpHdr.Payload(), 0)
|
||||
pseudoChecksum := header.PseudoHeaderChecksum(header.UDPProtocolNumber, packet.network.SourceAddressSlice(), packet.network.DestinationAddressSlice(), udpHdr.Length())
|
||||
udpChecksum := ^udpHdr.CalculateChecksum(checksum.Combine(pseudoChecksum, payloadChecksum))
|
||||
if udpChecksum == 0 {
|
||||
udpChecksum = 0xffff
|
||||
}
|
||||
udpHdr.SetChecksum(udpChecksum)
|
||||
case uint8(header.ICMPv4ProtocolNumber):
|
||||
if len(transport) < header.ICMPv4MinimumSize {
|
||||
return
|
||||
}
|
||||
icmpHdr := header.ICMPv4(transport)
|
||||
icmpHdr.SetChecksum(0)
|
||||
icmpHdr.SetChecksum(header.ICMPv4Checksum(icmpHdr, 0))
|
||||
case uint8(header.ICMPv6ProtocolNumber):
|
||||
if len(transport) < header.ICMPv6MinimumSize {
|
||||
return
|
||||
}
|
||||
icmpHdr := header.ICMPv6(transport)
|
||||
icmpHdr.SetChecksum(0)
|
||||
icmpHdr.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{
|
||||
Header: icmpHdr,
|
||||
Src: packet.network.SourceAddressSlice(),
|
||||
Dst: packet.network.DestinationAddressSlice(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
func clampTCPMSS(packet *forwardPacket, effectiveMTU uint32) {
|
||||
if effectiveMTU == 0 || packet.protocol != uint8(header.TCPProtocolNumber) {
|
||||
return
|
||||
}
|
||||
transport := packet.transport
|
||||
if len(transport) < header.TCPMinimumSize {
|
||||
return
|
||||
}
|
||||
tcpHdr := header.TCP(transport)
|
||||
tcpHeaderLength := int(tcpHdr.DataOffset())
|
||||
if tcpHeaderLength < header.TCPMinimumSize || tcpHeaderLength > len(transport) {
|
||||
return
|
||||
}
|
||||
var networkHeaderLength int
|
||||
switch packet.ipVersion {
|
||||
case 4:
|
||||
networkHeaderLength = len(packet.network.(header.IPv4)) - len(transport)
|
||||
default:
|
||||
networkHeaderLength = len(packet.network.(header.IPv6)) - len(transport)
|
||||
}
|
||||
if effectiveMTU <= uint32(networkHeaderLength+header.TCPMinimumSize) {
|
||||
return
|
||||
}
|
||||
maxMSS := min(effectiveMTU-uint32(networkHeaderLength+header.TCPMinimumSize), header.TCPMaximumMSS)
|
||||
options := tcpHdr.Options()
|
||||
for i := 0; i < len(options); {
|
||||
switch options[i] {
|
||||
case header.TCPOptionEOL:
|
||||
return
|
||||
case header.TCPOptionNOP:
|
||||
i++
|
||||
continue
|
||||
case header.TCPOptionMSS:
|
||||
if i+header.TCPOptionMSSLength > len(options) || options[i+1] != header.TCPOptionMSSLength {
|
||||
return
|
||||
}
|
||||
currentMSS := binary.BigEndian.Uint16(options[i+2:])
|
||||
if uint32(currentMSS) <= maxMSS {
|
||||
return
|
||||
}
|
||||
binary.BigEndian.PutUint16(options[i+2:], uint16(maxMSS))
|
||||
return
|
||||
default:
|
||||
if i+2 > len(options) {
|
||||
return
|
||||
}
|
||||
optionLength := int(options[i+1])
|
||||
if optionLength < 2 || i+optionLength > len(options) {
|
||||
return
|
||||
}
|
||||
i += optionLength
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func rewriteEmbeddedDestination(embedded *embeddedPacket, destination tcpip.Address, selector uint16, remapSelector bool) {
|
||||
oldDestination := embedded.network.DestinationAddress()
|
||||
if ipHdr, isIPv4 := embedded.network.(header.IPv4); isIPv4 {
|
||||
ipHdr.SetDestinationAddressWithChecksumUpdate(destination)
|
||||
} else {
|
||||
embedded.network.SetDestinationAddress(destination)
|
||||
}
|
||||
rewriteEmbeddedSelector(embedded, oldDestination, destination, selector, remapSelector, true)
|
||||
}
|
||||
|
||||
func rewriteEmbeddedSource(embedded *embeddedPacket, source tcpip.Address, selector uint16, remapSelector bool) {
|
||||
oldSource := embedded.network.SourceAddress()
|
||||
if ipHdr, isIPv4 := embedded.network.(header.IPv4); isIPv4 {
|
||||
ipHdr.SetSourceAddressWithChecksumUpdate(source)
|
||||
} else {
|
||||
embedded.network.SetSourceAddress(source)
|
||||
}
|
||||
rewriteEmbeddedSelector(embedded, oldSource, source, selector, remapSelector, false)
|
||||
}
|
||||
|
||||
func rewriteEmbeddedSelector(embedded *embeddedPacket, oldAddress, newAddress tcpip.Address, selector uint16, remapSelector bool, destinationSide bool) {
|
||||
if !remapSelector {
|
||||
return
|
||||
}
|
||||
payload := embedded.payload
|
||||
_, isIPv4 := embedded.network.(header.IPv4)
|
||||
switch embedded.protocol {
|
||||
case uint8(header.TCPProtocolNumber):
|
||||
if len(payload) >= 4 {
|
||||
if destinationSide {
|
||||
binary.BigEndian.PutUint16(payload[2:], selector)
|
||||
} else {
|
||||
binary.BigEndian.PutUint16(payload[0:], selector)
|
||||
}
|
||||
}
|
||||
case uint8(header.UDPProtocolNumber):
|
||||
if len(payload) >= header.UDPMinimumSize {
|
||||
udpHdr := header.UDP(payload)
|
||||
if isIPv4 && udpHdr.Checksum() == 0 {
|
||||
if destinationSide {
|
||||
udpHdr.SetDestinationPort(selector)
|
||||
} else {
|
||||
udpHdr.SetSourcePort(selector)
|
||||
}
|
||||
} else {
|
||||
if oldAddress != newAddress {
|
||||
udpHdr.UpdateChecksumPseudoHeaderAddress(oldAddress, newAddress, true)
|
||||
}
|
||||
if destinationSide {
|
||||
udpHdr.SetDestinationPortWithChecksumUpdate(selector)
|
||||
} else {
|
||||
udpHdr.SetSourcePortWithChecksumUpdate(selector)
|
||||
}
|
||||
}
|
||||
}
|
||||
case uint8(header.ICMPv4ProtocolNumber):
|
||||
if len(payload) >= header.ICMPv4MinimumSize {
|
||||
header.ICMPv4(payload).SetIdentWithChecksumUpdate(selector)
|
||||
}
|
||||
case uint8(header.ICMPv6ProtocolNumber):
|
||||
if len(payload) >= header.ICMPv6MinimumSize {
|
||||
icmpHdr := header.ICMPv6(payload)
|
||||
if oldAddress != newAddress {
|
||||
icmpHdr.UpdateChecksumPseudoHeaderAddress(oldAddress, newAddress)
|
||||
}
|
||||
icmpHdr.SetIdentWithChecksumUpdate(selector)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue