Compare commits
2 Commits
v0.2.1
...
v0.2.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b967c6f837 | ||
|
|
6a1419aeae |
6
go.mod
6
go.mod
@@ -7,11 +7,11 @@ require (
|
||||
github.com/go-ole/go-ole v1.3.0
|
||||
github.com/sagernet/gvisor v0.0.0-20231209105102-8d27a30e436e
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97
|
||||
github.com/sagernet/sing v0.3.0
|
||||
github.com/sagernet/sing v0.2.20-0.20231211084415-35e7014b0898
|
||||
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
||||
golang.org/x/net v0.20.0
|
||||
golang.org/x/sys v0.16.0
|
||||
golang.org/x/net v0.19.0
|
||||
golang.org/x/sys v0.15.0
|
||||
)
|
||||
|
||||
require (
|
||||
|
||||
16
go.sum
16
go.sum
@@ -1,30 +1,26 @@
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
|
||||
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/sagernet/gvisor v0.0.0-20231209105102-8d27a30e436e h1:DOkjByVeAR56dkszjnMZke4wr7yM/1xHaJF3G9olkEE=
|
||||
github.com/sagernet/gvisor v0.0.0-20231209105102-8d27a30e436e/go.mod h1:fLxq/gtp0qzkaEwywlRRiGmjOK5ES/xUzyIKIFP2Asw=
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||
github.com/sagernet/sing v0.3.0 h1:PIDVFZHnQAAYRL1UYqNM+0k5s8f/tb1lUW6UDcQiOc8=
|
||||
github.com/sagernet/sing v0.3.0/go.mod h1:9pfuAH6mZfgnz/YjP6xu5sxx882rfyjpcrTdUpd6w3g=
|
||||
github.com/sagernet/sing v0.2.20-0.20231211084415-35e7014b0898 h1:ZR0wpw4/0NCICOX10SIUW8jpPVV7+D98nGA6p4zWICo=
|
||||
github.com/sagernet/sing v0.2.20-0.20231211084415-35e7014b0898/go.mod h1:Ce5LNojQOgOiWhiD8pPD6E9H7e2KgtOe3Zxx4Ou5u80=
|
||||
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 h1:rc/CcqLH3lh8n+csdOuDfP+NuykE0U6AeYSJJHKDgSg=
|
||||
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9/go.mod h1:a/83NAfUXvEuLpmxDssAXxgUgrEy12MId3Wd7OTs76s=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
|
||||
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
||||
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
|
||||
@@ -50,10 +50,6 @@ func (p TCPPacket) SetChecksum(sum [2]byte) {
|
||||
p[17] = sum[1]
|
||||
}
|
||||
|
||||
func (p TCPPacket) OffloadChecksum() {
|
||||
p.SetChecksum(zeroChecksum)
|
||||
}
|
||||
|
||||
func (p TCPPacket) ResetChecksum(psum uint32) {
|
||||
p.SetChecksum(zeroChecksum)
|
||||
p.SetChecksum(Checksum(psum, p))
|
||||
|
||||
@@ -45,10 +45,6 @@ func (p UDPPacket) SetChecksum(sum [2]byte) {
|
||||
p[7] = sum[1]
|
||||
}
|
||||
|
||||
func (p UDPPacket) OffloadChecksum() {
|
||||
p.SetChecksum(zeroChecksum)
|
||||
}
|
||||
|
||||
func (p UDPPacket) ResetChecksum(psum uint32) {
|
||||
p.SetChecksum(zeroChecksum)
|
||||
p.SetChecksum(Checksum(psum, p))
|
||||
|
||||
@@ -91,18 +91,17 @@ func (m *Mixed) tunLoop() {
|
||||
m.wintunLoop(winTun)
|
||||
return
|
||||
}
|
||||
if linuxTUN, isLinuxTUN := m.tun.(LinuxTUN); isLinuxTUN {
|
||||
m.frontHeadroom = linuxTUN.FrontHeadroom()
|
||||
m.txChecksumOffload = linuxTUN.TXChecksumOffload()
|
||||
batchSize := linuxTUN.BatchSize()
|
||||
if batchTUN, isBatchTUN := m.tun.(BatchTUN); isBatchTUN {
|
||||
batchSize := batchTUN.BatchSize()
|
||||
if batchSize > 1 {
|
||||
m.batchLoop(linuxTUN, batchSize)
|
||||
m.batchLoop(batchTUN, batchSize)
|
||||
return
|
||||
}
|
||||
}
|
||||
packetBuffer := make([]byte, m.mtu+PacketOffset)
|
||||
frontHeadroom := m.tun.FrontHeadroom()
|
||||
packetBuffer := make([]byte, m.mtu+frontHeadroom+PacketOffset)
|
||||
for {
|
||||
n, err := m.tun.Read(packetBuffer)
|
||||
n, err := m.tun.Read(packetBuffer[frontHeadroom:])
|
||||
if err != nil {
|
||||
if E.IsClosed(err) {
|
||||
return
|
||||
@@ -112,8 +111,8 @@ func (m *Mixed) tunLoop() {
|
||||
if n < clashtcpip.IPv4PacketMinLength {
|
||||
continue
|
||||
}
|
||||
rawPacket := packetBuffer[:n]
|
||||
packet := packetBuffer[PacketOffset:n]
|
||||
rawPacket := packetBuffer[:frontHeadroom+n]
|
||||
packet := packetBuffer[frontHeadroom+PacketOffset : frontHeadroom+n]
|
||||
if m.processPacket(packet) {
|
||||
_, err = m.tun.Write(rawPacket)
|
||||
if err != nil {
|
||||
@@ -143,15 +142,18 @@ func (m *Mixed) wintunLoop(winTun WinTun) {
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Mixed) batchLoop(linuxTUN LinuxTUN, batchSize int) {
|
||||
func (m *Mixed) batchLoop(linuxTUN BatchTUN, batchSize int) {
|
||||
frontHeadroom := m.tun.FrontHeadroom()
|
||||
packetBuffers := make([][]byte, batchSize)
|
||||
readBuffers := make([][]byte, batchSize)
|
||||
writeBuffers := make([][]byte, batchSize)
|
||||
packetSizes := make([]int, batchSize)
|
||||
for i := range packetBuffers {
|
||||
packetBuffers[i] = make([]byte, m.mtu+m.frontHeadroom)
|
||||
packetBuffers[i] = make([]byte, m.mtu+frontHeadroom+PacketOffset)
|
||||
readBuffers[i] = packetBuffers[i][frontHeadroom:]
|
||||
}
|
||||
for {
|
||||
n, err := linuxTUN.BatchRead(packetBuffers, m.frontHeadroom, packetSizes)
|
||||
n, err := linuxTUN.BatchRead(readBuffers, packetSizes)
|
||||
if err != nil {
|
||||
if E.IsClosed(err) {
|
||||
return
|
||||
@@ -167,13 +169,13 @@ func (m *Mixed) batchLoop(linuxTUN LinuxTUN, batchSize int) {
|
||||
continue
|
||||
}
|
||||
packetBuffer := packetBuffers[i]
|
||||
packet := packetBuffer[m.frontHeadroom : m.frontHeadroom+packetSize]
|
||||
packet := packetBuffer[frontHeadroom+PacketOffset : frontHeadroom+packetSize]
|
||||
if m.processPacket(packet) {
|
||||
writeBuffers = append(writeBuffers, packetBuffer[:m.frontHeadroom+packetSize])
|
||||
writeBuffers = append(writeBuffers, packetBuffer[:frontHeadroom+packetSize])
|
||||
}
|
||||
}
|
||||
if len(writeBuffers) > 0 {
|
||||
err = linuxTUN.BatchWrite(writeBuffers, m.frontHeadroom)
|
||||
err = linuxTUN.BatchWrite(writeBuffers)
|
||||
if err != nil {
|
||||
m.logger.Trace(E.Cause(err, "batch write packet"))
|
||||
}
|
||||
|
||||
105
stack_system.go
105
stack_system.go
@@ -41,8 +41,6 @@ type System struct {
|
||||
udpNat *udpnat.Service[netip.AddrPort]
|
||||
bindInterface bool
|
||||
interfaceFinder control.InterfaceFinder
|
||||
frontHeadroom int
|
||||
txChecksumOffload bool
|
||||
}
|
||||
|
||||
type Session struct {
|
||||
@@ -111,9 +109,9 @@ func (s *System) start() error {
|
||||
var listener net.ListenConfig
|
||||
if s.bindInterface {
|
||||
listener.Control = control.Append(listener.Control, func(network, address string, conn syscall.RawConn) error {
|
||||
bindErr := control.BindToInterface0(s.interfaceFinder, conn, network, address, s.tunName, -1, true)
|
||||
if bindErr != nil {
|
||||
s.logger.Warn("bind forwarder to interface: ", bindErr)
|
||||
err := control.BindToInterface(s.interfaceFinder, s.tunName, -1)(network, address, conn)
|
||||
if err != nil {
|
||||
s.logger.Warn("bind forwarder to interface: ", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
@@ -146,18 +144,17 @@ func (s *System) tunLoop() {
|
||||
s.wintunLoop(winTun)
|
||||
return
|
||||
}
|
||||
if linuxTUN, isLinuxTUN := s.tun.(LinuxTUN); isLinuxTUN {
|
||||
s.frontHeadroom = linuxTUN.FrontHeadroom()
|
||||
s.txChecksumOffload = linuxTUN.TXChecksumOffload()
|
||||
batchSize := linuxTUN.BatchSize()
|
||||
if batchTUN, isBatchTUN := s.tun.(BatchTUN); isBatchTUN {
|
||||
batchSize := batchTUN.BatchSize()
|
||||
if batchSize > 1 {
|
||||
s.batchLoop(linuxTUN, batchSize)
|
||||
s.batchLoop(batchTUN, batchSize)
|
||||
return
|
||||
}
|
||||
}
|
||||
packetBuffer := make([]byte, s.mtu+PacketOffset)
|
||||
frontHeadroom := s.tun.FrontHeadroom()
|
||||
packetBuffer := make([]byte, s.mtu+frontHeadroom+PacketOffset)
|
||||
for {
|
||||
n, err := s.tun.Read(packetBuffer)
|
||||
n, err := s.tun.Read(packetBuffer[frontHeadroom:])
|
||||
if err != nil {
|
||||
if E.IsClosed(err) {
|
||||
return
|
||||
@@ -167,8 +164,8 @@ func (s *System) tunLoop() {
|
||||
if n < clashtcpip.IPv4PacketMinLength {
|
||||
continue
|
||||
}
|
||||
rawPacket := packetBuffer[:n]
|
||||
packet := packetBuffer[PacketOffset:n]
|
||||
rawPacket := packetBuffer[:frontHeadroom+n]
|
||||
packet := packetBuffer[frontHeadroom+PacketOffset : frontHeadroom+n]
|
||||
if s.processPacket(packet) {
|
||||
_, err = s.tun.Write(rawPacket)
|
||||
if err != nil {
|
||||
@@ -198,15 +195,18 @@ func (s *System) wintunLoop(winTun WinTun) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *System) batchLoop(linuxTUN LinuxTUN, batchSize int) {
|
||||
func (s *System) batchLoop(linuxTUN BatchTUN, batchSize int) {
|
||||
frontHeadroom := s.tun.FrontHeadroom()
|
||||
packetBuffers := make([][]byte, batchSize)
|
||||
readBuffers := make([][]byte, batchSize)
|
||||
writeBuffers := make([][]byte, batchSize)
|
||||
packetSizes := make([]int, batchSize)
|
||||
for i := range packetBuffers {
|
||||
packetBuffers[i] = make([]byte, s.mtu+s.frontHeadroom)
|
||||
packetBuffers[i] = make([]byte, s.mtu+frontHeadroom+PacketOffset)
|
||||
readBuffers[i] = packetBuffers[i][frontHeadroom:]
|
||||
}
|
||||
for {
|
||||
n, err := linuxTUN.BatchRead(packetBuffers, s.frontHeadroom, packetSizes)
|
||||
n, err := linuxTUN.BatchRead(readBuffers, packetSizes)
|
||||
if err != nil {
|
||||
if E.IsClosed(err) {
|
||||
return
|
||||
@@ -222,13 +222,13 @@ func (s *System) batchLoop(linuxTUN LinuxTUN, batchSize int) {
|
||||
continue
|
||||
}
|
||||
packetBuffer := packetBuffers[i]
|
||||
packet := packetBuffer[s.frontHeadroom : s.frontHeadroom+packetSize]
|
||||
packet := packetBuffer[frontHeadroom+PacketOffset : frontHeadroom+packetSize]
|
||||
if s.processPacket(packet) {
|
||||
writeBuffers = append(writeBuffers, packetBuffer[:s.frontHeadroom+packetSize])
|
||||
writeBuffers = append(writeBuffers, packetBuffer[:frontHeadroom+packetSize])
|
||||
}
|
||||
}
|
||||
if len(writeBuffers) > 0 {
|
||||
err = linuxTUN.BatchWrite(writeBuffers, s.frontHeadroom)
|
||||
err = linuxTUN.BatchWrite(writeBuffers)
|
||||
if err != nil {
|
||||
s.logger.Trace(E.Cause(err, "batch write packet"))
|
||||
}
|
||||
@@ -354,13 +354,8 @@ func (s *System) processIPv4TCP(packet clashtcpip.IPv4Packet, header clashtcpip.
|
||||
packet.SetDestinationIP(s.inet4ServerAddress)
|
||||
header.SetDestinationPort(s.tcpPort)
|
||||
}
|
||||
if !s.txChecksumOffload {
|
||||
header.ResetChecksum(packet.PseudoSum())
|
||||
packet.ResetChecksum()
|
||||
} else {
|
||||
header.OffloadChecksum()
|
||||
packet.ResetChecksum()
|
||||
}
|
||||
header.ResetChecksum(packet.PseudoSum())
|
||||
packet.ResetChecksum()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -385,11 +380,8 @@ func (s *System) processIPv6TCP(packet clashtcpip.IPv6Packet, header clashtcpip.
|
||||
packet.SetDestinationIP(s.inet6ServerAddress)
|
||||
header.SetDestinationPort(s.tcpPort6)
|
||||
}
|
||||
if !s.txChecksumOffload {
|
||||
header.ResetChecksum(packet.PseudoSum())
|
||||
} else {
|
||||
header.OffloadChecksum()
|
||||
}
|
||||
header.ResetChecksum(packet.PseudoSum())
|
||||
packet.ResetChecksum()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -420,13 +412,7 @@ func (s *System) processIPv4UDP(packet clashtcpip.IPv4Packet, header clashtcpip.
|
||||
headerLen := packet.HeaderLen() + clashtcpip.UDPHeaderSize
|
||||
headerCopy := make([]byte, headerLen)
|
||||
copy(headerCopy, packet[:headerLen])
|
||||
return &systemUDPPacketWriter4{
|
||||
s.tun,
|
||||
s.frontHeadroom + PacketOffset,
|
||||
headerCopy,
|
||||
source,
|
||||
s.txChecksumOffload,
|
||||
}
|
||||
return &systemUDPPacketWriter4{s.tun, s.tun.FrontHeadroom() + PacketOffset, headerCopy, source}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
@@ -452,13 +438,7 @@ func (s *System) processIPv6UDP(packet clashtcpip.IPv6Packet, header clashtcpip.
|
||||
headerLen := len(packet) - int(header.Length()) + clashtcpip.UDPHeaderSize
|
||||
headerCopy := make([]byte, headerLen)
|
||||
copy(headerCopy, packet[:headerLen])
|
||||
return &systemUDPPacketWriter6{
|
||||
s.tun,
|
||||
s.frontHeadroom + PacketOffset,
|
||||
headerCopy,
|
||||
source,
|
||||
s.txChecksumOffload,
|
||||
}
|
||||
return &systemUDPPacketWriter6{s.tun, s.tun.FrontHeadroom() + PacketOffset, headerCopy, source}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
@@ -490,11 +470,10 @@ func (s *System) processIPv6ICMP(packet clashtcpip.IPv6Packet, header clashtcpip
|
||||
}
|
||||
|
||||
type systemUDPPacketWriter4 struct {
|
||||
tun Tun
|
||||
frontHeadroom int
|
||||
header []byte
|
||||
source netip.AddrPort
|
||||
txChecksumOffload bool
|
||||
tun Tun
|
||||
frontHeadroom int
|
||||
header []byte
|
||||
source netip.AddrPort
|
||||
}
|
||||
|
||||
func (w *systemUDPPacketWriter4) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||
@@ -511,13 +490,8 @@ func (w *systemUDPPacketWriter4) WritePacket(buffer *buf.Buffer, destination M.S
|
||||
udpHdr.SetDestinationPort(udpHdr.SourcePort())
|
||||
udpHdr.SetSourcePort(destination.Port)
|
||||
udpHdr.SetLength(uint16(buffer.Len() + clashtcpip.UDPHeaderSize))
|
||||
if !w.txChecksumOffload {
|
||||
udpHdr.ResetChecksum(ipHdr.PseudoSum())
|
||||
ipHdr.ResetChecksum()
|
||||
} else {
|
||||
udpHdr.OffloadChecksum()
|
||||
ipHdr.ResetChecksum()
|
||||
}
|
||||
udpHdr.ResetChecksum(ipHdr.PseudoSum())
|
||||
ipHdr.ResetChecksum()
|
||||
if PacketOffset > 0 {
|
||||
newPacket.ExtendHeader(PacketOffset)[3] = syscall.AF_INET
|
||||
} else {
|
||||
@@ -527,11 +501,10 @@ func (w *systemUDPPacketWriter4) WritePacket(buffer *buf.Buffer, destination M.S
|
||||
}
|
||||
|
||||
type systemUDPPacketWriter6 struct {
|
||||
tun Tun
|
||||
frontHeadroom int
|
||||
header []byte
|
||||
source netip.AddrPort
|
||||
txChecksumOffload bool
|
||||
tun Tun
|
||||
frontHeadroom int
|
||||
header []byte
|
||||
source netip.AddrPort
|
||||
}
|
||||
|
||||
func (w *systemUDPPacketWriter6) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||
@@ -549,11 +522,7 @@ func (w *systemUDPPacketWriter6) WritePacket(buffer *buf.Buffer, destination M.S
|
||||
udpHdr.SetDestinationPort(udpHdr.SourcePort())
|
||||
udpHdr.SetSourcePort(destination.Port)
|
||||
udpHdr.SetLength(udpLen)
|
||||
if !w.txChecksumOffload {
|
||||
udpHdr.ResetChecksum(ipHdr.PseudoSum())
|
||||
} else {
|
||||
udpHdr.OffloadChecksum()
|
||||
}
|
||||
udpHdr.ResetChecksum(ipHdr.PseudoSum())
|
||||
if PacketOffset > 0 {
|
||||
newPacket.ExtendHeader(PacketOffset)[3] = syscall.AF_INET6
|
||||
} else {
|
||||
|
||||
13
tun.go
13
tun.go
@@ -24,6 +24,7 @@ type Handler interface {
|
||||
type Tun interface {
|
||||
io.ReadWriter
|
||||
N.VectorisedWriter
|
||||
N.FrontHeadroom
|
||||
Close() error
|
||||
}
|
||||
|
||||
@@ -32,13 +33,11 @@ type WinTun interface {
|
||||
ReadPacket() ([]byte, func(), error)
|
||||
}
|
||||
|
||||
type LinuxTUN interface {
|
||||
type BatchTUN interface {
|
||||
Tun
|
||||
N.FrontHeadroom
|
||||
BatchSize() int
|
||||
BatchRead(buffers [][]byte, offset int, readN []int) (n int, err error)
|
||||
BatchWrite(buffers [][]byte, offset int) error
|
||||
TXChecksumOffload() bool
|
||||
BatchRead(buffers [][]byte, readN []int) (n int, err error)
|
||||
BatchWrite(buffers [][]byte) error
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
@@ -47,6 +46,7 @@ type Options struct {
|
||||
Inet6Address []netip.Prefix
|
||||
MTU uint32
|
||||
GSO bool
|
||||
GSOMaxSize uint32
|
||||
AutoRoute bool
|
||||
StrictRoute bool
|
||||
Inet4RouteAddress []netip.Prefix
|
||||
@@ -64,9 +64,6 @@ type Options struct {
|
||||
TableIndex int
|
||||
FileDescriptor int
|
||||
Logger logger.Logger
|
||||
|
||||
// No work for TCP, do not use.
|
||||
_TXChecksumOffload bool
|
||||
}
|
||||
|
||||
func CalculateInterfaceName(name string) (tunName string) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
@@ -67,9 +68,14 @@ func New(options Options) (Tun, error) {
|
||||
if !ok {
|
||||
panic("create vectorised writer")
|
||||
}
|
||||
runtime.SetFinalizer(nativeTun.tunFile, nil)
|
||||
return nativeTun, nil
|
||||
}
|
||||
|
||||
func (t *NativeTun) FrontHeadroom() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (t *NativeTun) Read(p []byte) (n int, err error) {
|
||||
return t.tunFile.Read(p)
|
||||
}
|
||||
|
||||
123
tun_linux.go
123
tun_linux.go
@@ -24,7 +24,7 @@ import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
var _ LinuxTUN = (*NativeTun)(nil)
|
||||
var _ BatchTUN = (*NativeTun)(nil)
|
||||
|
||||
type NativeTun struct {
|
||||
tunFd int
|
||||
@@ -35,12 +35,9 @@ type NativeTun struct {
|
||||
ruleIndex6 []int
|
||||
gsoEnabled bool
|
||||
gsoBuffer []byte
|
||||
gsoToWrite []int
|
||||
gsoReadAccess sync.Mutex
|
||||
tcpGROAccess sync.Mutex
|
||||
tcp4GROTable *tcpGROTable
|
||||
tcp6GROTable *tcpGROTable
|
||||
txChecksumOffload bool
|
||||
}
|
||||
|
||||
func New(options Options) (Tun, error) {
|
||||
@@ -108,7 +105,7 @@ func (t *NativeTun) Read(p []byte) (n int, err error) {
|
||||
|
||||
func (t *NativeTun) Write(p []byte) (n int, err error) {
|
||||
if t.gsoEnabled {
|
||||
err = t.BatchWrite([][]byte{p}, virtioNetHdrLen)
|
||||
err = t.BatchWrite([][]byte{p})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -136,38 +133,44 @@ func (t *NativeTun) BatchSize() int {
|
||||
if !t.gsoEnabled {
|
||||
return 1
|
||||
}
|
||||
batchSize := int(gsoMaxSize/t.options.MTU) * 2
|
||||
batchSize := int(t.options.GSOMaxSize/t.options.MTU) * 2
|
||||
if batchSize > idealBatchSize {
|
||||
batchSize = idealBatchSize
|
||||
}
|
||||
return batchSize
|
||||
}
|
||||
|
||||
func (t *NativeTun) BatchRead(buffers [][]byte, offset int, readN []int) (n int, err error) {
|
||||
t.gsoReadAccess.Lock()
|
||||
defer t.gsoReadAccess.Unlock()
|
||||
n, err = t.tunFile.Read(t.gsoBuffer)
|
||||
if err != nil {
|
||||
func (t *NativeTun) BatchRead(buffers [][]byte, readN []int) (n int, err error) {
|
||||
if t.gsoEnabled {
|
||||
n, err = t.tunFile.Read(t.gsoBuffer)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n, err = handleVirtioRead(t.gsoBuffer[:n], buffers, readN, 0)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
} else {
|
||||
return 0, os.ErrInvalid
|
||||
}
|
||||
return handleVirtioRead(t.gsoBuffer[:n], buffers, readN, offset)
|
||||
}
|
||||
|
||||
func (t *NativeTun) BatchWrite(buffers [][]byte, offset int) error {
|
||||
func (t *NativeTun) BatchWrite(buffers [][]byte) error {
|
||||
t.tcpGROAccess.Lock()
|
||||
defer func() {
|
||||
t.tcp4GROTable.reset()
|
||||
t.tcp6GROTable.reset()
|
||||
t.tcpGROAccess.Unlock()
|
||||
}()
|
||||
t.gsoToWrite = t.gsoToWrite[:0]
|
||||
err := handleGRO(buffers, offset, t.tcp4GROTable, t.tcp6GROTable, &t.gsoToWrite)
|
||||
var toWrite []int
|
||||
err := handleGRO(buffers, virtioNetHdrLen, t.tcp4GROTable, t.tcp6GROTable, &toWrite)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
offset -= virtioNetHdrLen
|
||||
for _, bufferIndex := range t.gsoToWrite {
|
||||
_, err = t.tunFile.Write(buffers[bufferIndex][offset:])
|
||||
for _, bufferIndex := range toWrite {
|
||||
_, err = t.tunFile.Write(buffers[bufferIndex])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -247,45 +250,27 @@ func (t *NativeTun) configure(tunLink netlink.Link) error {
|
||||
}
|
||||
|
||||
if t.options.GSO {
|
||||
var vnetHdrEnabled bool
|
||||
vnetHdrEnabled, err = checkVNETHDREnabled(t.tunFd, t.options.Name)
|
||||
vnethdrEnabled, err := checkVNETHDREnabled(uint16(t.tunFd), t.options.Name)
|
||||
if err != nil {
|
||||
return E.Cause(err, "enable offload: check IFF_VNET_HDR enabled")
|
||||
}
|
||||
if !vnetHdrEnabled {
|
||||
if !vnethdrEnabled {
|
||||
return E.Cause(err, "enable offload: IFF_VNET_HDR not enabled")
|
||||
}
|
||||
err = setTCPOffload(t.tunFd)
|
||||
const (
|
||||
// TODO: support TSO with ECN bits
|
||||
tunOffloads = unix.TUN_F_CSUM | unix.TUN_F_TSO4 | unix.TUN_F_TSO6
|
||||
)
|
||||
err = unix.IoctlSetInt(t.tunFd, unix.TUNSETOFFLOAD, tunOffloads)
|
||||
if err != nil {
|
||||
return err
|
||||
return E.Cause(os.NewSyscallError("TUNSETOFFLOAD", err), "enable offload")
|
||||
}
|
||||
t.gsoEnabled = true
|
||||
t.gsoBuffer = make([]byte, virtioNetHdrLen+int(gsoMaxSize))
|
||||
t.gsoBuffer = make([]byte, virtioNetHdrLen+int(t.options.GSOMaxSize))
|
||||
t.tcp4GROTable = newTCPGROTable()
|
||||
t.tcp6GROTable = newTCPGROTable()
|
||||
}
|
||||
|
||||
var rxChecksumOffload bool
|
||||
rxChecksumOffload, err = checkChecksumOffload(t.options.Name, unix.ETHTOOL_GRXCSUM)
|
||||
if err == nil && !rxChecksumOffload {
|
||||
_ = setChecksumOffload(t.options.Name, unix.ETHTOOL_SRXCSUM)
|
||||
}
|
||||
|
||||
if t.options._TXChecksumOffload {
|
||||
var txChecksumOffload bool
|
||||
txChecksumOffload, err = checkChecksumOffload(t.options.Name, unix.ETHTOOL_GTXCSUM)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err == nil && !txChecksumOffload {
|
||||
err = setChecksumOffload(t.options.Name, unix.ETHTOOL_STXCSUM)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
t.txChecksumOffload = true
|
||||
}
|
||||
|
||||
err = netlink.LinkSetUp(tunLink)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -325,6 +310,18 @@ func (t *NativeTun) configure(tunLink netlink.Link) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkVNETHDREnabled(fd uint16, name string) (bool, error) {
|
||||
ifr, err := unix.NewIfreq(name)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
err = unix.IoctlIfreq(int(fd), unix.TUNGETIFF, ifr)
|
||||
if err != nil {
|
||||
return false, os.NewSyscallError("TUNGETIFF", err)
|
||||
}
|
||||
return ifr.Uint16()&unix.IFF_VNET_HDR != 0, nil
|
||||
}
|
||||
|
||||
func (t *NativeTun) Close() error {
|
||||
if t.interfaceCallback != nil {
|
||||
t.options.InterfaceMonitor.UnregisterCallback(t.interfaceCallback)
|
||||
@@ -332,10 +329,6 @@ func (t *NativeTun) Close() error {
|
||||
return E.Errors(t.unsetRoute(), t.unsetRules(), common.Close(common.PtrOrNil(t.tunFile)))
|
||||
}
|
||||
|
||||
func (t *NativeTun) TXChecksumOffload() bool {
|
||||
return t.txChecksumOffload
|
||||
}
|
||||
|
||||
func prefixToIPNet(prefix netip.Prefix) *net.IPNet {
|
||||
return &net.IPNet{
|
||||
IP: prefix.Addr().AsSlice(),
|
||||
@@ -450,6 +443,14 @@ func (t *NativeTun) rules() []*netlink.Rule {
|
||||
it.Family = unix.AF_INET
|
||||
rules = append(rules, it)
|
||||
priority++
|
||||
|
||||
it = netlink.NewRule()
|
||||
it.Priority = priority
|
||||
it.OifName = includeInterface
|
||||
it.Goto = matchPriority
|
||||
it.Family = unix.AF_INET
|
||||
rules = append(rules, it)
|
||||
priority++
|
||||
}
|
||||
if p6 {
|
||||
it = netlink.NewRule()
|
||||
@@ -459,6 +460,14 @@ func (t *NativeTun) rules() []*netlink.Rule {
|
||||
it.Family = unix.AF_INET6
|
||||
rules = append(rules, it)
|
||||
priority6++
|
||||
|
||||
it = netlink.NewRule()
|
||||
it.Priority = priority6
|
||||
it.OifName = includeInterface
|
||||
it.Goto = matchPriority
|
||||
it.Family = unix.AF_INET6
|
||||
rules = append(rules, it)
|
||||
priority6++
|
||||
}
|
||||
}
|
||||
if p4 {
|
||||
@@ -499,6 +508,14 @@ func (t *NativeTun) rules() []*netlink.Rule {
|
||||
it.Family = unix.AF_INET
|
||||
rules = append(rules, it)
|
||||
priority++
|
||||
|
||||
it = netlink.NewRule()
|
||||
it.Priority = priority
|
||||
it.OifName = excludeInterface
|
||||
it.Goto = nopPriority
|
||||
it.Family = unix.AF_INET
|
||||
rules = append(rules, it)
|
||||
priority++
|
||||
}
|
||||
if p6 {
|
||||
it = netlink.NewRule()
|
||||
@@ -508,6 +525,14 @@ func (t *NativeTun) rules() []*netlink.Rule {
|
||||
it.Family = unix.AF_INET6
|
||||
rules = append(rules, it)
|
||||
priority6++
|
||||
|
||||
it = netlink.NewRule()
|
||||
it.Priority = priority6
|
||||
it.OifName = excludeInterface
|
||||
it.Goto = nopPriority
|
||||
it.Family = unix.AF_INET6
|
||||
rules = append(rules, it)
|
||||
priority6++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
//go:build linux
|
||||
|
||||
package tun
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func checkVNETHDREnabled(fd int, name string) (bool, error) {
|
||||
ifr, err := unix.NewIfreq(name)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
err = unix.IoctlIfreq(fd, unix.TUNGETIFF, ifr)
|
||||
if err != nil {
|
||||
return false, os.NewSyscallError("TUNGETIFF", err)
|
||||
}
|
||||
return ifr.Uint16()&unix.IFF_VNET_HDR != 0, nil
|
||||
}
|
||||
|
||||
func setTCPOffload(fd int) error {
|
||||
const (
|
||||
// TODO: support TSO with ECN bits
|
||||
tunOffloads = unix.TUN_F_CSUM | unix.TUN_F_TSO4 | unix.TUN_F_TSO6
|
||||
)
|
||||
err := unix.IoctlSetInt(fd, unix.TUNSETOFFLOAD, tunOffloads)
|
||||
if err != nil {
|
||||
return E.Cause(os.NewSyscallError("TUNSETOFFLOAD", err), "enable offload")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ifreqData struct {
|
||||
ifrName [unix.IFNAMSIZ]byte
|
||||
ifrData uintptr
|
||||
}
|
||||
|
||||
type ethtoolValue struct {
|
||||
cmd uint32
|
||||
data uint32
|
||||
}
|
||||
|
||||
//go:linkname ioctlPtr golang.org/x/sys/unix.ioctlPtr
|
||||
func ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error)
|
||||
|
||||
func checkChecksumOffload(name string, cmd uint32) (bool, error) {
|
||||
fd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, unix.IPPROTO_IP)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer syscall.Close(fd)
|
||||
ifr := ifreqData{}
|
||||
copy(ifr.ifrName[:], name)
|
||||
data := ethtoolValue{cmd: cmd}
|
||||
ifr.ifrData = uintptr(unsafe.Pointer(&data))
|
||||
err = ioctlPtr(fd, unix.SIOCETHTOOL, unsafe.Pointer(&ifr))
|
||||
if err != nil {
|
||||
return false, os.NewSyscallError("SIOCETHTOOL ETHTOOL_GTXCSUM", err)
|
||||
}
|
||||
return data.data == 0, nil
|
||||
}
|
||||
|
||||
func setChecksumOffload(name string, cmd uint32) error {
|
||||
fd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, unix.IPPROTO_IP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer syscall.Close(fd)
|
||||
ifr := ifreqData{}
|
||||
copy(ifr.ifrName[:], name)
|
||||
data := ethtoolValue{cmd: cmd, data: 0}
|
||||
ifr.ifrData = uintptr(unsafe.Pointer(&data))
|
||||
err = ioctlPtr(fd, unix.SIOCETHTOOL, unsafe.Pointer(&ifr))
|
||||
if err != nil {
|
||||
return os.NewSyscallError("SIOCETHTOOL ETHTOOL_STXCSUM", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -14,15 +14,13 @@ func (t *NativeTun) NewEndpoint() (stack.LinkEndpoint, error) {
|
||||
return fdbased.New(&fdbased.Options{
|
||||
FDs: []int{t.tunFd},
|
||||
MTU: t.options.MTU,
|
||||
GSOMaxSize: gsoMaxSize,
|
||||
GSOMaxSize: t.options.GSOMaxSize,
|
||||
RXChecksumOffload: true,
|
||||
TXChecksumOffload: t.txChecksumOffload,
|
||||
})
|
||||
}
|
||||
return fdbased.New(&fdbased.Options{
|
||||
FDs: []int{t.tunFd},
|
||||
MTU: t.options.MTU,
|
||||
RXChecksumOffload: true,
|
||||
TXChecksumOffload: t.txChecksumOffload,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
gsoMaxSize = 65536
|
||||
tcpFlagsOffset = 13
|
||||
idealBatchSize = 128
|
||||
)
|
||||
@@ -750,12 +749,8 @@ func checksumNoFold(b []byte, initial uint64) uint64 {
|
||||
}
|
||||
|
||||
func checksumFold(b []byte, initial uint64) uint16 {
|
||||
ac := checksumNoFold(b, initial)
|
||||
ac = (ac >> 16) + (ac & 0xffff)
|
||||
ac = (ac >> 16) + (ac & 0xffff)
|
||||
ac = (ac >> 16) + (ac & 0xffff)
|
||||
ac = (ac >> 16) + (ac & 0xffff)
|
||||
return uint16(ac)
|
||||
r := clashtcpip.Checksum(uint32(initial), b)
|
||||
return binary.BigEndian.Uint16(r[:])
|
||||
}
|
||||
|
||||
func pseudoHeaderChecksumNoFold(protocol uint8, srcAddr, dstAddr []byte, totalLen uint16) uint64 {
|
||||
|
||||
@@ -65,6 +65,10 @@ func New(options Options) (WinTun, error) {
|
||||
return nativeTun, nil
|
||||
}
|
||||
|
||||
func (t *NativeTun) FrontHeadroom() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (t *NativeTun) configure() error {
|
||||
luid := winipcfg.LUID(t.adapter.LUID())
|
||||
if len(t.options.Inet4Address) > 0 {
|
||||
|
||||
Reference in New Issue
Block a user