]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
net: in virtio_net_hdr only add VLAN_HLEN to csum_start if payload holds vlan
authorWillem de Bruijn <willemb@google.com>
Wed, 6 Jun 2018 15:23:01 +0000 (11:23 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 25 Jun 2018 23:53:55 +0000 (07:53 +0800)
[ Upstream commit fd3a88625844907151737fc3b4201676effa6d27 ]

Tun, tap, virtio, packet and uml vector all use struct virtio_net_hdr
to communicate packet metadata to userspace.

For skbuffs with vlan, the first two return the packet as it may have
existed on the wire, inserting the VLAN tag in the user buffer.  Then
virtio_net_hdr.csum_start needs to be adjusted by VLAN_HLEN bytes.

Commit f09e2249c4f5 ("macvtap: restore vlan header on user read")
added this feature to macvtap. Commit 3ce9b20f1971 ("macvtap: Fix
csum_start when VLAN tags are present") then fixed up csum_start.

Virtio, packet and uml do not insert the vlan header in the user
buffer.

When introducing virtio_net_hdr_from_skb to deduplicate filling in
the virtio_net_hdr, the variant from macvtap which adds VLAN_HLEN was
applied uniformly, breaking csum offset for packets with vlan on
virtio and packet.

Make insertion of VLAN_HLEN optional. Convert the callers to pass it
when needed.

Fixes: e858fae2b0b8f4 ("virtio_net: use common code for virtio_net_hdr and skb GSO conversion")
Fixes: 1276f24eeef2 ("packet: use common code for virtio_net_hdr and skb GSO conversion")
Signed-off-by: Willem de Bruijn <willemb@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/net/tap.c
drivers/net/tun.c
drivers/net/virtio_net.c
include/linux/virtio_net.h
net/packet/af_packet.c

index 9b6cb780affec603f8200d258988785c5edf55a0..f0f7cd9776671fb6d3f3dd6498611afe593b41c3 100644 (file)
@@ -774,13 +774,16 @@ static ssize_t tap_put_user(struct tap_queue *q,
        int total;
 
        if (q->flags & IFF_VNET_HDR) {
+               int vlan_hlen = skb_vlan_tag_present(skb) ? VLAN_HLEN : 0;
                struct virtio_net_hdr vnet_hdr;
+
                vnet_hdr_len = READ_ONCE(q->vnet_hdr_sz);
                if (iov_iter_count(iter) < vnet_hdr_len)
                        return -EINVAL;
 
                if (virtio_net_hdr_from_skb(skb, &vnet_hdr,
-                                           tap_is_little_endian(q), true))
+                                           tap_is_little_endian(q), true,
+                                           vlan_hlen))
                        BUG();
 
                if (copy_to_iter(&vnet_hdr, sizeof(vnet_hdr), iter) !=
index 24e645c86ae73ed78371810cb487a3a5273f608b..b3c58890ef3311214f57ed6dcd911069a51d20a8 100644 (file)
@@ -2062,7 +2062,8 @@ static ssize_t tun_put_user(struct tun_struct *tun,
                        return -EINVAL;
 
                if (virtio_net_hdr_from_skb(skb, &gso,
-                                           tun_is_little_endian(tun), true)) {
+                                           tun_is_little_endian(tun), true,
+                                           vlan_hlen)) {
                        struct skb_shared_info *sinfo = skb_shinfo(skb);
                        pr_err("unexpected GSO type: "
                               "0x%x, gso_size %d, hdr_len %d\n",
index 8911e3466e617b02662f5ab4ce4030512a6b90a8..89bc5cd4d02ff799f9f7196d9d7a2c2a05f800e9 100644 (file)
@@ -1358,7 +1358,8 @@ static int xmit_skb(struct send_queue *sq, struct sk_buff *skb)
                hdr = skb_vnet_hdr(skb);
 
        if (virtio_net_hdr_from_skb(skb, &hdr->hdr,
-                                   virtio_is_little_endian(vi->vdev), false))
+                                   virtio_is_little_endian(vi->vdev), false,
+                                   0))
                BUG();
 
        if (vi->mergeable_rx_bufs)
index f144216febc642fd70512df9dddefe1a7f119478..9397628a196714dc2177552465fe91fd18b9627d 100644 (file)
@@ -58,7 +58,8 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb,
 static inline int virtio_net_hdr_from_skb(const struct sk_buff *skb,
                                          struct virtio_net_hdr *hdr,
                                          bool little_endian,
-                                         bool has_data_valid)
+                                         bool has_data_valid,
+                                         int vlan_hlen)
 {
        memset(hdr, 0, sizeof(*hdr));   /* no info leak */
 
@@ -83,12 +84,8 @@ static inline int virtio_net_hdr_from_skb(const struct sk_buff *skb,
 
        if (skb->ip_summed == CHECKSUM_PARTIAL) {
                hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
-               if (skb_vlan_tag_present(skb))
-                       hdr->csum_start = __cpu_to_virtio16(little_endian,
-                               skb_checksum_start_offset(skb) + VLAN_HLEN);
-               else
-                       hdr->csum_start = __cpu_to_virtio16(little_endian,
-                               skb_checksum_start_offset(skb));
+               hdr->csum_start = __cpu_to_virtio16(little_endian,
+                       skb_checksum_start_offset(skb) + vlan_hlen);
                hdr->csum_offset = __cpu_to_virtio16(little_endian,
                                skb->csum_offset);
        } else if (has_data_valid &&
index c9432a0ccd5688adf88cc3e297bd8575871b40dd..29102f3639fee2f56e9fc73093e0c0948fdf977e 100644 (file)
@@ -2037,7 +2037,7 @@ static int packet_rcv_vnet(struct msghdr *msg, const struct sk_buff *skb,
                return -EINVAL;
        *len -= sizeof(vnet_hdr);
 
-       if (virtio_net_hdr_from_skb(skb, &vnet_hdr, vio_le(), true))
+       if (virtio_net_hdr_from_skb(skb, &vnet_hdr, vio_le(), true, 0))
                return -EINVAL;
 
        return memcpy_to_msg(msg, (void *)&vnet_hdr, sizeof(vnet_hdr));
@@ -2304,7 +2304,7 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev,
        if (do_vnet) {
                if (virtio_net_hdr_from_skb(skb, h.raw + macoff -
                                            sizeof(struct virtio_net_hdr),
-                                           vio_le(), true)) {
+                                           vio_le(), true, 0)) {
                        spin_lock(&sk->sk_receive_queue.lock);
                        goto drop_n_account;
                }