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: Toshiaki Makita <makita.toshiaki@lab.ntt.co.jp> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Родитель
4bbb3e0e82
Коммит
cbe7128c4b
|
@ -299,6 +299,44 @@ static inline bool vlan_hw_offload_capable(netdev_features_t features,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __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 error if skb_cow_head failes.
|
||||||
|
*
|
||||||
|
* Does not change skb->protocol so this function can be used during receive.
|
||||||
|
*/
|
||||||
|
static inline int __vlan_insert_inner_tag(struct sk_buff *skb,
|
||||||
|
__be16 vlan_proto, u16 vlan_tci,
|
||||||
|
unsigned int mac_len)
|
||||||
|
{
|
||||||
|
struct vlan_ethhdr *veth;
|
||||||
|
|
||||||
|
if (skb_cow_head(skb, VLAN_HLEN) < 0)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
skb_push(skb, VLAN_HLEN);
|
||||||
|
|
||||||
|
/* Move the mac header sans proto to the beginning of the new header. */
|
||||||
|
memmove(skb->data, skb->data + VLAN_HLEN, mac_len - ETH_TLEN);
|
||||||
|
skb->mac_header -= VLAN_HLEN;
|
||||||
|
|
||||||
|
veth = (struct vlan_ethhdr *)(skb->data + mac_len - ETH_HLEN);
|
||||||
|
|
||||||
|
/* first, the ethernet type */
|
||||||
|
veth->h_vlan_proto = vlan_proto;
|
||||||
|
|
||||||
|
/* now, the TCI */
|
||||||
|
veth->h_vlan_TCI = htons(vlan_tci);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* __vlan_insert_tag - regular VLAN tag inserting
|
* __vlan_insert_tag - regular VLAN tag inserting
|
||||||
* @skb: skbuff to tag
|
* @skb: skbuff to tag
|
||||||
|
@ -313,24 +351,37 @@ static inline bool vlan_hw_offload_capable(netdev_features_t features,
|
||||||
static inline int __vlan_insert_tag(struct sk_buff *skb,
|
static inline int __vlan_insert_tag(struct sk_buff *skb,
|
||||||
__be16 vlan_proto, u16 vlan_tci)
|
__be16 vlan_proto, u16 vlan_tci)
|
||||||
{
|
{
|
||||||
struct vlan_ethhdr *veth;
|
return __vlan_insert_inner_tag(skb, vlan_proto, vlan_tci, ETH_HLEN);
|
||||||
|
}
|
||||||
|
|
||||||
if (skb_cow_head(skb, VLAN_HLEN) < 0)
|
/**
|
||||||
return -ENOMEM;
|
* 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.
|
||||||
|
*
|
||||||
|
* 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_inner_tag(struct sk_buff *skb,
|
||||||
|
__be16 vlan_proto,
|
||||||
|
u16 vlan_tci,
|
||||||
|
unsigned int mac_len)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
veth = skb_push(skb, VLAN_HLEN);
|
err = __vlan_insert_inner_tag(skb, vlan_proto, vlan_tci, mac_len);
|
||||||
|
if (err) {
|
||||||
/* Move the mac addresses to the beginning of the new header. */
|
dev_kfree_skb_any(skb);
|
||||||
memmove(skb->data, skb->data + VLAN_HLEN, 2 * ETH_ALEN);
|
return NULL;
|
||||||
skb->mac_header -= VLAN_HLEN;
|
}
|
||||||
|
return skb;
|
||||||
/* first, the ethernet type */
|
|
||||||
veth->h_vlan_proto = vlan_proto;
|
|
||||||
|
|
||||||
/* now, the TCI */
|
|
||||||
veth->h_vlan_TCI = htons(vlan_tci);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -350,14 +401,7 @@ static inline int __vlan_insert_tag(struct sk_buff *skb,
|
||||||
static inline struct sk_buff *vlan_insert_tag(struct sk_buff *skb,
|
static inline struct sk_buff *vlan_insert_tag(struct sk_buff *skb,
|
||||||
__be16 vlan_proto, u16 vlan_tci)
|
__be16 vlan_proto, u16 vlan_tci)
|
||||||
{
|
{
|
||||||
int err;
|
return vlan_insert_inner_tag(skb, vlan_proto, vlan_tci, ETH_HLEN);
|
||||||
|
|
||||||
err = __vlan_insert_tag(skb, vlan_proto, vlan_tci);
|
|
||||||
if (err) {
|
|
||||||
dev_kfree_skb_any(skb);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return skb;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче