Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e693be29 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'vlan-untag-and-insert-fixes'

Toshiaki Makita says:

====================
Fix vlan untag and insertion for bridge and vlan with reorder_hdr off

As Brandon Carpenter reported[1], sending non-vlan-offloaded packets from
bridge devices ends up with corrupted packets. He narrowed down this problem
and found that the root cause is in skb_reorder_vlan_header().

While I was working on fixing this problem, I found that the function does
not work properly for double tagged packets with reorder_hdr off as well.

Patch 1 fixes these 2 problems in skb_reorder_vlan_header().

And it turned out that fixing skb_reorder_vlan_header() is not sufficient
to receive double tagged packets with reorder_hdr off while I was testing the
fix. Vlan tags got out of order when vlan devices with reorder_hdr disabled
were stacked. Patch 2 fixes this problem.

[1] https://www.spinics.net/lists/linux-ethernet-bridging/msg07039.html


====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 51d4740f cbe7128c
Loading
Loading
Loading
Loading
+55 −11
Original line number Diff line number Diff line
@@ -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
 * @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
 * 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_tag(struct sk_buff *skb,
				    __be16 vlan_proto, u16 vlan_tci)
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;

	veth = skb_push(skb, VLAN_HLEN);
	skb_push(skb, VLAN_HLEN);

	/* Move the mac addresses to the beginning of the new header. */
	memmove(skb->data, skb->data + VLAN_HLEN, 2 * ETH_ALEN);
	/* 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;

@@ -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
 * @vlan_proto: VLAN encapsulation protocol
 * @vlan_tci: VLAN TCI to insert
 *
 * 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.
 *
 * 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,
 *
 * 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)
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;

	err = __vlan_insert_tag(skb, vlan_proto, vlan_tci);
	err = __vlan_insert_inner_tag(skb, vlan_proto, vlan_tci, mac_len);
	if (err) {
		dev_kfree_skb_any(skb);
		return NULL;
@@ -360,6 +384,26 @@ static inline struct sk_buff *vlan_insert_tag(struct sk_buff *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
 * @skb: skbuff to tag
+1 −0
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@
 */

#define ETH_ALEN	6		/* Octets in one ethernet addr	 */
#define ETH_TLEN	2		/* Octets in ethernet type field */
#define ETH_HLEN	14		/* Total octets in header.	 */
#define ETH_ZLEN	60		/* Min. octets in frame sans FCS */
#define ETH_DATA_LEN	1500		/* Max. octets in payload	 */
+2 −2
Original line number Diff line number Diff line
@@ -48,8 +48,8 @@ bool vlan_do_receive(struct sk_buff **skbp)
		 * original position later
		 */
		skb_push(skb, offset);
		skb = *skbp = vlan_insert_tag(skb, skb->vlan_proto,
					      skb->vlan_tci);
		skb = *skbp = vlan_insert_inner_tag(skb, skb->vlan_proto,
						    skb->vlan_tci, skb->mac_len);
		if (!skb)
			return false;
		skb_pull(skb, offset + VLAN_HLEN);
+5 −2
Original line number Diff line number Diff line
@@ -5020,13 +5020,16 @@ EXPORT_SYMBOL_GPL(skb_gso_validate_mac_len);

static struct sk_buff *skb_reorder_vlan_header(struct sk_buff *skb)
{
	int mac_len;

	if (skb_cow(skb, skb_headroom(skb)) < 0) {
		kfree_skb(skb);
		return NULL;
	}

	memmove(skb->data - ETH_HLEN, skb->data - skb->mac_len - VLAN_HLEN,
		2 * ETH_ALEN);
	mac_len = skb->data - skb_mac_header(skb);
	memmove(skb_mac_header(skb) + VLAN_HLEN, skb_mac_header(skb),
		mac_len - VLAN_HLEN - ETH_TLEN);
	skb->mac_header += VLAN_HLEN;
	return skb;
}