sing-tun/route_nat.go
2023-03-22 01:28:18 +08:00

119 lines
2.9 KiB
Go

package tun
import (
"net/netip"
"sync"
"github.com/sagernet/sing-tun/internal/clashtcpip"
)
type NatMapping struct {
access sync.RWMutex
sessions map[RouteSession]RouteContext
ipRewrite bool
}
func NewNatMapping(ipRewrite bool) *NatMapping {
return &NatMapping{
sessions: make(map[RouteSession]RouteContext),
ipRewrite: ipRewrite,
}
}
func (m *NatMapping) CreateSession(session RouteSession, context RouteContext) {
if m.ipRewrite {
session.Source = netip.AddrPort{}
}
m.access.Lock()
m.sessions[session] = context
m.access.Unlock()
}
func (m *NatMapping) DeleteSession(session RouteSession) {
if m.ipRewrite {
session.Source = netip.AddrPort{}
}
m.access.Lock()
delete(m.sessions, session)
m.access.Unlock()
}
func (m *NatMapping) WritePacket(packet []byte) (bool, error) {
var routeSession RouteSession
var ipHdr clashtcpip.IP
switch ipVersion := packet[0] >> 4; ipVersion {
case 4:
routeSession.IPVersion = 4
ipHdr = clashtcpip.IPv4Packet(packet)
case 6:
routeSession.IPVersion = 6
ipHdr = clashtcpip.IPv6Packet(packet)
default:
return false, nil
}
routeSession.Network = ipHdr.Protocol()
switch routeSession.Network {
case clashtcpip.TCP:
tcpHdr := clashtcpip.TCPPacket(ipHdr.Payload())
routeSession.Destination = netip.AddrPortFrom(ipHdr.SourceIP(), tcpHdr.SourcePort())
if !m.ipRewrite {
routeSession.Source = netip.AddrPortFrom(ipHdr.DestinationIP(), tcpHdr.DestinationPort())
}
case clashtcpip.UDP:
udpHdr := clashtcpip.UDPPacket(ipHdr.Payload())
routeSession.Destination = netip.AddrPortFrom(ipHdr.SourceIP(), udpHdr.SourcePort())
if !m.ipRewrite {
routeSession.Source = netip.AddrPortFrom(ipHdr.DestinationIP(), udpHdr.DestinationPort())
}
default:
routeSession.Destination = netip.AddrPortFrom(ipHdr.SourceIP(), 0)
if !m.ipRewrite {
routeSession.Source = netip.AddrPortFrom(ipHdr.DestinationIP(), 0)
}
}
m.access.RLock()
context, loaded := m.sessions[routeSession]
m.access.RUnlock()
if !loaded {
return false, nil
}
return true, context.WritePacket(packet)
}
type NatWriter struct {
inet4Address netip.Addr
inet6Address netip.Addr
}
func NewNatWriter(inet4Address netip.Addr, inet6Address netip.Addr) *NatWriter {
return &NatWriter{
inet4Address: inet4Address,
inet6Address: inet6Address,
}
}
func (w *NatWriter) RewritePacket(packet []byte) {
var ipHdr clashtcpip.IP
var bindAddr netip.Addr
switch ipVersion := packet[0] >> 4; ipVersion {
case 4:
ipHdr = clashtcpip.IPv4Packet(packet)
bindAddr = w.inet4Address
case 6:
ipHdr = clashtcpip.IPv6Packet(packet)
bindAddr = w.inet6Address
default:
return
}
ipHdr.SetSourceIP(bindAddr)
switch ipHdr.Protocol() {
case clashtcpip.TCP:
tcpHdr := clashtcpip.TCPPacket(ipHdr.Payload())
tcpHdr.ResetChecksum(ipHdr.PseudoSum())
case clashtcpip.UDP:
udpHdr := clashtcpip.UDPPacket(ipHdr.Payload())
udpHdr.ResetChecksum(ipHdr.PseudoSum())
default:
}
ipHdr.ResetChecksum()
}