vlan_core.c 4.4 KB
Newer Older
1 2 3
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/if_vlan.h>
4
#include <linux/netpoll.h>
5 6
#include "vlan.h"

7
bool vlan_do_receive(struct sk_buff **skbp)
8
{
9 10
	struct sk_buff *skb = *skbp;
	u16 vlan_id = skb->vlan_tci & VLAN_VID_MASK;
11
	struct net_device *vlan_dev;
E
Eric Dumazet 已提交
12
	struct vlan_pcpu_stats *rx_stats;
13

14 15 16 17 18
	vlan_dev = vlan_find_dev(skb->dev, vlan_id);
	if (!vlan_dev) {
		if (vlan_id)
			skb->pkt_type = PACKET_OTHERHOST;
		return false;
19
	}
20

21 22 23
	skb = *skbp = skb_share_check(skb, GFP_ATOMIC);
	if (unlikely(!skb))
		return false;
H
Herbert Xu 已提交
24

25
	skb->dev = vlan_dev;
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
	if (skb->pkt_type == PACKET_OTHERHOST) {
		/* Our lower layer thinks this is not local, let's make sure.
		 * This allows the VLAN to have a different MAC than the
		 * underlying device, and still route correctly. */
		if (!compare_ether_addr(eth_hdr(skb)->h_dest,
					vlan_dev->dev_addr))
			skb->pkt_type = PACKET_HOST;
	}

	if (!(vlan_dev_info(vlan_dev)->flags & VLAN_FLAG_REORDER_HDR)) {
		unsigned int offset = skb->data - skb_mac_header(skb);

		/*
		 * vlan_insert_tag expect skb->data pointing to mac header.
		 * So change skb->data before calling it and change back to
		 * original position later
		 */
		skb_push(skb, offset);
		skb = *skbp = vlan_insert_tag(skb, skb->vlan_tci);
		if (!skb)
			return false;
		skb_pull(skb, offset + VLAN_HLEN);
		skb_reset_mac_len(skb);
	}

51
	skb->priority = vlan_get_ingress_priority(vlan_dev, skb->vlan_tci);
52
	skb->vlan_tci = 0;
53

E
Eric Dumazet 已提交
54
	rx_stats = this_cpu_ptr(vlan_dev_info(vlan_dev)->vlan_pcpu_stats);
E
Eric Dumazet 已提交
55

E
Eric Dumazet 已提交
56
	u64_stats_update_begin(&rx_stats->syncp);
E
Eric Dumazet 已提交
57 58
	rx_stats->rx_packets++;
	rx_stats->rx_bytes += skb->len;
59
	if (skb->pkt_type == PACKET_MULTICAST)
E
Eric Dumazet 已提交
60 61
		rx_stats->rx_multicast++;
	u64_stats_update_end(&rx_stats->syncp);
62 63

	return true;
64
}
65 66 67 68 69

struct net_device *vlan_dev_real_dev(const struct net_device *dev)
{
	return vlan_dev_info(dev)->real_dev;
}
70
EXPORT_SYMBOL(vlan_dev_real_dev);
71 72 73 74 75

u16 vlan_dev_vlan_id(const struct net_device *dev)
{
	return vlan_dev_info(dev)->vlan_id;
}
76
EXPORT_SYMBOL(vlan_dev_vlan_id);
H
Herbert Xu 已提交
77

78 79 80
/* VLAN rx hw acceleration helper.  This acts like netif_{rx,receive_skb}(). */
int __vlan_hwaccel_rx(struct sk_buff *skb, struct vlan_group *grp,
		      u16 vlan_tci, int polling)
H
Herbert Xu 已提交
81
{
82
	__vlan_hwaccel_put_tag(skb, vlan_tci);
83
	return polling ? netif_receive_skb(skb) : netif_rx(skb);
H
Herbert Xu 已提交
84
}
85
EXPORT_SYMBOL(__vlan_hwaccel_rx);
H
Herbert Xu 已提交
86

87 88
gro_result_t vlan_gro_receive(struct napi_struct *napi, struct vlan_group *grp,
			      unsigned int vlan_tci, struct sk_buff *skb)
H
Herbert Xu 已提交
89
{
90 91
	__vlan_hwaccel_put_tag(skb, vlan_tci);
	return napi_gro_receive(napi, skb);
H
Herbert Xu 已提交
92 93 94
}
EXPORT_SYMBOL(vlan_gro_receive);

95 96
gro_result_t vlan_gro_frags(struct napi_struct *napi, struct vlan_group *grp,
			    unsigned int vlan_tci)
H
Herbert Xu 已提交
97
{
98 99
	__vlan_hwaccel_put_tag(napi->skb, vlan_tci);
	return napi_gro_frags(napi);
H
Herbert Xu 已提交
100 101
}
EXPORT_SYMBOL(vlan_gro_frags);
102

103
static struct sk_buff *vlan_reorder_header(struct sk_buff *skb)
104
{
105 106 107 108 109
	if (skb_cow(skb, skb_headroom(skb)) < 0)
		return NULL;
	memmove(skb->data - ETH_HLEN, skb->data - VLAN_ETH_HLEN, 2 * ETH_ALEN);
	skb->mac_header += VLAN_HLEN;
	skb_reset_mac_len(skb);
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
	return skb;
}

static void vlan_set_encap_proto(struct sk_buff *skb, struct vlan_hdr *vhdr)
{
	__be16 proto;
	unsigned char *rawp;

	/*
	 * Was a VLAN packet, grab the encapsulated protocol, which the layer
	 * three protocols care about.
	 */

	proto = vhdr->h_vlan_encapsulated_proto;
	if (ntohs(proto) >= 1536) {
		skb->protocol = proto;
		return;
	}

	rawp = skb->data;
	if (*(unsigned short *) rawp == 0xFFFF)
		/*
		 * This is a magic hack to spot IPX packets. Older Novell
		 * breaks the protocol design and runs IPX over 802.3 without
		 * an 802.2 LLC layer. We look for FFFF which isn't a used
		 * 802.2 SSAP/DSAP. This won't work for fault tolerant netware
		 * but does for the rest.
		 */
		skb->protocol = htons(ETH_P_802_3);
	else
		/*
		 * Real 802.2 LLC
		 */
		skb->protocol = htons(ETH_P_802_2);
}

struct sk_buff *vlan_untag(struct sk_buff *skb)
{
	struct vlan_hdr *vhdr;
	u16 vlan_tci;

	if (unlikely(vlan_tx_tag_present(skb))) {
		/* vlan_tci is already set-up so leave this for another time */
		return skb;
	}

	skb = skb_share_check(skb, GFP_ATOMIC);
	if (unlikely(!skb))
		goto err_free;

	if (unlikely(!pskb_may_pull(skb, VLAN_HLEN)))
		goto err_free;

	vhdr = (struct vlan_hdr *) skb->data;
	vlan_tci = ntohs(vhdr->h_vlan_TCI);
	__vlan_hwaccel_put_tag(skb, vlan_tci);

	skb_pull_rcsum(skb, VLAN_HLEN);
	vlan_set_encap_proto(skb, vhdr);

170
	skb = vlan_reorder_header(skb);
171 172 173 174 175 176 177 178 179
	if (unlikely(!skb))
		goto err_free;

	return skb;

err_free:
	kfree_skb(skb);
	return NULL;
}