Commit f09e2249c4f5c7c13261ec73f5a7807076af0c8e
Committed by
David S. Miller
1 parent
c53cff5e42
Exists in
smarc-l5.0.0_1.0.0-ga
and in
5 other branches
macvtap: restore vlan header on user read
Ethernet vlan header is not on the packet and kept in the skb->vlan_tci when it comes from lower dev. This patch inserts vlan header in user buffer during skb copy on user read. Signed-off-by: Basil Gor <basil.gor@gmail.com> Acked-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Showing 1 changed file with 38 additions and 5 deletions Side-by-side Diff
drivers/net/macvtap.c
1 | 1 | #include <linux/etherdevice.h> |
2 | 2 | #include <linux/if_macvlan.h> |
3 | +#include <linux/if_vlan.h> | |
3 | 4 | #include <linux/interrupt.h> |
4 | 5 | #include <linux/nsproxy.h> |
5 | 6 | #include <linux/compat.h> |
... | ... | @@ -759,6 +760,8 @@ |
759 | 760 | struct macvlan_dev *vlan; |
760 | 761 | int ret; |
761 | 762 | int vnet_hdr_len = 0; |
763 | + int vlan_offset = 0; | |
764 | + int copied; | |
762 | 765 | |
763 | 766 | if (q->flags & IFF_VNET_HDR) { |
764 | 767 | struct virtio_net_hdr vnet_hdr; |
765 | 768 | |
766 | 769 | |
767 | 770 | |
768 | 771 | |
769 | 772 | |
... | ... | @@ -773,18 +776,48 @@ |
773 | 776 | if (memcpy_toiovecend(iv, (void *)&vnet_hdr, 0, sizeof(vnet_hdr))) |
774 | 777 | return -EFAULT; |
775 | 778 | } |
779 | + copied = vnet_hdr_len; | |
776 | 780 | |
777 | - len = min_t(int, skb->len, len); | |
781 | + if (!vlan_tx_tag_present(skb)) | |
782 | + len = min_t(int, skb->len, len); | |
783 | + else { | |
784 | + int copy; | |
785 | + struct { | |
786 | + __be16 h_vlan_proto; | |
787 | + __be16 h_vlan_TCI; | |
788 | + } veth; | |
789 | + veth.h_vlan_proto = htons(ETH_P_8021Q); | |
790 | + veth.h_vlan_TCI = htons(vlan_tx_tag_get(skb)); | |
778 | 791 | |
779 | - ret = skb_copy_datagram_const_iovec(skb, 0, iv, vnet_hdr_len, len); | |
792 | + vlan_offset = offsetof(struct vlan_ethhdr, h_vlan_proto); | |
793 | + len = min_t(int, skb->len + VLAN_HLEN, len); | |
780 | 794 | |
795 | + copy = min_t(int, vlan_offset, len); | |
796 | + ret = skb_copy_datagram_const_iovec(skb, 0, iv, copied, copy); | |
797 | + len -= copy; | |
798 | + copied += copy; | |
799 | + if (ret || !len) | |
800 | + goto done; | |
801 | + | |
802 | + copy = min_t(int, sizeof(veth), len); | |
803 | + ret = memcpy_toiovecend(iv, (void *)&veth, copied, copy); | |
804 | + len -= copy; | |
805 | + copied += copy; | |
806 | + if (ret || !len) | |
807 | + goto done; | |
808 | + } | |
809 | + | |
810 | + ret = skb_copy_datagram_const_iovec(skb, vlan_offset, iv, copied, len); | |
811 | + copied += len; | |
812 | + | |
813 | +done: | |
781 | 814 | rcu_read_lock_bh(); |
782 | 815 | vlan = rcu_dereference_bh(q->vlan); |
783 | 816 | if (vlan) |
784 | - macvlan_count_rx(vlan, len, ret == 0, 0); | |
817 | + macvlan_count_rx(vlan, copied - vnet_hdr_len, ret == 0, 0); | |
785 | 818 | rcu_read_unlock_bh(); |
786 | 819 | |
787 | - return ret ? ret : (len + vnet_hdr_len); | |
820 | + return ret ? ret : copied; | |
788 | 821 | } |
789 | 822 | |
790 | 823 | static ssize_t macvtap_do_read(struct macvtap_queue *q, struct kiocb *iocb, |