Skip to content

Commit 052af4a

Browse files
jwhitedzx2c4
authored andcommitted
tun: use correct IP header comparisons in tcpGRO() and tcpPacketsCanCoalesce()
tcpGRO() was using an incorrect IPv4 more fragments bit mask. tcpPacketsCanCoalesce() was not distinguishing tcp6 from tcp4, and TTL values were not compared. TTL values should be equal at the IP layer, otherwise the packets should not coalesce. This tracks with the kernel. Reviewed-by: Denton Gentry <[email protected]> Signed-off-by: Jordan Whited <[email protected]> Signed-off-by: Jason A. Donenfeld <[email protected]>
1 parent aad7fca commit 052af4a

File tree

2 files changed

+119
-16
lines changed

2 files changed

+119
-16
lines changed

tun/tcp_offload_linux.go

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -189,14 +189,29 @@ func tcpPacketsCanCoalesce(pkt []byte, iphLen, tcphLen uint8, seq uint32, pshSet
189189
return coalesceUnavailable
190190
}
191191
}
192-
if pkt[1] != pktTarget[1] {
193-
// cannot coalesce with unequal ToS values
194-
return coalesceUnavailable
195-
}
196-
if pkt[6]>>5 != pktTarget[6]>>5 {
197-
// cannot coalesce with unequal DF or reserved bits. MF is checked
198-
// further up the stack.
199-
return coalesceUnavailable
192+
if pkt[0]>>4 == 6 {
193+
if pkt[0] != pktTarget[0] || pkt[1]>>4 != pktTarget[1]>>4 {
194+
// cannot coalesce with unequal Traffic class values
195+
return coalesceUnavailable
196+
}
197+
if pkt[7] != pktTarget[7] {
198+
// cannot coalesce with unequal Hop limit values
199+
return coalesceUnavailable
200+
}
201+
} else {
202+
if pkt[1] != pktTarget[1] {
203+
// cannot coalesce with unequal ToS values
204+
return coalesceUnavailable
205+
}
206+
if pkt[6]>>5 != pktTarget[6]>>5 {
207+
// cannot coalesce with unequal DF or reserved bits. MF is checked
208+
// further up the stack.
209+
return coalesceUnavailable
210+
}
211+
if pkt[8] != pktTarget[8] {
212+
// cannot coalesce with unequal TTL values
213+
return coalesceUnavailable
214+
}
200215
}
201216
// seq adjacency
202217
lhsLen := item.gsoSize
@@ -366,7 +381,7 @@ func coalesceTCPPackets(mode canCoalesce, pkt []byte, pktBuffsIndex int, gsoSize
366381
}
367382

368383
const (
369-
ipv4FlagMoreFragments = 0x80
384+
ipv4FlagMoreFragments uint8 = 0x20
370385
)
371386

372387
const (
@@ -409,7 +424,7 @@ func tcpGRO(bufs [][]byte, offset int, pktI int, table *tcpGROTable, isV6 bool)
409424
return false
410425
}
411426
if !isV6 {
412-
if pkt[6]&ipv4FlagMoreFragments != 0 || (pkt[6]<<3 != 0 || pkt[7] != 0) {
427+
if pkt[6]&ipv4FlagMoreFragments != 0 || pkt[6]<<3 != 0 || pkt[7] != 0 {
413428
// no GRO support for fragmented segments for now
414429
return false
415430
}

tun/tcp_offload_linux_test.go

Lines changed: 94 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,19 +28,23 @@ var (
2828
ip6PortC = netip.MustParseAddrPort("[2001:db8::3]:1")
2929
)
3030

31-
func tcp4Packet(srcIPPort, dstIPPort netip.AddrPort, flags header.TCPFlags, segmentSize, seq uint32) []byte {
31+
func tcp4PacketMutateIPFields(srcIPPort, dstIPPort netip.AddrPort, flags header.TCPFlags, segmentSize, seq uint32, ipFn func(*header.IPv4Fields)) []byte {
3232
totalLen := 40 + segmentSize
3333
b := make([]byte, offset+int(totalLen), 65535)
3434
ipv4H := header.IPv4(b[offset:])
3535
srcAs4 := srcIPPort.Addr().As4()
3636
dstAs4 := dstIPPort.Addr().As4()
37-
ipv4H.Encode(&header.IPv4Fields{
37+
ipFields := &header.IPv4Fields{
3838
SrcAddr: tcpip.Address(srcAs4[:]),
3939
DstAddr: tcpip.Address(dstAs4[:]),
4040
Protocol: unix.IPPROTO_TCP,
4141
TTL: 64,
4242
TotalLength: uint16(totalLen),
43-
})
43+
}
44+
if ipFn != nil {
45+
ipFn(ipFields)
46+
}
47+
ipv4H.Encode(ipFields)
4448
tcpH := header.TCP(b[offset+20:])
4549
tcpH.Encode(&header.TCPFields{
4650
SrcPort: srcIPPort.Port(),
@@ -57,19 +61,27 @@ func tcp4Packet(srcIPPort, dstIPPort netip.AddrPort, flags header.TCPFlags, segm
5761
return b
5862
}
5963

60-
func tcp6Packet(srcIPPort, dstIPPort netip.AddrPort, flags header.TCPFlags, segmentSize, seq uint32) []byte {
64+
func tcp4Packet(srcIPPort, dstIPPort netip.AddrPort, flags header.TCPFlags, segmentSize, seq uint32) []byte {
65+
return tcp4PacketMutateIPFields(srcIPPort, dstIPPort, flags, segmentSize, seq, nil)
66+
}
67+
68+
func tcp6PacketMutateIPFields(srcIPPort, dstIPPort netip.AddrPort, flags header.TCPFlags, segmentSize, seq uint32, ipFn func(*header.IPv6Fields)) []byte {
6169
totalLen := 60 + segmentSize
6270
b := make([]byte, offset+int(totalLen), 65535)
6371
ipv6H := header.IPv6(b[offset:])
6472
srcAs16 := srcIPPort.Addr().As16()
6573
dstAs16 := dstIPPort.Addr().As16()
66-
ipv6H.Encode(&header.IPv6Fields{
74+
ipFields := &header.IPv6Fields{
6775
SrcAddr: tcpip.Address(srcAs16[:]),
6876
DstAddr: tcpip.Address(dstAs16[:]),
6977
TransportProtocol: unix.IPPROTO_TCP,
7078
HopLimit: 64,
7179
PayloadLength: uint16(segmentSize + 20),
72-
})
80+
}
81+
if ipFn != nil {
82+
ipFn(ipFields)
83+
}
84+
ipv6H.Encode(ipFields)
7385
tcpH := header.TCP(b[offset+40:])
7486
tcpH.Encode(&header.TCPFields{
7587
SrcPort: srcIPPort.Port(),
@@ -85,6 +97,10 @@ func tcp6Packet(srcIPPort, dstIPPort netip.AddrPort, flags header.TCPFlags, segm
8597
return b
8698
}
8799

100+
func tcp6Packet(srcIPPort, dstIPPort netip.AddrPort, flags header.TCPFlags, segmentSize, seq uint32) []byte {
101+
return tcp6PacketMutateIPFields(srcIPPort, dstIPPort, flags, segmentSize, seq, nil)
102+
}
103+
88104
func Test_handleVirtioRead(t *testing.T) {
89105
tests := []struct {
90106
name string
@@ -245,6 +261,78 @@ func Test_handleGRO(t *testing.T) {
245261
[]int{340},
246262
false,
247263
},
264+
{
265+
"tcp4 unequal TTL",
266+
[][]byte{
267+
tcp4Packet(ip4PortA, ip4PortB, header.TCPFlagAck, 100, 1),
268+
tcp4PacketMutateIPFields(ip4PortA, ip4PortB, header.TCPFlagAck, 100, 101, func(fields *header.IPv4Fields) {
269+
fields.TTL++
270+
}),
271+
},
272+
[]int{0, 1},
273+
[]int{140, 140},
274+
false,
275+
},
276+
{
277+
"tcp4 unequal ToS",
278+
[][]byte{
279+
tcp4Packet(ip4PortA, ip4PortB, header.TCPFlagAck, 100, 1),
280+
tcp4PacketMutateIPFields(ip4PortA, ip4PortB, header.TCPFlagAck, 100, 101, func(fields *header.IPv4Fields) {
281+
fields.TOS++
282+
}),
283+
},
284+
[]int{0, 1},
285+
[]int{140, 140},
286+
false,
287+
},
288+
{
289+
"tcp4 unequal flags more fragments set",
290+
[][]byte{
291+
tcp4Packet(ip4PortA, ip4PortB, header.TCPFlagAck, 100, 1),
292+
tcp4PacketMutateIPFields(ip4PortA, ip4PortB, header.TCPFlagAck, 100, 101, func(fields *header.IPv4Fields) {
293+
fields.Flags = 1
294+
}),
295+
},
296+
[]int{0, 1},
297+
[]int{140, 140},
298+
false,
299+
},
300+
{
301+
"tcp4 unequal flags DF set",
302+
[][]byte{
303+
tcp4Packet(ip4PortA, ip4PortB, header.TCPFlagAck, 100, 1),
304+
tcp4PacketMutateIPFields(ip4PortA, ip4PortB, header.TCPFlagAck, 100, 101, func(fields *header.IPv4Fields) {
305+
fields.Flags = 2
306+
}),
307+
},
308+
[]int{0, 1},
309+
[]int{140, 140},
310+
false,
311+
},
312+
{
313+
"tcp6 unequal hop limit",
314+
[][]byte{
315+
tcp6Packet(ip6PortA, ip6PortB, header.TCPFlagAck, 100, 1),
316+
tcp6PacketMutateIPFields(ip6PortA, ip6PortB, header.TCPFlagAck, 100, 101, func(fields *header.IPv6Fields) {
317+
fields.HopLimit++
318+
}),
319+
},
320+
[]int{0, 1},
321+
[]int{160, 160},
322+
false,
323+
},
324+
{
325+
"tcp6 unequal traffic class",
326+
[][]byte{
327+
tcp6Packet(ip6PortA, ip6PortB, header.TCPFlagAck, 100, 1),
328+
tcp6PacketMutateIPFields(ip6PortA, ip6PortB, header.TCPFlagAck, 100, 101, func(fields *header.IPv6Fields) {
329+
fields.TrafficClass++
330+
}),
331+
},
332+
[]int{0, 1},
333+
[]int{160, 160},
334+
false,
335+
},
248336
}
249337

250338
for _, tt := range tests {

0 commit comments

Comments
 (0)