tun: disqualify tcp4 packets w/IP options from coalescing

IP options were not being compared prior to coalescing. They are not
commonly used. Disqualification due to nonzero options is in line with
the kernel.

Reviewed-by: Denton Gentry <dgentry@tailscale.com>
Signed-off-by: Jordan Whited <jordan@tailscale.com>
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
Jordan Whited 2023-03-24 15:09:47 -07:00 committed by Jason A. Donenfeld
parent 6f895be10d
commit aad7fca9c5
2 changed files with 55 additions and 5 deletions

View File

@ -397,9 +397,6 @@ func tcpGRO(bufs [][]byte, offset int, pktI int, table *tcpGROTable, isV6 bool)
if totalLen != len(pkt) { if totalLen != len(pkt) {
return false return false
} }
if iphLen < 20 || iphLen > 60 {
return false
}
} }
if len(pkt) < iphLen { if len(pkt) < iphLen {
return false return false
@ -474,13 +471,16 @@ func tcpGRO(bufs [][]byte, offset int, pktI int, table *tcpGROTable, isV6 bool)
return false return false
} }
func isTCP4(b []byte) bool { func isTCP4NoIPOptions(b []byte) bool {
if len(b) < 40 { if len(b) < 40 {
return false return false
} }
if b[0]>>4 != 4 { if b[0]>>4 != 4 {
return false return false
} }
if b[0]&0x0F != 5 {
return false
}
if b[9] != unix.IPPROTO_TCP { if b[9] != unix.IPPROTO_TCP {
return false return false
} }
@ -511,7 +511,7 @@ func handleGRO(bufs [][]byte, offset int, tcp4Table, tcp6Table *tcpGROTable, toW
} }
var coalesced bool var coalesced bool
switch { switch {
case isTCP4(bufs[i][offset:]): case isTCP4NoIPOptions(bufs[i][offset:]): // ipv4 packets w/IP options do not coalesce
coalesced = tcpGRO(bufs, offset, i, tcp4Table, false) coalesced = tcpGRO(bufs, offset, i, tcp4Table, false)
case isTCP6NoEH(bufs[i][offset:]): // ipv6 packets w/extension headers do not coalesce case isTCP6NoEH(bufs[i][offset:]): // ipv6 packets w/extension headers do not coalesce
coalesced = tcpGRO(bufs, offset, i, tcp6Table, true) coalesced = tcpGRO(bufs, offset, i, tcp6Table, true)

View File

@ -271,3 +271,53 @@ func Test_handleGRO(t *testing.T) {
}) })
} }
} }
func Test_isTCP4NoIPOptions(t *testing.T) {
valid := tcp4Packet(ip4PortA, ip4PortB, header.TCPFlagAck, 100, 1)[virtioNetHdrLen:]
invalidLen := valid[:39]
invalidHeaderLen := make([]byte, len(valid))
copy(invalidHeaderLen, valid)
invalidHeaderLen[0] = 0x46
invalidProtocol := make([]byte, len(valid))
copy(invalidProtocol, valid)
invalidProtocol[9] = unix.IPPROTO_TCP + 1
tests := []struct {
name string
b []byte
want bool
}{
{
"valid",
valid,
true,
},
{
"invalid length",
invalidLen,
false,
},
{
"invalid version",
[]byte{0x00},
false,
},
{
"invalid header len",
invalidHeaderLen,
false,
},
{
"invalid protocol",
invalidProtocol,
false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := isTCP4NoIPOptions(tt.b); got != tt.want {
t.Errorf("isTCP4NoIPOptions() = %v, want %v", got, tt.want)
}
})
}
}