forked from mirrors/linux
		
	net: vlan: add 802.1ad support
Add support for 802.1ad VLAN devices. This mainly consists of checking for
ETH_P_8021AD in addition to ETH_P_8021Q in a couple of places and check
offloading capabilities based on the used protocol.
Configuration is done using "ip link":
# ip link add link eth0 eth0.1000 \
	type vlan proto 802.1ad id 1000
# ip link add link eth0.1000 eth0.1000.1000 \
	type vlan proto 802.1q id 1000
52:54:00:12:34:56 > 92:b1:54:28:e4:8c, ethertype 802.1Q (0x8100), length 106: vlan 1000, p 0, ethertype 802.1Q, vlan 1000, p 0, ethertype IPv4, (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto ICMP (1), length 84)
    20.1.0.2 > 20.1.0.1: ICMP echo request, id 3003, seq 8, length 64
92:b1:54:28:e4:8c > 52:54:00:12:34:56, ethertype 802.1Q-QinQ (0x88a8), length 106: vlan 1000, p 0, ethertype 802.1Q, vlan 1000, p 0, ethertype IPv4, (tos 0x0, ttl 64, id 47944, offset 0, flags [none], proto ICMP (1), length 84)
    20.1.0.1 > 20.1.0.2: ICMP echo reply, id 3003, seq 8, length 64
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
			
			
This commit is contained in:
		
							parent
							
								
									86a9bad3ab
								
							
						
					
					
						commit
						8ad227ff89
					
				
					 8 changed files with 61 additions and 16 deletions
				
			
		| 
						 | 
					@ -162,6 +162,8 @@ static inline bool vlan_hw_offload_capable(netdev_features_t features,
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (proto == htons(ETH_P_8021Q) && features & NETIF_F_HW_VLAN_CTAG_TX)
 | 
						if (proto == htons(ETH_P_8021Q) && features & NETIF_F_HW_VLAN_CTAG_TX)
 | 
				
			||||||
		return true;
 | 
							return true;
 | 
				
			||||||
 | 
						if (proto == htons(ETH_P_8021AD) && features & NETIF_F_HW_VLAN_STAG_TX)
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
	return false;
 | 
						return false;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -271,9 +273,9 @@ static inline int __vlan_get_tag(const struct sk_buff *skb, u16 *vlan_tci)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct vlan_ethhdr *veth = (struct vlan_ethhdr *)skb->data;
 | 
						struct vlan_ethhdr *veth = (struct vlan_ethhdr *)skb->data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (veth->h_vlan_proto != htons(ETH_P_8021Q)) {
 | 
						if (veth->h_vlan_proto != htons(ETH_P_8021Q) &&
 | 
				
			||||||
 | 
						    veth->h_vlan_proto != htons(ETH_P_8021AD))
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	*vlan_tci = ntohs(veth->h_vlan_TCI);
 | 
						*vlan_tci = ntohs(veth->h_vlan_TCI);
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,6 +25,9 @@ enum {
 | 
				
			||||||
	NETIF_F_HW_VLAN_CTAG_TX_BIT,	/* Transmit VLAN CTAG HW acceleration */
 | 
						NETIF_F_HW_VLAN_CTAG_TX_BIT,	/* Transmit VLAN CTAG HW acceleration */
 | 
				
			||||||
	NETIF_F_HW_VLAN_CTAG_RX_BIT,	/* Receive VLAN CTAG HW acceleration */
 | 
						NETIF_F_HW_VLAN_CTAG_RX_BIT,	/* Receive VLAN CTAG HW acceleration */
 | 
				
			||||||
	NETIF_F_HW_VLAN_CTAG_FILTER_BIT,/* Receive filtering on VLAN CTAGs */
 | 
						NETIF_F_HW_VLAN_CTAG_FILTER_BIT,/* Receive filtering on VLAN CTAGs */
 | 
				
			||||||
 | 
						NETIF_F_HW_VLAN_STAG_TX_BIT,	/* Transmit VLAN STAG HW acceleration */
 | 
				
			||||||
 | 
						NETIF_F_HW_VLAN_STAG_RX_BIT,	/* Receive VLAN STAG HW acceleration */
 | 
				
			||||||
 | 
						NETIF_F_HW_VLAN_STAG_FILTER_BIT,/* Receive filtering on VLAN STAGs */
 | 
				
			||||||
	NETIF_F_VLAN_CHALLENGED_BIT,	/* Device cannot handle VLAN packets */
 | 
						NETIF_F_VLAN_CHALLENGED_BIT,	/* Device cannot handle VLAN packets */
 | 
				
			||||||
	NETIF_F_GSO_BIT,		/* Enable software GSO. */
 | 
						NETIF_F_GSO_BIT,		/* Enable software GSO. */
 | 
				
			||||||
	NETIF_F_LLTX_BIT,		/* LockLess TX - deprecated. Please */
 | 
						NETIF_F_LLTX_BIT,		/* LockLess TX - deprecated. Please */
 | 
				
			||||||
| 
						 | 
					@ -83,6 +86,9 @@ enum {
 | 
				
			||||||
#define NETIF_F_HW_VLAN_CTAG_FILTER __NETIF_F(HW_VLAN_CTAG_FILTER)
 | 
					#define NETIF_F_HW_VLAN_CTAG_FILTER __NETIF_F(HW_VLAN_CTAG_FILTER)
 | 
				
			||||||
#define NETIF_F_HW_VLAN_CTAG_RX	__NETIF_F(HW_VLAN_CTAG_RX)
 | 
					#define NETIF_F_HW_VLAN_CTAG_RX	__NETIF_F(HW_VLAN_CTAG_RX)
 | 
				
			||||||
#define NETIF_F_HW_VLAN_CTAG_TX	__NETIF_F(HW_VLAN_CTAG_TX)
 | 
					#define NETIF_F_HW_VLAN_CTAG_TX	__NETIF_F(HW_VLAN_CTAG_TX)
 | 
				
			||||||
 | 
					#define NETIF_F_HW_VLAN_STAG_FILTER __NETIF_F(HW_VLAN_STAG_FILTER)
 | 
				
			||||||
 | 
					#define NETIF_F_HW_VLAN_STAG_RX	__NETIF_F(HW_VLAN_STAG_RX)
 | 
				
			||||||
 | 
					#define NETIF_F_HW_VLAN_STAG_TX	__NETIF_F(HW_VLAN_STAG_TX)
 | 
				
			||||||
#define NETIF_F_IP_CSUM		__NETIF_F(IP_CSUM)
 | 
					#define NETIF_F_IP_CSUM		__NETIF_F(IP_CSUM)
 | 
				
			||||||
#define NETIF_F_IPV6_CSUM	__NETIF_F(IPV6_CSUM)
 | 
					#define NETIF_F_IPV6_CSUM	__NETIF_F(IPV6_CSUM)
 | 
				
			||||||
#define NETIF_F_LLTX		__NETIF_F(LLTX)
 | 
					#define NETIF_F_LLTX		__NETIF_F(LLTX)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -250,6 +250,7 @@ enum {
 | 
				
			||||||
	IFLA_VLAN_FLAGS,
 | 
						IFLA_VLAN_FLAGS,
 | 
				
			||||||
	IFLA_VLAN_EGRESS_QOS,
 | 
						IFLA_VLAN_EGRESS_QOS,
 | 
				
			||||||
	IFLA_VLAN_INGRESS_QOS,
 | 
						IFLA_VLAN_INGRESS_QOS,
 | 
				
			||||||
 | 
						IFLA_VLAN_PROTOCOL,
 | 
				
			||||||
	__IFLA_VLAN_MAX,
 | 
						__IFLA_VLAN_MAX,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,7 +3,7 @@
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
config VLAN_8021Q
 | 
					config VLAN_8021Q
 | 
				
			||||||
	tristate "802.1Q VLAN Support"
 | 
						tristate "802.1Q/802.1ad VLAN Support"
 | 
				
			||||||
	---help---
 | 
						---help---
 | 
				
			||||||
	  Select this and you will be able to create 802.1Q VLAN interfaces
 | 
						  Select this and you will be able to create 802.1Q VLAN interfaces
 | 
				
			||||||
	  on your ethernet interfaces.  802.1Q VLAN supports almost
 | 
						  on your ethernet interfaces.  802.1Q VLAN supports almost
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -91,6 +91,7 @@ static inline struct vlan_dev_priv *vlan_dev_priv(const struct net_device *dev)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum vlan_protos {
 | 
					enum vlan_protos {
 | 
				
			||||||
	VLAN_PROTO_8021Q	= 0,
 | 
						VLAN_PROTO_8021Q	= 0,
 | 
				
			||||||
 | 
						VLAN_PROTO_8021AD,
 | 
				
			||||||
	VLAN_PROTO_NUM,
 | 
						VLAN_PROTO_NUM,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -116,6 +117,8 @@ static inline unsigned int vlan_proto_idx(__be16 proto)
 | 
				
			||||||
	switch (proto) {
 | 
						switch (proto) {
 | 
				
			||||||
	case __constant_htons(ETH_P_8021Q):
 | 
						case __constant_htons(ETH_P_8021Q):
 | 
				
			||||||
		return VLAN_PROTO_8021Q;
 | 
							return VLAN_PROTO_8021Q;
 | 
				
			||||||
 | 
						case __constant_htons(ETH_P_8021AD):
 | 
				
			||||||
 | 
							return VLAN_PROTO_8021AD;
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		BUG();
 | 
							BUG();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -194,6 +194,18 @@ struct vlan_vid_info {
 | 
				
			||||||
	int refcount;
 | 
						int refcount;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool vlan_hw_filter_capable(const struct net_device *dev,
 | 
				
			||||||
 | 
									     const struct vlan_vid_info *vid_info)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (vid_info->proto == htons(ETH_P_8021Q) &&
 | 
				
			||||||
 | 
						    dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						if (vid_info->proto == htons(ETH_P_8021AD) &&
 | 
				
			||||||
 | 
						    dev->features & NETIF_F_HW_VLAN_STAG_FILTER)
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct vlan_vid_info *vlan_vid_info_get(struct vlan_info *vlan_info,
 | 
					static struct vlan_vid_info *vlan_vid_info_get(struct vlan_info *vlan_info,
 | 
				
			||||||
					       __be16 proto, u16 vid)
 | 
										       __be16 proto, u16 vid)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -231,8 +243,7 @@ static int __vlan_vid_add(struct vlan_info *vlan_info, __be16 proto, u16 vid,
 | 
				
			||||||
	if (!vid_info)
 | 
						if (!vid_info)
 | 
				
			||||||
		return -ENOMEM;
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (proto == htons(ETH_P_8021Q) &&
 | 
						if (vlan_hw_filter_capable(dev, vid_info)) {
 | 
				
			||||||
	    dev->features & NETIF_F_HW_VLAN_CTAG_FILTER) {
 | 
					 | 
				
			||||||
		err =  ops->ndo_vlan_rx_add_vid(dev, proto, vid);
 | 
							err =  ops->ndo_vlan_rx_add_vid(dev, proto, vid);
 | 
				
			||||||
		if (err) {
 | 
							if (err) {
 | 
				
			||||||
			kfree(vid_info);
 | 
								kfree(vid_info);
 | 
				
			||||||
| 
						 | 
					@ -290,8 +301,7 @@ static void __vlan_vid_del(struct vlan_info *vlan_info,
 | 
				
			||||||
	u16 vid = vid_info->vid;
 | 
						u16 vid = vid_info->vid;
 | 
				
			||||||
	int err;
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (proto == htons(ETH_P_8021Q) &&
 | 
						if (vlan_hw_filter_capable(dev, vid_info)) {
 | 
				
			||||||
	    dev->features & NETIF_F_HW_VLAN_CTAG_FILTER) {
 | 
					 | 
				
			||||||
		err = ops->ndo_vlan_rx_kill_vid(dev, proto, vid);
 | 
							err = ops->ndo_vlan_rx_kill_vid(dev, proto, vid);
 | 
				
			||||||
		if (err) {
 | 
							if (err) {
 | 
				
			||||||
			pr_warn("failed to kill vid %04x/%d for device %s\n",
 | 
								pr_warn("failed to kill vid %04x/%d for device %s\n",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,6 +23,7 @@ static const struct nla_policy vlan_policy[IFLA_VLAN_MAX + 1] = {
 | 
				
			||||||
	[IFLA_VLAN_FLAGS]	= { .len = sizeof(struct ifla_vlan_flags) },
 | 
						[IFLA_VLAN_FLAGS]	= { .len = sizeof(struct ifla_vlan_flags) },
 | 
				
			||||||
	[IFLA_VLAN_EGRESS_QOS]	= { .type = NLA_NESTED },
 | 
						[IFLA_VLAN_EGRESS_QOS]	= { .type = NLA_NESTED },
 | 
				
			||||||
	[IFLA_VLAN_INGRESS_QOS] = { .type = NLA_NESTED },
 | 
						[IFLA_VLAN_INGRESS_QOS] = { .type = NLA_NESTED },
 | 
				
			||||||
 | 
						[IFLA_VLAN_PROTOCOL]	= { .type = NLA_U16 },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const struct nla_policy vlan_map_policy[IFLA_VLAN_QOS_MAX + 1] = {
 | 
					static const struct nla_policy vlan_map_policy[IFLA_VLAN_QOS_MAX + 1] = {
 | 
				
			||||||
| 
						 | 
					@ -53,6 +54,16 @@ static int vlan_validate(struct nlattr *tb[], struct nlattr *data[])
 | 
				
			||||||
	if (!data)
 | 
						if (!data)
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (data[IFLA_VLAN_PROTOCOL]) {
 | 
				
			||||||
 | 
							switch (nla_get_be16(data[IFLA_VLAN_PROTOCOL])) {
 | 
				
			||||||
 | 
							case __constant_htons(ETH_P_8021Q):
 | 
				
			||||||
 | 
							case __constant_htons(ETH_P_8021AD):
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								return -EPROTONOSUPPORT;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (data[IFLA_VLAN_ID]) {
 | 
						if (data[IFLA_VLAN_ID]) {
 | 
				
			||||||
		id = nla_get_u16(data[IFLA_VLAN_ID]);
 | 
							id = nla_get_u16(data[IFLA_VLAN_ID]);
 | 
				
			||||||
		if (id >= VLAN_VID_MASK)
 | 
							if (id >= VLAN_VID_MASK)
 | 
				
			||||||
| 
						 | 
					@ -107,6 +118,7 @@ static int vlan_newlink(struct net *src_net, struct net_device *dev,
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
 | 
						struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
 | 
				
			||||||
	struct net_device *real_dev;
 | 
						struct net_device *real_dev;
 | 
				
			||||||
 | 
						__be16 proto;
 | 
				
			||||||
	int err;
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!data[IFLA_VLAN_ID])
 | 
						if (!data[IFLA_VLAN_ID])
 | 
				
			||||||
| 
						 | 
					@ -118,7 +130,12 @@ static int vlan_newlink(struct net *src_net, struct net_device *dev,
 | 
				
			||||||
	if (!real_dev)
 | 
						if (!real_dev)
 | 
				
			||||||
		return -ENODEV;
 | 
							return -ENODEV;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	vlan->vlan_proto = htons(ETH_P_8021Q);
 | 
						if (data[IFLA_VLAN_PROTOCOL])
 | 
				
			||||||
 | 
							proto = nla_get_be16(data[IFLA_VLAN_PROTOCOL]);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							proto = htons(ETH_P_8021Q);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						vlan->vlan_proto = proto;
 | 
				
			||||||
	vlan->vlan_id	 = nla_get_u16(data[IFLA_VLAN_ID]);
 | 
						vlan->vlan_id	 = nla_get_u16(data[IFLA_VLAN_ID]);
 | 
				
			||||||
	vlan->real_dev	 = real_dev;
 | 
						vlan->real_dev	 = real_dev;
 | 
				
			||||||
	vlan->flags	 = VLAN_FLAG_REORDER_HDR;
 | 
						vlan->flags	 = VLAN_FLAG_REORDER_HDR;
 | 
				
			||||||
| 
						 | 
					@ -152,7 +169,8 @@ static size_t vlan_get_size(const struct net_device *dev)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
 | 
						struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nla_total_size(2) +	/* IFLA_VLAN_ID */
 | 
						return nla_total_size(2) +	/* IFLA_VLAN_PROTOCOL */
 | 
				
			||||||
 | 
						       nla_total_size(2) +	/* IFLA_VLAN_ID */
 | 
				
			||||||
	       sizeof(struct ifla_vlan_flags) + /* IFLA_VLAN_FLAGS */
 | 
						       sizeof(struct ifla_vlan_flags) + /* IFLA_VLAN_FLAGS */
 | 
				
			||||||
	       vlan_qos_map_size(vlan->nr_ingress_mappings) +
 | 
						       vlan_qos_map_size(vlan->nr_ingress_mappings) +
 | 
				
			||||||
	       vlan_qos_map_size(vlan->nr_egress_mappings);
 | 
						       vlan_qos_map_size(vlan->nr_egress_mappings);
 | 
				
			||||||
| 
						 | 
					@ -167,7 +185,8 @@ static int vlan_fill_info(struct sk_buff *skb, const struct net_device *dev)
 | 
				
			||||||
	struct nlattr *nest;
 | 
						struct nlattr *nest;
 | 
				
			||||||
	unsigned int i;
 | 
						unsigned int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (nla_put_u16(skb, IFLA_VLAN_ID, vlan_dev_priv(dev)->vlan_id))
 | 
						if (nla_put_be16(skb, IFLA_VLAN_PROTOCOL, vlan->vlan_proto) ||
 | 
				
			||||||
 | 
						    nla_put_u16(skb, IFLA_VLAN_ID, vlan->vlan_id))
 | 
				
			||||||
		goto nla_put_failure;
 | 
							goto nla_put_failure;
 | 
				
			||||||
	if (vlan->flags) {
 | 
						if (vlan->flags) {
 | 
				
			||||||
		f.flags = vlan->flags;
 | 
							f.flags = vlan->flags;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2212,7 +2212,7 @@ __be16 skb_network_protocol(struct sk_buff *skb)
 | 
				
			||||||
	__be16 type = skb->protocol;
 | 
						__be16 type = skb->protocol;
 | 
				
			||||||
	int vlan_depth = ETH_HLEN;
 | 
						int vlan_depth = ETH_HLEN;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	while (type == htons(ETH_P_8021Q)) {
 | 
						while (type == htons(ETH_P_8021Q) || type == htons(ETH_P_8021AD)) {
 | 
				
			||||||
		struct vlan_hdr *vh;
 | 
							struct vlan_hdr *vh;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (unlikely(!pskb_may_pull(skb, vlan_depth + VLAN_HLEN)))
 | 
							if (unlikely(!pskb_may_pull(skb, vlan_depth + VLAN_HLEN)))
 | 
				
			||||||
| 
						 | 
					@ -2428,20 +2428,22 @@ netdev_features_t netif_skb_features(struct sk_buff *skb)
 | 
				
			||||||
	if (skb_shinfo(skb)->gso_segs > skb->dev->gso_max_segs)
 | 
						if (skb_shinfo(skb)->gso_segs > skb->dev->gso_max_segs)
 | 
				
			||||||
		features &= ~NETIF_F_GSO_MASK;
 | 
							features &= ~NETIF_F_GSO_MASK;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (protocol == htons(ETH_P_8021Q)) {
 | 
						if (protocol == htons(ETH_P_8021Q) || protocol == htons(ETH_P_8021AD)) {
 | 
				
			||||||
		struct vlan_ethhdr *veh = (struct vlan_ethhdr *)skb->data;
 | 
							struct vlan_ethhdr *veh = (struct vlan_ethhdr *)skb->data;
 | 
				
			||||||
		protocol = veh->h_vlan_encapsulated_proto;
 | 
							protocol = veh->h_vlan_encapsulated_proto;
 | 
				
			||||||
	} else if (!vlan_tx_tag_present(skb)) {
 | 
						} else if (!vlan_tx_tag_present(skb)) {
 | 
				
			||||||
		return harmonize_features(skb, protocol, features);
 | 
							return harmonize_features(skb, protocol, features);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	features &= (skb->dev->vlan_features | NETIF_F_HW_VLAN_CTAG_TX);
 | 
						features &= (skb->dev->vlan_features | NETIF_F_HW_VLAN_CTAG_TX |
 | 
				
			||||||
 | 
										       NETIF_F_HW_VLAN_STAG_TX);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (protocol != htons(ETH_P_8021Q)) {
 | 
						if (protocol != htons(ETH_P_8021Q) && protocol != htons(ETH_P_8021AD)) {
 | 
				
			||||||
		return harmonize_features(skb, protocol, features);
 | 
							return harmonize_features(skb, protocol, features);
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		features &= NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST |
 | 
							features &= NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST |
 | 
				
			||||||
				NETIF_F_GEN_CSUM | NETIF_F_HW_VLAN_CTAG_TX;
 | 
									NETIF_F_GEN_CSUM | NETIF_F_HW_VLAN_CTAG_TX |
 | 
				
			||||||
 | 
									NETIF_F_HW_VLAN_STAG_TX;
 | 
				
			||||||
		return harmonize_features(skb, protocol, features);
 | 
							return harmonize_features(skb, protocol, features);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -3360,6 +3362,7 @@ static bool skb_pfmemalloc_protocol(struct sk_buff *skb)
 | 
				
			||||||
	case __constant_htons(ETH_P_IP):
 | 
						case __constant_htons(ETH_P_IP):
 | 
				
			||||||
	case __constant_htons(ETH_P_IPV6):
 | 
						case __constant_htons(ETH_P_IPV6):
 | 
				
			||||||
	case __constant_htons(ETH_P_8021Q):
 | 
						case __constant_htons(ETH_P_8021Q):
 | 
				
			||||||
 | 
						case __constant_htons(ETH_P_8021AD):
 | 
				
			||||||
		return true;
 | 
							return true;
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
| 
						 | 
					@ -3400,7 +3403,8 @@ static int __netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	__this_cpu_inc(softnet_data.processed);
 | 
						__this_cpu_inc(softnet_data.processed);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (skb->protocol == cpu_to_be16(ETH_P_8021Q)) {
 | 
						if (skb->protocol == cpu_to_be16(ETH_P_8021Q) ||
 | 
				
			||||||
 | 
						    skb->protocol == cpu_to_be16(ETH_P_8021AD)) {
 | 
				
			||||||
		skb = vlan_untag(skb);
 | 
							skb = vlan_untag(skb);
 | 
				
			||||||
		if (unlikely(!skb))
 | 
							if (unlikely(!skb))
 | 
				
			||||||
			goto unlock;
 | 
								goto unlock;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue