vlan_core.c 4.3 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 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
/* Must be invoked with rcu_read_lock or with RTNL. */
struct net_device *__vlan_find_dev_deep(struct net_device *real_dev,
					u16 vlan_id)
{
	struct vlan_group *grp = rcu_dereference_rtnl(real_dev->vlgrp);

	if (grp) {
		return vlan_group_get_device(grp, vlan_id);
	} else {
		/*
		 * Bonding slaves do not have grp assigned to themselves.
		 * Grp is assigned to bonding master instead.
		 */
		if (netif_is_bond_slave(real_dev))
			return __vlan_find_dev_deep(real_dev->master, vlan_id);
	}

	return NULL;
}
EXPORT_SYMBOL(__vlan_find_dev_deep);

87 88 89 90
struct net_device *vlan_dev_real_dev(const struct net_device *dev)
{
	return vlan_dev_info(dev)->real_dev;
}
91
EXPORT_SYMBOL(vlan_dev_real_dev);
92 93 94 95 96

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

99
static struct sk_buff *vlan_reorder_header(struct sk_buff *skb)
100
{
101 102 103 104 105
	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);
106 107 108 109 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
	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);

166
	skb = vlan_reorder_header(skb);
167 168 169
	if (unlikely(!skb))
		goto err_free;

170 171
	skb_reset_network_header(skb);
	skb_reset_transport_header(skb);
172 173 174 175 176 177
	return skb;

err_free:
	kfree_skb(skb);
	return NULL;
}