Files
2026-07-09 11:20:34 +08:00

124 lines
3.5 KiB
Go

//go:build linux || darwin || (windows && (amd64 || 386))
//nolint:unused
package bridge
import (
"net/netip"
"sync"
"github.com/sagernet/sing-tun/gtcpip"
"github.com/sagernet/sing-tun/gtcpip/checksum"
"github.com/sagernet/sing-tun/gtcpip/header"
E "github.com/sagernet/sing/common/exceptions"
)
const (
bridgeTunMTU = 0xffff
maxPacketLength = 0xffff
bridgeMaxInstances = 254
bridgeWriteBatchSize = 32
)
var (
bridgeInet4Base = netip.MustParseAddr("192.0.2.1")
bridgeInet6Base = netip.MustParseAddr("2001:db8::1")
bridgeIndexAccess sync.Mutex
bridgeIndexInUse [bridgeMaxInstances]bool
)
func allocateBridgeIndex() (uint32, error) {
bridgeIndexAccess.Lock()
defer bridgeIndexAccess.Unlock()
for index := range bridgeMaxInstances {
if !bridgeIndexInUse[index] {
bridgeIndexInUse[index] = true
return uint32(index), nil
}
}
return 0, E.New("too many bridge outbounds: limit is ", bridgeMaxInstances)
}
func releaseBridgeIndex(index uint32) {
bridgeIndexAccess.Lock()
defer bridgeIndexAccess.Unlock()
bridgeIndexInUse[index] = false
}
func addressAt(base netip.Addr, offset uint32) netip.Addr {
addr := base
for range offset {
addr = addr.Next()
}
return addr
}
func fixReturnChecksum(packet []byte) {
switch header.IPVersion(packet) {
case header.IPv4Version:
if len(packet) < header.IPv4MinimumSize {
return
}
ipHdr := header.IPv4(packet)
if !ipHdr.IsValid(len(packet)) {
return
}
if ipHdr.Flags()&header.IPv4FlagMoreFragments != 0 || ipHdr.FragmentOffset() != 0 {
return
}
ipHdr.SetChecksum(0)
ipHdr.SetChecksum(^ipHdr.CalculateChecksum())
recomputeTransportChecksum(ipHdr.TransportProtocol(), ipHdr.Payload(), ipHdr.SourceAddressSlice(), ipHdr.DestinationAddressSlice())
case header.IPv6Version:
if len(packet) < header.IPv6MinimumSize {
return
}
ipHdr := header.IPv6(packet)
recomputeTransportChecksum(ipHdr.TransportProtocol(), ipHdr.Payload(), ipHdr.SourceAddressSlice(), ipHdr.DestinationAddressSlice())
}
}
func recomputeTransportChecksum(protocol tcpip.TransportProtocolNumber, transport []byte, source []byte, destination []byte) {
switch protocol {
case 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, source, destination, uint16(len(transport)))
tcpHdr.SetChecksum(^tcpHdr.CalculateChecksum(checksum.Combine(pseudoChecksum, payloadChecksum)))
case header.UDPProtocolNumber:
if len(transport) < header.UDPMinimumSize {
return
}
udpHdr := header.UDP(transport)
udpHdr.SetChecksum(0)
payloadChecksum := checksum.Checksum(udpHdr.Payload(), 0)
pseudoChecksum := header.PseudoHeaderChecksum(header.UDPProtocolNumber, source, destination, udpHdr.Length())
udpChecksum := ^udpHdr.CalculateChecksum(checksum.Combine(pseudoChecksum, payloadChecksum))
if udpChecksum == 0 {
udpChecksum = 0xffff
}
udpHdr.SetChecksum(udpChecksum)
case header.ICMPv4ProtocolNumber:
if len(transport) < header.ICMPv4MinimumSize {
return
}
icmpHdr := header.ICMPv4(transport)
icmpHdr.SetChecksum(header.ICMPv4Checksum(icmpHdr, 0))
case header.ICMPv6ProtocolNumber:
if len(transport) < header.ICMPv6MinimumSize {
return
}
icmpHdr := header.ICMPv6(transport)
icmpHdr.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{
Header: icmpHdr,
Src: source,
Dst: destination,
}))
}
}