Skb with skb->protocol 0 at the time of virtio_net_hdr_to_skb may have a protocol inferred from virtio_net_hdr with virtio_net_hdr_set_proto. Unlike TCP, UDP does not have separate types for IPv4 and IPv6. Type VIRTIO_NET_HDR_GSO_UDP is guessed to be IPv4/UDP. As of the below commit, UFOv6 packets are dropped due to not matching the protocol as obtained from dev_parse_header_protocol. Invert the test to take that L2 protocol field as starting point and pass both UFOv4 and UFOv6 for VIRTIO_NET_HDR_GSO_UDP.
Unified · Split
| @@ -7,6 +7,21 @@ · 6 unmodified lines | ||
| 7 | 7 | #include <uapi/linux/udp.h> |
| 8 | 8 | #include <uapi/linux/virtio_net.h> |
| 9 | 9 | |
| 10 | +static inline bool virtio_net_hdr_match_proto(__be16 protocol, __u8 gso_type) | |
| 11 | +{ | |
| 12 | + switch (gso_type & ~VIRTIO_NET_HDR_GSO_ECN) { | |
| 13 | + case VIRTIO_NET_HDR_GSO_TCPV4: | |
| 14 | + return protocol == cpu_to_be16(ETH_P_IP); | |
| 15 | + case VIRTIO_NET_HDR_GSO_TCPV6: | |
| 16 | + return protocol == cpu_to_be16(ETH_P_IPV6); | |
| 17 | + case VIRTIO_NET_HDR_GSO_UDP: | |
| 18 | + return protocol == cpu_to_be16(ETH_P_IP) || | |
| 19 | + protocol == cpu_to_be16(ETH_P_IPV6); | |
| 20 | + default: | |
| 21 | + return false; | |
| 22 | + } | |
| 23 | +} | |
| 24 | + | |
| 10 | 25 | static inline int virtio_net_hdr_set_proto(struct sk_buff *skb, |
| 11 | 26 | const struct virtio_net_hdr *hdr) |
| 12 | 27 | { |
| @@ -88,9 +103,12 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb, · 75 unmodified lines | ||
| 88 | 103 | if (!skb->protocol) { |
| 89 | 104 | __be16 protocol = dev_parse_header_protocol(skb); |
| 90 | 105 | |
| 91 | − virtio_net_hdr_set_proto(skb, hdr); | |
| 92 | − if (protocol && protocol != skb->protocol) | |
| 106 | + if (!protocol) | |
| 107 | + virtio_net_hdr_set_proto(skb, hdr); | |
| 108 | + else if (!virtio_net_hdr_match_proto(protocol, hdr->gso_type)) | |
| 93 | 109 | return -EINVAL; |
| 110 | + else | |
| 111 | + skb->protocol = protocol; | |
| 94 | 112 | } |
| 95 | 113 | retry: |
| 96 | 114 | if (!skb_flow_dissect_flow_keys_basic(NULL, skb, &keys, |