Commit cbe7128c authored by Toshiaki Makita's avatar Toshiaki Makita Committed by David S. Miller

vlan: Fix out of order vlan headers with reorder header off

With reorder header off, received packets are untagged in skb_vlan_untag()
called from within __netif_receive_skb_core(), and later the tag will be
inserted back in vlan_do_receive().

This caused out of order vlan headers when we create a vlan device on top
of another vlan device, because vlan_do_receive() inserts a tag as the
outermost vlan tag. E.g. the outer tag is first removed in skb_vlan_untag()
and inserted back in vlan_do_receive(), then the inner tag is next removed
and inserted back as the outermost tag.

This patch fixes the behaviour by inserting the inner tag at the right
position.
Signed-off-by: default avatarToshiaki Makita <makita.toshiaki@lab.ntt.co.jp>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 4bbb3e0e
...@@ -300,30 +300,34 @@ static inline bool vlan_hw_offload_capable(netdev_features_t features, ...@@ -300,30 +300,34 @@ static inline bool vlan_hw_offload_capable(netdev_features_t features,
} }
/** /**
* __vlan_insert_tag - regular VLAN tag inserting * __vlan_insert_inner_tag - inner VLAN tag inserting
* @skb: skbuff to tag * @skb: skbuff to tag
* @vlan_proto: VLAN encapsulation protocol * @vlan_proto: VLAN encapsulation protocol
* @vlan_tci: VLAN TCI to insert * @vlan_tci: VLAN TCI to insert
* @mac_len: MAC header length including outer vlan headers
* *
* Inserts the VLAN tag into @skb as part of the payload * Inserts the VLAN tag into @skb as part of the payload at offset mac_len
* Returns error if skb_cow_head failes. * Returns error if skb_cow_head failes.
* *
* Does not change skb->protocol so this function can be used during receive. * Does not change skb->protocol so this function can be used during receive.
*/ */
static inline int __vlan_insert_tag(struct sk_buff *skb, static inline int __vlan_insert_inner_tag(struct sk_buff *skb,
__be16 vlan_proto, u16 vlan_tci) __be16 vlan_proto, u16 vlan_tci,
unsigned int mac_len)
{ {
struct vlan_ethhdr *veth; struct vlan_ethhdr *veth;
if (skb_cow_head(skb, VLAN_HLEN) < 0) if (skb_cow_head(skb, VLAN_HLEN) < 0)
return -ENOMEM; return -ENOMEM;
veth = skb_push(skb, VLAN_HLEN); skb_push(skb, VLAN_HLEN);
/* Move the mac addresses to the beginning of the new header. */ /* Move the mac header sans proto to the beginning of the new header. */
memmove(skb->data, skb->data + VLAN_HLEN, 2 * ETH_ALEN); memmove(skb->data, skb->data + VLAN_HLEN, mac_len - ETH_TLEN);
skb->mac_header -= VLAN_HLEN; skb->mac_header -= VLAN_HLEN;
veth = (struct vlan_ethhdr *)(skb->data + mac_len - ETH_HLEN);
/* first, the ethernet type */ /* first, the ethernet type */
veth->h_vlan_proto = vlan_proto; veth->h_vlan_proto = vlan_proto;
...@@ -334,12 +338,30 @@ static inline int __vlan_insert_tag(struct sk_buff *skb, ...@@ -334,12 +338,30 @@ static inline int __vlan_insert_tag(struct sk_buff *skb,
} }
/** /**
* vlan_insert_tag - regular VLAN tag inserting * __vlan_insert_tag - regular VLAN tag inserting
* @skb: skbuff to tag * @skb: skbuff to tag
* @vlan_proto: VLAN encapsulation protocol * @vlan_proto: VLAN encapsulation protocol
* @vlan_tci: VLAN TCI to insert * @vlan_tci: VLAN TCI to insert
* *
* Inserts the VLAN tag into @skb as part of the payload * Inserts the VLAN tag into @skb as part of the payload
* Returns error if skb_cow_head failes.
*
* Does not change skb->protocol so this function can be used during receive.
*/
static inline int __vlan_insert_tag(struct sk_buff *skb,
__be16 vlan_proto, u16 vlan_tci)
{
return __vlan_insert_inner_tag(skb, vlan_proto, vlan_tci, ETH_HLEN);
}
/**
* vlan_insert_inner_tag - inner VLAN tag inserting
* @skb: skbuff to tag
* @vlan_proto: VLAN encapsulation protocol
* @vlan_tci: VLAN TCI to insert
* @mac_len: MAC header length including outer vlan headers
*
* Inserts the VLAN tag into @skb as part of the payload at offset mac_len
* Returns a VLAN tagged skb. If a new skb is created, @skb is freed. * Returns a VLAN tagged skb. If a new skb is created, @skb is freed.
* *
* Following the skb_unshare() example, in case of error, the calling function * Following the skb_unshare() example, in case of error, the calling function
...@@ -347,12 +369,14 @@ static inline int __vlan_insert_tag(struct sk_buff *skb, ...@@ -347,12 +369,14 @@ static inline int __vlan_insert_tag(struct sk_buff *skb,
* *
* Does not change skb->protocol so this function can be used during receive. * Does not change skb->protocol so this function can be used during receive.
*/ */
static inline struct sk_buff *vlan_insert_tag(struct sk_buff *skb, static inline struct sk_buff *vlan_insert_inner_tag(struct sk_buff *skb,
__be16 vlan_proto, u16 vlan_tci) __be16 vlan_proto,
u16 vlan_tci,
unsigned int mac_len)
{ {
int err; int err;
err = __vlan_insert_tag(skb, vlan_proto, vlan_tci); err = __vlan_insert_inner_tag(skb, vlan_proto, vlan_tci, mac_len);
if (err) { if (err) {
dev_kfree_skb_any(skb); dev_kfree_skb_any(skb);
return NULL; return NULL;
...@@ -360,6 +384,26 @@ static inline struct sk_buff *vlan_insert_tag(struct sk_buff *skb, ...@@ -360,6 +384,26 @@ static inline struct sk_buff *vlan_insert_tag(struct sk_buff *skb,
return skb; return skb;
} }
/**
* vlan_insert_tag - regular VLAN tag inserting
* @skb: skbuff to tag
* @vlan_proto: VLAN encapsulation protocol
* @vlan_tci: VLAN TCI to insert
*
* Inserts the VLAN tag into @skb as part of the payload
* Returns a VLAN tagged skb. If a new skb is created, @skb is freed.
*
* Following the skb_unshare() example, in case of error, the calling function
* doesn't have to worry about freeing the original skb.
*
* Does not change skb->protocol so this function can be used during receive.
*/
static inline struct sk_buff *vlan_insert_tag(struct sk_buff *skb,
__be16 vlan_proto, u16 vlan_tci)
{
return vlan_insert_inner_tag(skb, vlan_proto, vlan_tci, ETH_HLEN);
}
/** /**
* vlan_insert_tag_set_proto - regular VLAN tag inserting * vlan_insert_tag_set_proto - regular VLAN tag inserting
* @skb: skbuff to tag * @skb: skbuff to tag
......
...@@ -48,8 +48,8 @@ bool vlan_do_receive(struct sk_buff **skbp) ...@@ -48,8 +48,8 @@ bool vlan_do_receive(struct sk_buff **skbp)
* original position later * original position later
*/ */
skb_push(skb, offset); skb_push(skb, offset);
skb = *skbp = vlan_insert_tag(skb, skb->vlan_proto, skb = *skbp = vlan_insert_inner_tag(skb, skb->vlan_proto,
skb->vlan_tci); skb->vlan_tci, skb->mac_len);
if (!skb) if (!skb)
return false; return false;
skb_pull(skb, offset + VLAN_HLEN); skb_pull(skb, offset + VLAN_HLEN);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment