Skip to content

Commit 6c3a223

Browse files
committed
tunnel: write inbound packets to TUN in batches
this reduces syscalls count, but only for Linux
1 parent 0f7ef46 commit 6c3a223

2 files changed

Lines changed: 88 additions & 12 deletions

File tree

service/tunnel.go

Lines changed: 53 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -351,24 +351,66 @@ func (vp *VpnPeer) backgroundOutboundHandler(t *Tunnel) {
351351
}
352352

353353
func (vp *VpnPeer) backgroundInboundHandler(t *Tunnel) {
354+
batchSize := t.device.BatchSize()
355+
bytesBufs := make([][]byte, 0, batchSize)
356+
packetsBufs := make([]*vpn.Packet, batchSize)
357+
354358
for {
355-
packet, open := <-vp.inboundCh
359+
firstPacket, open := <-vp.inboundCh
356360
if !open {
357361
return
358362
}
359363
localIP := *vp.localIP.Load()
360-
ok := packet.Parse()
361-
if !ok {
362-
t.logger.Warnf("got invalid packet from peerID (%s) local ip (%s)", vp.peerID, localIP)
363-
t.device.PutTempPacket(packet)
364-
continue
364+
365+
packetsBufs[0] = firstPacket
366+
packetsBatch := readBatchFromChan(vp.inboundCh, packetsBufs, 1)
367+
368+
newLen := 0
369+
for i, packet := range packetsBatch {
370+
ok := packet.Parse()
371+
if !ok {
372+
t.logger.Warnf("got invalid packet from peerID (%s) local ip (%s)", vp.peerID, localIP)
373+
t.device.PutTempPacket(packet)
374+
packetsBatch[i] = nil
375+
continue
376+
}
377+
packetsBatch[newLen] = packet
378+
newLen++
365379
}
366-
// TODO: add batching
367-
err := t.device.WritePacket(packet, localIP)
368-
if err != nil {
369-
t.logger.Warnf("write packet to vpn: %v", err)
380+
filteredPackets := packetsBatch[:newLen]
381+
382+
if len(filteredPackets) > 0 {
383+
err := t.device.WritePacketsBatch(filteredPackets, bytesBufs, localIP)
384+
if err != nil {
385+
t.logger.Warnf("write packets batch to vpn for local ip %s: %v", localIP, err)
386+
}
370387
}
371388

372-
t.device.PutTempPacket(packet)
389+
for i, packet := range packetsBatch {
390+
if packet == nil {
391+
continue
392+
}
393+
t.device.PutTempPacket(packet)
394+
packetsBatch[i] = nil
395+
}
396+
}
397+
}
398+
399+
func readBatchFromChan(ch chan *vpn.Packet, buf []*vpn.Packet, offset int) []*vpn.Packet {
400+
i := offset
401+
for {
402+
if i == len(buf) {
403+
return buf[:i]
404+
}
405+
select {
406+
case packet, ok := <-ch:
407+
if !ok {
408+
return buf[:i]
409+
}
410+
buf[i] = packet
411+
i++
412+
default:
413+
return buf[:i]
414+
}
373415
}
374416
}

vpn/vpn.go

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ func (d *Device) PutTempPacket(data *Packet) {
7070
d.packetsPool.Put(data)
7171
}
7272

73-
// TODO: batch write
7473
func (d *Device) WritePacket(data *Packet, senderIP net.IP) error {
7574
if data.IsIPv6 {
7675
// TODO: implement. We need to set Device.localIP ipv6 instead of ipv4
@@ -92,6 +91,41 @@ func (d *Device) WritePacket(data *Packet, senderIP net.IP) error {
9291
return nil
9392
}
9493

94+
// bufs should be len(bufs) == 0, cap(bufs) == len(packets)
95+
func (d *Device) WritePacketsBatch(packets []*Packet, bufs [][]byte, senderIP net.IP) error {
96+
for _, packet := range packets {
97+
if packet.IsIPv6 {
98+
// TODO: implement. We need to set Device.localIP ipv6 instead of ipv4
99+
continue
100+
} else {
101+
copy(packet.Src, senderIP)
102+
copy(packet.Dst, d.localIP)
103+
}
104+
packet.RecalculateChecksum()
105+
106+
bufs = append(bufs, packet.Buffer[:tunPacketOffset+len(packet.Packet)])
107+
}
108+
109+
defer func() {
110+
for i := range bufs {
111+
bufs[i] = nil
112+
}
113+
}()
114+
115+
packetsCount, err := d.tun.Write(bufs, tunPacketOffset)
116+
if err != nil {
117+
return fmt.Errorf("write packet to tun: %v", err)
118+
} else if packetsCount < len(bufs) {
119+
d.logger.Warnf("wrote %d packets, but expected %d", packetsCount, len(bufs))
120+
}
121+
122+
return nil
123+
}
124+
125+
func (d *Device) BatchSize() int {
126+
return d.tun.BatchSize()
127+
}
128+
95129
func (d *Device) Close() error {
96130
return d.tun.Close()
97131
}

0 commit comments

Comments
 (0)