Skip to content

Commit 1cd52bf

Browse files
committed
Fix GSO support
1 parent 6122891 commit 1cd52bf

6 files changed

Lines changed: 157 additions & 66 deletions

File tree

stack.go

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,6 @@ type StackOptions struct {
2828
InterfaceFinder control.InterfaceFinder
2929
}
3030

31-
func (o *StackOptions) BufferSize() uint32 {
32-
if o.TunOptions.GSO {
33-
return o.TunOptions.GSOMaxSize
34-
} else {
35-
return o.TunOptions.MTU
36-
}
37-
}
38-
3931
func NewStack(
4032
stack string,
4133
options StackOptions,

stack_mixed.go

Lines changed: 56 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ func (m *Mixed) Start() error {
4848
if err != nil {
4949
return err
5050
}
51-
endpoint := channel.New(1024, m.mtu, "")
51+
endpoint := channel.New(1024, uint32(m.mtu), "")
5252
ipStack, err := newGVisorStack(endpoint)
5353
if err != nil {
5454
return err
@@ -95,8 +95,16 @@ func (m *Mixed) tunLoop() {
9595
m.wintunLoop(winTun)
9696
return
9797
}
98+
99+
if batchTUN, isBatchTUN := m.tun.(BatchTUN); isBatchTUN {
100+
batchSize := batchTUN.BatchSize()
101+
if batchSize > 1 {
102+
m.batchLoop(batchTUN, batchSize)
103+
return
104+
}
105+
}
98106
frontHeadroom := m.tun.FrontHeadroom()
99-
packetBuffer := make([]byte, m.bufferSize+frontHeadroom+PacketOffset)
107+
packetBuffer := make([]byte, m.mtu+frontHeadroom+PacketOffset)
100108
for {
101109
n, err := m.tun.Read(packetBuffer[frontHeadroom:])
102110
if err != nil {
@@ -110,17 +118,7 @@ func (m *Mixed) tunLoop() {
110118
}
111119
rawPacket := packetBuffer[:frontHeadroom+n]
112120
packet := packetBuffer[frontHeadroom+PacketOffset : frontHeadroom+n]
113-
switch ipVersion := packet[0] >> 4; ipVersion {
114-
case 4:
115-
err = m.processIPv4(rawPacket, packet)
116-
case 6:
117-
err = m.processIPv6(rawPacket, packet)
118-
default:
119-
err = E.New("ip: unknown version: ", ipVersion)
120-
}
121-
if err != nil {
122-
m.logger.Trace(err)
123-
}
121+
m.processPacket(rawPacket, packet)
124122
}
125123
}
126124

@@ -134,18 +132,53 @@ func (m *Mixed) wintunLoop(winTun WinTun) {
134132
release()
135133
continue
136134
}
137-
switch ipVersion := packet[0] >> 4; ipVersion {
138-
case 4:
139-
err = m.processIPv4(packet, packet)
140-
case 6:
141-
err = m.processIPv6(packet, packet)
142-
default:
143-
err = E.New("ip: unknown version: ", ipVersion)
144-
}
135+
m.processPacket(packet, packet)
136+
release()
137+
}
138+
}
139+
140+
func (m *Mixed) batchLoop(linuxTUN BatchTUN, batchSize int) {
141+
frontHeadroom := m.tun.FrontHeadroom()
142+
packetBuffers := make([][]byte, batchSize)
143+
for i := range packetBuffers {
144+
packetBuffers[i] = make([]byte, m.mtu+frontHeadroom+PacketOffset)
145+
}
146+
packetSizes := make([]int, batchSize)
147+
for {
148+
n, err := linuxTUN.BatchRead(packetBuffers, packetSizes)
145149
if err != nil {
146-
m.logger.Trace(err)
150+
if E.IsClosed(err) {
151+
return
152+
}
153+
m.logger.Error(E.Cause(err, "batch read packet"))
147154
}
148-
release()
155+
if n == 0 {
156+
continue
157+
}
158+
for i := 0; i < n; i++ {
159+
packetBuffer := packetBuffers[i][:packetSizes[i]]
160+
if n < clashtcpip.IPv4PacketMinLength {
161+
continue
162+
}
163+
rawPacket := packetBuffer[:frontHeadroom+n]
164+
packet := packetBuffer[frontHeadroom+PacketOffset : frontHeadroom+n]
165+
m.processPacket(rawPacket, packet)
166+
}
167+
}
168+
}
169+
170+
func (m *Mixed) processPacket(rawPacket []byte, packet []byte) {
171+
var err error
172+
switch ipVersion := packet[0] >> 4; ipVersion {
173+
case 4:
174+
err = m.processIPv4(rawPacket, packet)
175+
case 6:
176+
err = m.processIPv6(rawPacket, packet)
177+
default:
178+
err = E.New("ip: unknown version: ", ipVersion)
179+
}
180+
if err != nil {
181+
m.logger.Trace(err)
149182
}
150183
}
151184

stack_system.go

Lines changed: 70 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ type System struct {
2222
ctx context.Context
2323
tun Tun
2424
tunName string
25-
mtu uint32
26-
bufferSize int
25+
mtu int
2726
handler Handler
2827
logger logger.Logger
2928
inet4Prefixes []netip.Prefix
@@ -57,8 +56,7 @@ func NewSystem(options StackOptions) (Stack, error) {
5756
ctx: options.Context,
5857
tun: options.Tun,
5958
tunName: options.TunOptions.Name,
60-
mtu: options.TunOptions.MTU,
61-
bufferSize: int(options.BufferSize()),
59+
mtu: int(options.TunOptions.MTU),
6260
udpTimeout: options.UDPTimeout,
6361
handler: options.Handler,
6462
logger: options.Logger,
@@ -147,8 +145,15 @@ func (s *System) tunLoop() {
147145
s.wintunLoop(winTun)
148146
return
149147
}
148+
if batchTUN, isBatchTUN := s.tun.(BatchTUN); isBatchTUN {
149+
batchSize := batchTUN.BatchSize()
150+
if batchSize > 1 {
151+
s.batchLoop(batchTUN, batchSize)
152+
return
153+
}
154+
}
150155
frontHeadroom := s.tun.FrontHeadroom()
151-
packetBuffer := make([]byte, s.bufferSize+frontHeadroom+PacketOffset)
156+
packetBuffer := make([]byte, s.mtu+frontHeadroom+PacketOffset)
152157
for {
153158
n, err := s.tun.Read(packetBuffer[frontHeadroom:])
154159
if err != nil {
@@ -162,17 +167,7 @@ func (s *System) tunLoop() {
162167
}
163168
rawPacket := packetBuffer[:frontHeadroom+n]
164169
packet := packetBuffer[frontHeadroom+PacketOffset : frontHeadroom+n]
165-
switch ipVersion := packet[0] >> 4; ipVersion {
166-
case 4:
167-
err = s.processIPv4(rawPacket, packet)
168-
case 6:
169-
err = s.processIPv6(rawPacket, packet)
170-
default:
171-
err = E.New("ip: unknown version: ", ipVersion)
172-
}
173-
if err != nil {
174-
s.logger.Trace(err)
175-
}
170+
s.processPacket(rawPacket, packet)
176171
}
177172
}
178173

@@ -186,18 +181,53 @@ func (s *System) wintunLoop(winTun WinTun) {
186181
release()
187182
continue
188183
}
189-
switch ipVersion := packet[0] >> 4; ipVersion {
190-
case 4:
191-
err = s.processIPv4(packet, packet)
192-
case 6:
193-
err = s.processIPv6(packet, packet)
194-
default:
195-
err = E.New("ip: unknown version: ", ipVersion)
196-
}
184+
s.processPacket(packet, packet)
185+
release()
186+
}
187+
}
188+
189+
func (s *System) batchLoop(linuxTUN BatchTUN, batchSize int) {
190+
frontHeadroom := s.tun.FrontHeadroom()
191+
packetBuffers := make([][]byte, batchSize)
192+
for i := range packetBuffers {
193+
packetBuffers[i] = make([]byte, s.mtu+frontHeadroom+PacketOffset)
194+
}
195+
packetSizes := make([]int, batchSize)
196+
for {
197+
n, err := linuxTUN.BatchRead(packetBuffers, packetSizes)
197198
if err != nil {
198-
s.logger.Trace(err)
199+
if E.IsClosed(err) {
200+
return
201+
}
202+
s.logger.Error(E.Cause(err, "batch read packet"))
199203
}
200-
release()
204+
if n == 0 {
205+
continue
206+
}
207+
for i := 0; i < n; i++ {
208+
packetBuffer := packetBuffers[i][:packetSizes[i]]
209+
if n < clashtcpip.IPv4PacketMinLength {
210+
continue
211+
}
212+
rawPacket := packetBuffer[:frontHeadroom+n]
213+
packet := packetBuffer[frontHeadroom+PacketOffset : frontHeadroom+n]
214+
s.processPacket(rawPacket, packet)
215+
}
216+
}
217+
}
218+
219+
func (s *System) processPacket(rawPacket []byte, packet []byte) {
220+
var err error
221+
switch ipVersion := packet[0] >> 4; ipVersion {
222+
case 4:
223+
err = s.processIPv4(rawPacket, packet)
224+
case 6:
225+
err = s.processIPv6(rawPacket, packet)
226+
default:
227+
err = E.New("ip: unknown version: ", ipVersion)
228+
}
229+
if err != nil {
230+
s.logger.Trace(err)
201231
}
202232
}
203233

@@ -354,7 +384,7 @@ func (s *System) processIPv4UDP(rawPacket []byte, packet clashtcpip.IPv4Packet,
354384
headerLen := packet.HeaderLen() + clashtcpip.UDPHeaderSize
355385
headerCopy := make([]byte, headerLen)
356386
copy(headerCopy, packet[:headerLen])
357-
return &systemUDPPacketWriter4{s.tun, s.tun.FrontHeadroom(), headerCopy, source}
387+
return &systemUDPPacketWriter4{s.tun, s.tun.FrontHeadroom() + PacketOffset, headerCopy, source}
358388
})
359389
return nil
360390
}
@@ -380,7 +410,7 @@ func (s *System) processIPv6UDP(rawPacket []byte, packet clashtcpip.IPv6Packet,
380410
headerLen := len(packet) - int(header.Length()) + clashtcpip.UDPHeaderSize
381411
headerCopy := make([]byte, headerLen)
382412
copy(headerCopy, packet[:headerLen])
383-
return &systemUDPPacketWriter6{s.tun, s.tun.FrontHeadroom(), headerCopy, source}
413+
return &systemUDPPacketWriter6{s.tun, s.tun.FrontHeadroom() + PacketOffset, headerCopy, source}
384414
})
385415
return nil
386416
}
@@ -421,8 +451,7 @@ type systemUDPPacketWriter4 struct {
421451
func (w *systemUDPPacketWriter4) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
422452
newPacket := buf.NewSize(w.frontHeadroom + len(w.header) + buffer.Len())
423453
defer newPacket.Release()
424-
newPacket.WriteZeroN(w.frontHeadroom)
425-
newPacket.Advance(w.frontHeadroom)
454+
newPacket.Resize(w.frontHeadroom, 0)
426455
newPacket.Write(w.header)
427456
newPacket.Write(buffer.Bytes())
428457
ipHdr := clashtcpip.IPv4Packet(newPacket.Bytes())
@@ -435,7 +464,11 @@ func (w *systemUDPPacketWriter4) WritePacket(buffer *buf.Buffer, destination M.S
435464
udpHdr.SetLength(uint16(buffer.Len() + clashtcpip.UDPHeaderSize))
436465
udpHdr.ResetChecksum(ipHdr.PseudoSum())
437466
ipHdr.ResetChecksum()
438-
newPacket.Advance(-w.frontHeadroom)
467+
if PacketOffset > 0 {
468+
newPacket.ExtendHeader(PacketOffset)[3] = syscall.AF_INET
469+
} else {
470+
newPacket.Advance(-w.frontHeadroom)
471+
}
439472
return common.Error(w.tun.Write(newPacket.Bytes()))
440473
}
441474

@@ -449,8 +482,7 @@ type systemUDPPacketWriter6 struct {
449482
func (w *systemUDPPacketWriter6) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
450483
newPacket := buf.NewSize(w.frontHeadroom + len(w.header) + buffer.Len())
451484
defer newPacket.Release()
452-
newPacket.WriteZeroN(w.frontHeadroom)
453-
newPacket.Advance(w.frontHeadroom)
485+
newPacket.Resize(w.frontHeadroom, 0)
454486
newPacket.Write(w.header)
455487
newPacket.Write(buffer.Bytes())
456488
ipHdr := clashtcpip.IPv6Packet(newPacket.Bytes())
@@ -463,6 +495,10 @@ func (w *systemUDPPacketWriter6) WritePacket(buffer *buf.Buffer, destination M.S
463495
udpHdr.SetSourcePort(destination.Port)
464496
udpHdr.SetLength(udpLen)
465497
udpHdr.ResetChecksum(ipHdr.PseudoSum())
466-
newPacket.Advance(-w.frontHeadroom)
498+
if PacketOffset > 0 {
499+
newPacket.ExtendHeader(PacketOffset)[3] = syscall.AF_INET6
500+
} else {
501+
newPacket.Advance(-w.frontHeadroom)
502+
}
467503
return common.Error(w.tun.Write(newPacket.Bytes()))
468504
}

tun.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ type WinTun interface {
3232
ReadPacket() ([]byte, func(), error)
3333
}
3434

35+
type BatchTUN interface {
36+
BatchSize() int
37+
BatchRead(buffers [][]byte, readN []int) (n int, err error)
38+
}
39+
3540
type Options struct {
3641
Name string
3742
Inet4Address []netip.Prefix

tun_linux.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import (
2121
"golang.org/x/sys/unix"
2222
)
2323

24+
var _ BatchTUN = (*NativeTun)(nil)
25+
2426
type NativeTun struct {
2527
tunFd int
2628
tunFile *os.File
@@ -119,6 +121,29 @@ func (t *NativeTun) Write(p []byte) (n int, err error) {
119121
return t.tunFile.Write(p)
120122
}
121123

124+
func (t *NativeTun) BatchSize() int {
125+
if !t.gsoEnabled {
126+
return 1
127+
}
128+
return idealBatchSize
129+
}
130+
131+
func (t *NativeTun) BatchRead(buffers [][]byte, readN []int) (n int, err error) {
132+
if t.gsoEnabled {
133+
n, err = t.tunFile.Read(t.gsoBuffer)
134+
if err != nil {
135+
return
136+
}
137+
n, err = handleVirtioRead(t.gsoBuffer[:n], buffers, readN, 0)
138+
if err != nil {
139+
return
140+
}
141+
return
142+
} else {
143+
return 0, os.ErrInvalid
144+
}
145+
}
146+
122147
var controlPath string
123148

124149
func init() {

tun_linux_offload.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import (
2323

2424
const (
2525
tcpFlagsOffset = 13
26-
idealBatchSize = 1
26+
idealBatchSize = 128
2727
)
2828

2929
const (

0 commit comments

Comments
 (0)