mirror of
https://github.com/slackhq/nebula.git
synced 2026-08-15 14:16:58 +02:00
another VIRTIO_NET_HDR_GSO_ECN mistake
This commit is contained in:
@@ -77,10 +77,6 @@ const tcpCwrFlag = 0x80
|
|||||||
// cause a downstream miscompute. The TUN should never emit RSC_INFO and
|
// cause a downstream miscompute. The TUN should never emit RSC_INFO and
|
||||||
// the GSO type must agree with the IP version nibble.
|
// the GSO type must agree with the IP version nibble.
|
||||||
func CheckValid(pkt []byte, hdr Hdr) error {
|
func CheckValid(pkt []byte, hdr Hdr) error {
|
||||||
// When RSC_INFO is set the csum_start/csum_offset fields are repurposed to
|
|
||||||
// carry coalescing info rather than checksum offsets. A TUN writing via
|
|
||||||
// IFF_VNET_HDR should never emit this, but if it did we would silently
|
|
||||||
// miscompute the segment checksums — refuse the packet instead.
|
|
||||||
if hdr.Flags&unix.VIRTIO_NET_HDR_F_RSC_INFO != 0 {
|
if hdr.Flags&unix.VIRTIO_NET_HDR_F_RSC_INFO != 0 {
|
||||||
return fmt.Errorf("virtio RSC_INFO flag not supported on TUN reads")
|
return fmt.Errorf("virtio RSC_INFO flag not supported on TUN reads")
|
||||||
}
|
}
|
||||||
@@ -88,7 +84,10 @@ func CheckValid(pkt []byte, hdr Hdr) error {
|
|||||||
return fmt.Errorf("packet too short")
|
return fmt.Errorf("packet too short")
|
||||||
}
|
}
|
||||||
ipVersion := pkt[0] >> 4
|
ipVersion := pkt[0] >> 4
|
||||||
switch hdr.GSOType {
|
|
||||||
|
//mask out VIRTIO_NET_HDR_GSO_ECN, it's a qualifier, not a type
|
||||||
|
gsoType := hdr.GSOType &^ unix.VIRTIO_NET_HDR_GSO_ECN
|
||||||
|
switch gsoType {
|
||||||
case unix.VIRTIO_NET_HDR_GSO_TCPV4:
|
case unix.VIRTIO_NET_HDR_GSO_TCPV4:
|
||||||
if ipVersion != 4 {
|
if ipVersion != 4 {
|
||||||
return fmt.Errorf("invalid IP version %d for GSO type %d", ipVersion, hdr.GSOType)
|
return fmt.Errorf("invalid IP version %d for GSO type %d", ipVersion, hdr.GSOType)
|
||||||
|
|||||||
@@ -430,3 +430,40 @@ func TestFinishChecksumUDPValidates(t *testing.T) {
|
|||||||
t.Fatal("completed UDP checksum does not validate")
|
t.Fatal("completed UDP checksum does not validate")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestCheckValidMasksGSOECN: GSO_ECN is a qualifier bit the kernel ORs
|
||||||
|
// into gso_type for TSO superpackets with CWR set. CheckValid must
|
||||||
|
// validate an ECN-qualified type as its base type — previously TCPV4|ECN
|
||||||
|
// fell into the default case and skipped the IP-version agreement check.
|
||||||
|
// The qualifier is TCP-only, so it must be rejected on UDP_L4.
|
||||||
|
func TestCheckValidMasksGSOECN(t *testing.T) {
|
||||||
|
v4pkt, _, _ := buildTCPv4Super(100)
|
||||||
|
v6pkt := make([]byte, len(v4pkt))
|
||||||
|
copy(v6pkt, v4pkt)
|
||||||
|
v6pkt[0] = 0x60 // claim IPv6
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
pkt []byte
|
||||||
|
gsoType uint8
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{"tcpv4-ecn-v4", v4pkt, unix.VIRTIO_NET_HDR_GSO_TCPV4 | unix.VIRTIO_NET_HDR_GSO_ECN, false},
|
||||||
|
{"tcpv4-ecn-v6-mismatch", v6pkt, unix.VIRTIO_NET_HDR_GSO_TCPV4 | unix.VIRTIO_NET_HDR_GSO_ECN, true},
|
||||||
|
{"tcpv6-ecn-v4-mismatch", v4pkt, unix.VIRTIO_NET_HDR_GSO_TCPV6 | unix.VIRTIO_NET_HDR_GSO_ECN, true},
|
||||||
|
{"udp-l4-ecn-rejected", v4pkt, unix.VIRTIO_NET_HDR_GSO_UDP_L4 | unix.VIRTIO_NET_HDR_GSO_ECN, true},
|
||||||
|
{"tcpv4-plain-v4", v4pkt, unix.VIRTIO_NET_HDR_GSO_TCPV4, false},
|
||||||
|
{"tcpv4-plain-v6-mismatch", v6pkt, unix.VIRTIO_NET_HDR_GSO_TCPV4, true},
|
||||||
|
}
|
||||||
|
for _, tc := range cases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
err := CheckValid(tc.pkt, Hdr{GSOType: tc.gsoType})
|
||||||
|
if tc.wantErr && err == nil {
|
||||||
|
t.Errorf("CheckValid(gsoType=%#x) = nil, want error", tc.gsoType)
|
||||||
|
}
|
||||||
|
if !tc.wantErr && err != nil {
|
||||||
|
t.Errorf("CheckValid(gsoType=%#x) = %v, want nil", tc.gsoType, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user