Skip to content

Commit 4bbb3e0

Browse files
Toshiaki Makitadavem330
authored andcommitted
net: Fix vlan untag for bridge and vlan_dev with reorder_hdr off
When we have a bridge with vlan_filtering on and a vlan device on top of it, packets would be corrupted in skb_vlan_untag() called from br_dev_xmit(). The problem sits in skb_reorder_vlan_header() used in skb_vlan_untag(), which makes use of skb->mac_len. In this function mac_len is meant for handling rx path with vlan devices with reorder_header disabled, but in tx path mac_len is typically 0 and cannot be used, which is the problem in this case. The current code even does not properly handle rx path (skb_vlan_untag() called from __netif_receive_skb_core()) with reorder_header off actually. In rx path single tag case, it works as follows: - Before skb_reorder_vlan_header() mac_header data v v +-------------------+-------------+------+---- | ETH | VLAN | ETH | | ADDRS | TPID | TCI | TYPE | +-------------------+-------------+------+---- <-------- mac_len ---------> <-------------> to be removed - After skb_reorder_vlan_header() mac_header data v v +-------------------+------+---- | ETH | ETH | | ADDRS | TYPE | +-------------------+------+---- <-------- mac_len ---------> This is ok, but in rx double tag case, it corrupts packets: - Before skb_reorder_vlan_header() mac_header data v v +-------------------+-------------+-------------+------+---- | ETH | VLAN | VLAN | ETH | | ADDRS | TPID | TCI | TPID | TCI | TYPE | +-------------------+-------------+-------------+------+---- <--------------- mac_len ----------------> <-------------> should be removed <---------------------------> actually will be removed - After skb_reorder_vlan_header() mac_header data v v +-------------------+------+---- | ETH | ETH | | ADDRS | TYPE | +-------------------+------+---- <--------------- mac_len ----------------> So, two of vlan tags are both removed while only inner one should be removed and mac_header (and mac_len) is broken. skb_vlan_untag() is meant for removing the vlan header at (skb->data - 2), so use skb->data and skb->mac_header to calculate the right offset. Reported-by: Brandon Carpenter <brandon.carpenter@cypherpath.com> Fixes: a6e18ff ("vlan: Fix untag operations of stacked vlans with REORDER_HEADER off") Signed-off-by: Toshiaki Makita <makita.toshiaki@lab.ntt.co.jp> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 51d4740 commit 4bbb3e0

2 files changed

Lines changed: 6 additions & 2 deletions

File tree

include/uapi/linux/if_ether.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
*/
3131

3232
#define ETH_ALEN 6 /* Octets in one ethernet addr */
33+
#define ETH_TLEN 2 /* Octets in ethernet type field */
3334
#define ETH_HLEN 14 /* Total octets in header. */
3435
#define ETH_ZLEN 60 /* Min. octets in frame sans FCS */
3536
#define ETH_DATA_LEN 1500 /* Max. octets in payload */

net/core/skbuff.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5020,13 +5020,16 @@ EXPORT_SYMBOL_GPL(skb_gso_validate_mac_len);
50205020

50215021
static struct sk_buff *skb_reorder_vlan_header(struct sk_buff *skb)
50225022
{
5023+
int mac_len;
5024+
50235025
if (skb_cow(skb, skb_headroom(skb)) < 0) {
50245026
kfree_skb(skb);
50255027
return NULL;
50265028
}
50275029

5028-
memmove(skb->data - ETH_HLEN, skb->data - skb->mac_len - VLAN_HLEN,
5029-
2 * ETH_ALEN);
5030+
mac_len = skb->data - skb_mac_header(skb);
5031+
memmove(skb_mac_header(skb) + VLAN_HLEN, skb_mac_header(skb),
5032+
mac_len - VLAN_HLEN - ETH_TLEN);
50305033
skb->mac_header += VLAN_HLEN;
50315034
return skb;
50325035
}

0 commit comments

Comments
 (0)