vlan_dev.c 18.5 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
3
4
5
/* -*- linux-c -*-
 * INET		802.1Q VLAN
 *		Ethernet-type device handling.
 *
 * Authors:	Ben Greear <greearb@candelatech.com>
Patrick McHardy's avatar
Patrick McHardy committed
6
 *              Please send support related email to: netdev@vger.kernel.org
Linus Torvalds's avatar
Linus Torvalds committed
7
 *              VLAN Home Page: http://www.candelatech.com/~greear/vlan.html
8
 *
Linus Torvalds's avatar
Linus Torvalds committed
9
10
11
12
13
14
 * Fixes:       Mar 22 2001: Martin Bokaemper <mbokaemper@unispherenetworks.com>
 *                - reset skb->pkt_type on incoming packets when MAC was changed
 *                - see that changed MAC is saddr for outgoing packets
 *              Oct 20, 2001:  Ard van Breeman:
 *                - Fix MC-list, finally.
 *                - Flush MC-list on VLAN destroy.
15
 *
Linus Torvalds's avatar
Linus Torvalds committed
16
17
18
19
20
21
22
23
24
25
26
 *
 *		This program is free software; you can redistribute it and/or
 *		modify it under the terms of the GNU General Public License
 *		as published by the Free Software Foundation; either version
 *		2 of the License, or (at your option) any later version.
 */

#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
Patrick McHardy's avatar
Patrick McHardy committed
27
#include <linux/ethtool.h>
Linus Torvalds's avatar
Linus Torvalds committed
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <net/arp.h>

#include "vlan.h"
#include "vlanproc.h"
#include <linux/if_vlan.h>

/*
 *	Rebuild the Ethernet MAC header. This is called after an ARP
 *	(or in future other address resolution) has completed on this
 *	sk_buff. We now let ARP fill in the other fields.
 *
 *	This routine CANNOT use cached dst->neigh!
 *	Really, it is used only when dst->neigh is wrong.
 *
 * TODO:  This needs a checkup, I'm ignorant here. --BLG
 */
44
static int vlan_dev_rebuild_header(struct sk_buff *skb)
Linus Torvalds's avatar
Linus Torvalds committed
45
46
47
48
49
50
51
52
53
54
{
	struct net_device *dev = skb->dev;
	struct vlan_ethhdr *veth = (struct vlan_ethhdr *)(skb->data);

	switch (veth->h_vlan_encapsulated_proto) {
#ifdef CONFIG_INET
	case __constant_htons(ETH_P_IP):

		/* TODO:  Confirm this will work with VLAN headers... */
		return arp_find(veth->h_dest, skb);
55
#endif
Linus Torvalds's avatar
Linus Torvalds committed
56
	default:
57
58
		pr_debug("%s: unable to resolve type %X addresses.\n",
			 dev->name, ntohs(veth->h_vlan_encapsulated_proto));
59

Linus Torvalds's avatar
Linus Torvalds committed
60
61
		memcpy(veth->h_source, dev->dev_addr, ETH_ALEN);
		break;
62
	}
Linus Torvalds's avatar
Linus Torvalds committed
63
64
65
66
67
68

	return 0;
}

static inline struct sk_buff *vlan_check_reorder_header(struct sk_buff *skb)
{
69
	if (vlan_dev_info(skb->dev)->flags & VLAN_FLAG_REORDER_HDR) {
70
71
		if (skb_cow(skb, skb_headroom(skb)) < 0)
			skb = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
72
73
74
75
		if (skb) {
			/* Lifted from Gleb's VLAN code... */
			memmove(skb->data - ETH_HLEN,
				skb->data - VLAN_ETH_HLEN, 12);
76
			skb->mac_header += VLAN_HLEN;
Linus Torvalds's avatar
Linus Torvalds committed
77
78
79
80
81
82
		}
	}

	return skb;
}

83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
static inline 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);
}

Linus Torvalds's avatar
Linus Torvalds committed
117
/*
118
 *	Determine the packet's protocol ID. The rule here is that we
Linus Torvalds's avatar
Linus Torvalds committed
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
 *	assume 802.3 if the type field is short enough to be a length.
 *	This is normal practice and works for any 'now in use' protocol.
 *
 *  Also, at this point we assume that we ARE dealing exclusively with
 *  VLAN packets, or packets that should be made into VLAN packets based
 *  on a default VLAN ID.
 *
 *  NOTE:  Should be similar to ethernet/eth.c.
 *
 *  SANITY NOTE:  This method is called when a packet is moving up the stack
 *                towards userland.  To get here, it would have already passed
 *                through the ethernet/eth.c eth_type_trans() method.
 *  SANITY NOTE 2: We are referencing to the VLAN_HDR frields, which MAY be
 *                 stored UNALIGNED in the memory.  RISC systems don't like
 *                 such cases very much...
Patrick McHardy's avatar
Patrick McHardy committed
134
135
136
 *  SANITY NOTE 2a: According to Dave Miller & Alexey, it will always be
 *  		    aligned, so there doesn't need to be any of the unaligned
 *  		    stuff.  It has been commented out now...  --Ben
Linus Torvalds's avatar
Linus Torvalds committed
137
138
139
 *
 */
int vlan_skb_recv(struct sk_buff *skb, struct net_device *dev,
Patrick McHardy's avatar
Patrick McHardy committed
140
		  struct packet_type *ptype, struct net_device *orig_dev)
Linus Torvalds's avatar
Linus Torvalds committed
141
{
142
	struct vlan_hdr *vhdr;
Linus Torvalds's avatar
Linus Torvalds committed
143
	struct net_device_stats *stats;
144
145
	u16 vlan_id;
	u16 vlan_tci;
Linus Torvalds's avatar
Linus Torvalds committed
146

Patrick McHardy's avatar
Patrick McHardy committed
147
148
	skb = skb_share_check(skb, GFP_ATOMIC);
	if (skb == NULL)
149
		goto err_free;
150

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

154
	vhdr = (struct vlan_hdr *)skb->data;
155
156
	vlan_tci = ntohs(vhdr->h_vlan_TCI);
	vlan_id = vlan_tci & VLAN_VID_MASK;
Linus Torvalds's avatar
Linus Torvalds committed
157
158

	rcu_read_lock();
159
	skb->dev = __find_vlan_dev(dev, vlan_id);
Linus Torvalds's avatar
Linus Torvalds committed
160
	if (!skb->dev) {
Patrick McHardy's avatar
Patrick McHardy committed
161
		pr_debug("%s: ERROR: No net_device for VID: %u on dev: %s\n",
162
			 __func__, vlan_id, dev->name);
163
		goto err_unlock;
Linus Torvalds's avatar
Linus Torvalds committed
164
165
166
167
	}

	skb->dev->last_rx = jiffies;

Patrick McHardy's avatar
Patrick McHardy committed
168
	stats = &skb->dev->stats;
Linus Torvalds's avatar
Linus Torvalds committed
169
170
171
	stats->rx_packets++;
	stats->rx_bytes += skb->len;

172
	skb_pull_rcsum(skb, VLAN_HLEN);
173

174
	skb->priority = vlan_get_ingress_priority(skb->dev, vlan_tci);
Linus Torvalds's avatar
Linus Torvalds committed
175

176
	pr_debug("%s: priority: %u for TCI: %hu\n",
177
		 __func__, skb->priority, vlan_tci);
Linus Torvalds's avatar
Linus Torvalds committed
178
179
180

	switch (skb->pkt_type) {
	case PACKET_BROADCAST: /* Yeah, stats collect these together.. */
Patrick McHardy's avatar
Patrick McHardy committed
181
		/* stats->broadcast ++; // no such counter :-( */
Linus Torvalds's avatar
Linus Torvalds committed
182
183
184
185
186
187
		break;

	case PACKET_MULTICAST:
		stats->multicast++;
		break;

188
	case PACKET_OTHERHOST:
Linus Torvalds's avatar
Linus Torvalds committed
189
		/* Our lower layer thinks this is not local, let's make sure.
Patrick McHardy's avatar
Patrick McHardy committed
190
191
		 * This allows the VLAN to have a different MAC than the
		 * underlying device, and still route correctly.
Linus Torvalds's avatar
Linus Torvalds committed
192
		 */
Patrick McHardy's avatar
Patrick McHardy committed
193
194
		if (!compare_ether_addr(eth_hdr(skb)->h_dest,
					skb->dev->dev_addr))
Linus Torvalds's avatar
Linus Torvalds committed
195
196
197
198
			skb->pkt_type = PACKET_HOST;
		break;
	default:
		break;
199
	}
Linus Torvalds's avatar
Linus Torvalds committed
200

201
	vlan_set_encap_proto(skb, vhdr);
Linus Torvalds's avatar
Linus Torvalds committed
202
203

	skb = vlan_check_reorder_header(skb);
204
	if (!skb) {
Linus Torvalds's avatar
Linus Torvalds committed
205
		stats->rx_errors++;
206
		goto err_unlock;
Linus Torvalds's avatar
Linus Torvalds committed
207
	}
208
209

	netif_rx(skb);
Linus Torvalds's avatar
Linus Torvalds committed
210
	rcu_read_unlock();
211
212
213
214
215
216
217
	return NET_RX_SUCCESS;

err_unlock:
	rcu_read_unlock();
err_free:
	kfree_skb(skb);
	return NET_RX_DROP;
Linus Torvalds's avatar
Linus Torvalds committed
218
219
}

220
static inline u16
Patrick McHardy's avatar
Patrick McHardy committed
221
vlan_dev_get_egress_qos_mask(struct net_device *dev, struct sk_buff *skb)
Linus Torvalds's avatar
Linus Torvalds committed
222
{
Patrick McHardy's avatar
Patrick McHardy committed
223
	struct vlan_priority_tci_mapping *mp;
Linus Torvalds's avatar
Linus Torvalds committed
224

Patrick McHardy's avatar
Patrick McHardy committed
225
	mp = vlan_dev_info(dev)->egress_priority_map[(skb->priority & 0xF)];
Linus Torvalds's avatar
Linus Torvalds committed
226
227
	while (mp) {
		if (mp->priority == skb->priority) {
Patrick McHardy's avatar
Patrick McHardy committed
228
229
230
			return mp->vlan_qos; /* This should already be shifted
					      * to mask correctly with the
					      * VLAN's TCI */
Linus Torvalds's avatar
Linus Torvalds committed
231
232
233
234
235
236
237
		}
		mp = mp->next;
	}
	return 0;
}

/*
238
 *	Create the VLAN header for an arbitrary protocol layer
Linus Torvalds's avatar
Linus Torvalds committed
239
240
241
242
243
244
245
 *
 *	saddr=NULL	means use device source address
 *	daddr=NULL	means leave destination address (eg unresolved arp)
 *
 *  This is called when the SKB is moving down the stack towards the
 *  physical devices.
 */
246
247
248
249
static int vlan_dev_hard_header(struct sk_buff *skb, struct net_device *dev,
				unsigned short type,
				const void *daddr, const void *saddr,
				unsigned int len)
Linus Torvalds's avatar
Linus Torvalds committed
250
251
{
	struct vlan_hdr *vhdr;
252
	unsigned int vhdrlen = 0;
253
	u16 vlan_tci = 0;
254
	int rc;
Linus Torvalds's avatar
Linus Torvalds committed
255

256
257
258
	if (WARN_ON(skb_headroom(skb) < dev->hard_header_len))
		return -ENOSPC;

259
	if (!(vlan_dev_info(dev)->flags & VLAN_FLAG_REORDER_HDR)) {
Linus Torvalds's avatar
Linus Torvalds committed
260
261
		vhdr = (struct vlan_hdr *) skb_push(skb, VLAN_HLEN);

262
263
264
		vlan_tci = vlan_dev_info(dev)->vlan_id;
		vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb);
		vhdr->h_vlan_TCI = htons(vlan_tci);
Linus Torvalds's avatar
Linus Torvalds committed
265
266

		/*
Patrick McHardy's avatar
Patrick McHardy committed
267
268
269
		 *  Set the protocol type. For a packet of type ETH_P_802_3 we
		 *  put the length in here instead. It is up to the 802.2
		 *  layer to carry protocol information.
Linus Torvalds's avatar
Linus Torvalds committed
270
		 */
Patrick McHardy's avatar
Patrick McHardy committed
271
		if (type != ETH_P_802_3)
Linus Torvalds's avatar
Linus Torvalds committed
272
			vhdr->h_vlan_encapsulated_proto = htons(type);
Patrick McHardy's avatar
Patrick McHardy committed
273
		else
Linus Torvalds's avatar
Linus Torvalds committed
274
			vhdr->h_vlan_encapsulated_proto = htons(len);
275
276

		skb->protocol = htons(ETH_P_8021Q);
277
278
		type = ETH_P_8021Q;
		vhdrlen = VLAN_HLEN;
Linus Torvalds's avatar
Linus Torvalds committed
279
280
281
282
283
284
	}

	/* Before delegating work to the lower layer, enter our MAC-address */
	if (saddr == NULL)
		saddr = dev->dev_addr;

285
	/* Now make the underlying real hard header */
286
	dev = vlan_dev_info(dev)->real_dev;
287
288
289
	rc = dev_hard_header(skb, dev, type, daddr, saddr, len + vhdrlen);
	if (rc > 0)
		rc += vhdrlen;
Linus Torvalds's avatar
Linus Torvalds committed
290
291
292
	return rc;
}

293
static int vlan_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
Linus Torvalds's avatar
Linus Torvalds committed
294
{
Patrick McHardy's avatar
Patrick McHardy committed
295
	struct net_device_stats *stats = &dev->stats;
Linus Torvalds's avatar
Linus Torvalds committed
296
	struct vlan_ethhdr *veth = (struct vlan_ethhdr *)(skb->data);
297

Linus Torvalds's avatar
Linus Torvalds committed
298
299
300
301
302
	/* Handle non-VLAN frames if they are sent to us, for example by DHCP.
	 *
	 * NOTE: THIS ASSUMES DIX ETHERNET, SPECIFICALLY NOT SUPPORTING
	 * OTHER THINGS LIKE FDDI/TokenRing/802.3 SNAPs...
	 */
303
	if (veth->h_vlan_proto != htons(ETH_P_8021Q) ||
304
305
	    vlan_dev_info(dev)->flags & VLAN_FLAG_REORDER_HDR) {
		unsigned int orig_headroom = skb_headroom(skb);
306
		u16 vlan_tci;
Linus Torvalds's avatar
Linus Torvalds committed
307

308
		vlan_dev_info(dev)->cnt_encap_on_xmit++;
Linus Torvalds's avatar
Linus Torvalds committed
309

310
311
312
		vlan_tci = vlan_dev_info(dev)->vlan_id;
		vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb);
		skb = __vlan_put_tag(skb, vlan_tci);
Linus Torvalds's avatar
Linus Torvalds committed
313
314
		if (!skb) {
			stats->tx_dropped++;
315
			return NETDEV_TX_OK;
Linus Torvalds's avatar
Linus Torvalds committed
316
317
		}

Patrick McHardy's avatar
Patrick McHardy committed
318
		if (orig_headroom < VLAN_HLEN)
319
			vlan_dev_info(dev)->cnt_inc_headroom_on_tx++;
Linus Torvalds's avatar
Linus Torvalds committed
320
321
	}

322
	stats->tx_packets++;
Linus Torvalds's avatar
Linus Torvalds committed
323
324
	stats->tx_bytes += skb->len;

325
	skb->dev = vlan_dev_info(dev)->real_dev;
Linus Torvalds's avatar
Linus Torvalds committed
326
	dev_queue_xmit(skb);
327
	return NETDEV_TX_OK;
Linus Torvalds's avatar
Linus Torvalds committed
328
329
}

330
331
static int vlan_dev_hwaccel_hard_start_xmit(struct sk_buff *skb,
					    struct net_device *dev)
Linus Torvalds's avatar
Linus Torvalds committed
332
{
Patrick McHardy's avatar
Patrick McHardy committed
333
	struct net_device_stats *stats = &dev->stats;
334
	u16 vlan_tci;
Linus Torvalds's avatar
Linus Torvalds committed
335

336
337
338
	vlan_tci = vlan_dev_info(dev)->vlan_id;
	vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb);
	skb = __vlan_hwaccel_put_tag(skb, vlan_tci);
Linus Torvalds's avatar
Linus Torvalds committed
339
340
341
342

	stats->tx_packets++;
	stats->tx_bytes += skb->len;

343
	skb->dev = vlan_dev_info(dev)->real_dev;
Linus Torvalds's avatar
Linus Torvalds committed
344
	dev_queue_xmit(skb);
345
	return NETDEV_TX_OK;
Linus Torvalds's avatar
Linus Torvalds committed
346
347
}

348
static int vlan_dev_change_mtu(struct net_device *dev, int new_mtu)
Linus Torvalds's avatar
Linus Torvalds committed
349
350
351
352
{
	/* TODO: gotta make sure the underlying layer can handle it,
	 * maybe an IFF_VLAN_CAPABLE flag for devices?
	 */
353
	if (vlan_dev_info(dev)->real_dev->mtu < new_mtu)
Linus Torvalds's avatar
Linus Torvalds committed
354
355
356
357
358
359
360
		return -ERANGE;

	dev->mtu = new_mtu;

	return 0;
}

361
void vlan_dev_set_ingress_priority(const struct net_device *dev,
362
				   u32 skb_prio, u16 vlan_prio)
Linus Torvalds's avatar
Linus Torvalds committed
363
{
364
	struct vlan_dev_info *vlan = vlan_dev_info(dev);
365
366
367
368
369
370
371

	if (vlan->ingress_priority_map[vlan_prio & 0x7] && !skb_prio)
		vlan->nr_ingress_mappings--;
	else if (!vlan->ingress_priority_map[vlan_prio & 0x7] && skb_prio)
		vlan->nr_ingress_mappings++;

	vlan->ingress_priority_map[vlan_prio & 0x7] = skb_prio;
Linus Torvalds's avatar
Linus Torvalds committed
372
373
}

374
int vlan_dev_set_egress_priority(const struct net_device *dev,
375
				 u32 skb_prio, u16 vlan_prio)
Linus Torvalds's avatar
Linus Torvalds committed
376
{
377
	struct vlan_dev_info *vlan = vlan_dev_info(dev);
Linus Torvalds's avatar
Linus Torvalds committed
378
379
	struct vlan_priority_tci_mapping *mp = NULL;
	struct vlan_priority_tci_mapping *np;
380
	u32 vlan_qos = (vlan_prio << 13) & 0xE000;
381

382
	/* See if a priority mapping exists.. */
383
	mp = vlan->egress_priority_map[skb_prio & 0xF];
384
385
	while (mp) {
		if (mp->priority == skb_prio) {
386
387
388
389
390
			if (mp->vlan_qos && !vlan_qos)
				vlan->nr_egress_mappings--;
			else if (!mp->vlan_qos && vlan_qos)
				vlan->nr_egress_mappings++;
			mp->vlan_qos = vlan_qos;
391
			return 0;
Linus Torvalds's avatar
Linus Torvalds committed
392
		}
393
		mp = mp->next;
Linus Torvalds's avatar
Linus Torvalds committed
394
	}
395
396

	/* Create a new mapping then. */
397
	mp = vlan->egress_priority_map[skb_prio & 0xF];
398
399
400
401
402
403
	np = kmalloc(sizeof(struct vlan_priority_tci_mapping), GFP_KERNEL);
	if (!np)
		return -ENOBUFS;

	np->next = mp;
	np->priority = skb_prio;
404
405
406
407
	np->vlan_qos = vlan_qos;
	vlan->egress_priority_map[skb_prio & 0xF] = np;
	if (vlan_qos)
		vlan->nr_egress_mappings++;
408
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
409
410
}

411
/* Flags are defined in the vlan_flags enum in include/linux/if_vlan.h file. */
412
int vlan_dev_change_flags(const struct net_device *dev, u32 flags, u32 mask)
Linus Torvalds's avatar
Linus Torvalds committed
413
{
414
415
416
	struct vlan_dev_info *vlan = vlan_dev_info(dev);
	u32 old_flags = vlan->flags;

Patrick McHardy's avatar
Patrick McHardy committed
417
	if (mask & ~(VLAN_FLAG_REORDER_HDR | VLAN_FLAG_GVRP))
418
419
420
		return -EINVAL;

	vlan->flags = (old_flags & ~mask) | (flags & mask);
Patrick McHardy's avatar
Patrick McHardy committed
421
422
423
424
425
426
427

	if (netif_running(dev) && (vlan->flags ^ old_flags) & VLAN_FLAG_GVRP) {
		if (vlan->flags & VLAN_FLAG_GVRP)
			vlan_gvrp_request_join(dev);
		else
			vlan_gvrp_request_leave(dev);
	}
428
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
429
430
}

431
void vlan_dev_get_realdev_name(const struct net_device *dev, char *result)
Linus Torvalds's avatar
Linus Torvalds committed
432
{
433
	strncpy(result, vlan_dev_info(dev)->real_dev->name, 23);
Linus Torvalds's avatar
Linus Torvalds committed
434
435
}

436
static int vlan_dev_open(struct net_device *dev)
Linus Torvalds's avatar
Linus Torvalds committed
437
{
438
	struct vlan_dev_info *vlan = vlan_dev_info(dev);
439
440
441
442
	struct net_device *real_dev = vlan->real_dev;
	int err;

	if (!(real_dev->flags & IFF_UP))
Linus Torvalds's avatar
Linus Torvalds committed
443
444
		return -ENETDOWN;

445
446
447
	if (compare_ether_addr(dev->dev_addr, real_dev->dev_addr)) {
		err = dev_unicast_add(real_dev, dev->dev_addr, ETH_ALEN);
		if (err < 0)
448
			goto out;
449
450
	}

451
452
453
454
455
456
457
458
459
460
461
462
	if (dev->flags & IFF_ALLMULTI) {
		err = dev_set_allmulti(real_dev, 1);
		if (err < 0)
			goto del_unicast;
	}
	if (dev->flags & IFF_PROMISC) {
		err = dev_set_promiscuity(real_dev, 1);
		if (err < 0)
			goto clear_allmulti;
	}

	memcpy(vlan->real_dev_addr, real_dev->dev_addr, ETH_ALEN);
463

Patrick McHardy's avatar
Patrick McHardy committed
464
465
466
	if (vlan->flags & VLAN_FLAG_GVRP)
		vlan_gvrp_request_join(dev);

Linus Torvalds's avatar
Linus Torvalds committed
467
	return 0;
468
469
470
471
472
473
474
475
476

clear_allmulti:
	if (dev->flags & IFF_ALLMULTI)
		dev_set_allmulti(real_dev, -1);
del_unicast:
	if (compare_ether_addr(dev->dev_addr, real_dev->dev_addr))
		dev_unicast_delete(real_dev, dev->dev_addr, ETH_ALEN);
out:
	return err;
Linus Torvalds's avatar
Linus Torvalds committed
477
478
}

479
static int vlan_dev_stop(struct net_device *dev)
Linus Torvalds's avatar
Linus Torvalds committed
480
{
Patrick McHardy's avatar
Patrick McHardy committed
481
482
483
484
485
	struct vlan_dev_info *vlan = vlan_dev_info(dev);
	struct net_device *real_dev = vlan->real_dev;

	if (vlan->flags & VLAN_FLAG_GVRP)
		vlan_gvrp_request_leave(dev);
486

487
	dev_mc_unsync(real_dev, dev);
488
	dev_unicast_unsync(real_dev, dev);
489
490
491
492
493
	if (dev->flags & IFF_ALLMULTI)
		dev_set_allmulti(real_dev, -1);
	if (dev->flags & IFF_PROMISC)
		dev_set_promiscuity(real_dev, -1);

494
495
496
	if (compare_ether_addr(dev->dev_addr, real_dev->dev_addr))
		dev_unicast_delete(real_dev, dev->dev_addr, dev->addr_len);

Linus Torvalds's avatar
Linus Torvalds committed
497
498
499
	return 0;
}

500
static int vlan_dev_set_mac_address(struct net_device *dev, void *p)
501
{
502
	struct net_device *real_dev = vlan_dev_info(dev)->real_dev;
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
	struct sockaddr *addr = p;
	int err;

	if (!is_valid_ether_addr(addr->sa_data))
		return -EADDRNOTAVAIL;

	if (!(dev->flags & IFF_UP))
		goto out;

	if (compare_ether_addr(addr->sa_data, real_dev->dev_addr)) {
		err = dev_unicast_add(real_dev, addr->sa_data, ETH_ALEN);
		if (err < 0)
			return err;
	}

	if (compare_ether_addr(dev->dev_addr, real_dev->dev_addr))
		dev_unicast_delete(real_dev, dev->dev_addr, ETH_ALEN);

out:
	memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
	return 0;
}

526
static int vlan_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
Linus Torvalds's avatar
Linus Torvalds committed
527
{
528
	struct net_device *real_dev = vlan_dev_info(dev)->real_dev;
Linus Torvalds's avatar
Linus Torvalds committed
529
530
531
532
533
534
	struct ifreq ifrr;
	int err = -EOPNOTSUPP;

	strncpy(ifrr.ifr_name, real_dev->name, IFNAMSIZ);
	ifrr.ifr_ifru = ifr->ifr_ifru;

Patrick McHardy's avatar
Patrick McHardy committed
535
	switch (cmd) {
Linus Torvalds's avatar
Linus Torvalds committed
536
537
538
	case SIOCGMIIPHY:
	case SIOCGMIIREG:
	case SIOCSMIIREG:
539
		if (real_dev->do_ioctl && netif_device_present(real_dev))
Linus Torvalds's avatar
Linus Torvalds committed
540
541
542
543
			err = real_dev->do_ioctl(real_dev, &ifrr, cmd);
		break;
	}

544
	if (!err)
Linus Torvalds's avatar
Linus Torvalds committed
545
546
547
548
549
		ifr->ifr_ifru = ifrr.ifr_ifru;

	return err;
}

550
static void vlan_dev_change_rx_flags(struct net_device *dev, int change)
551
{
552
	struct net_device *real_dev = vlan_dev_info(dev)->real_dev;
553
554
555
556
557
558
559

	if (change & IFF_ALLMULTI)
		dev_set_allmulti(real_dev, dev->flags & IFF_ALLMULTI ? 1 : -1);
	if (change & IFF_PROMISC)
		dev_set_promiscuity(real_dev, dev->flags & IFF_PROMISC ? 1 : -1);
}

560
static void vlan_dev_set_rx_mode(struct net_device *vlan_dev)
Linus Torvalds's avatar
Linus Torvalds committed
561
{
562
	dev_mc_sync(vlan_dev_info(vlan_dev)->real_dev, vlan_dev);
563
	dev_unicast_sync(vlan_dev_info(vlan_dev)->real_dev, vlan_dev);
Linus Torvalds's avatar
Linus Torvalds committed
564
}
565
566
567
568
569
570
571
572

/*
 * vlan network devices have devices nesting below it, and are a special
 * "super class" of normal network devices; split their locks off into a
 * separate class since they always nest.
 */
static struct lock_class_key vlan_netdev_xmit_lock_key;

573
574
575
576
577
578
579
580
581
582
583
584
static void vlan_dev_set_lockdep_one(struct netdev_queue *txq,
				     int subclass)
{
	lockdep_set_class_and_subclass(&txq->_xmit_lock,
				       &vlan_netdev_xmit_lock_key, subclass);
}

static void vlan_dev_set_lockdep_class(struct net_device *dev, int subclass)
{
	vlan_dev_set_lockdep_one(&dev->tx_queue, subclass);
}

585
586
587
588
589
590
591
592
static const struct header_ops vlan_header_ops = {
	.create	 = vlan_dev_hard_header,
	.rebuild = vlan_dev_rebuild_header,
	.parse	 = eth_header_parse,
};

static int vlan_dev_init(struct net_device *dev)
{
593
	struct net_device *real_dev = vlan_dev_info(dev)->real_dev;
594
595
596
	int subclass = 0;

	/* IFF_BROADCAST|IFF_MULTICAST; ??? */
597
	dev->flags  = real_dev->flags & ~(IFF_UP | IFF_PROMISC | IFF_ALLMULTI);
598
599
600
601
602
	dev->iflink = real_dev->ifindex;
	dev->state  = (real_dev->state & ((1<<__LINK_STATE_NOCARRIER) |
					  (1<<__LINK_STATE_DORMANT))) |
		      (1<<__LINK_STATE_PRESENT);

603
	dev->features |= real_dev->features & real_dev->vlan_features;
604

605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
	/* ipv6 shared card related stuff */
	dev->dev_id = real_dev->dev_id;

	if (is_zero_ether_addr(dev->dev_addr))
		memcpy(dev->dev_addr, real_dev->dev_addr, dev->addr_len);
	if (is_zero_ether_addr(dev->broadcast))
		memcpy(dev->broadcast, real_dev->broadcast, dev->addr_len);

	if (real_dev->features & NETIF_F_HW_VLAN_TX) {
		dev->header_ops      = real_dev->header_ops;
		dev->hard_header_len = real_dev->hard_header_len;
		dev->hard_start_xmit = vlan_dev_hwaccel_hard_start_xmit;
	} else {
		dev->header_ops      = &vlan_header_ops;
		dev->hard_header_len = real_dev->hard_header_len + VLAN_HLEN;
		dev->hard_start_xmit = vlan_dev_hard_start_xmit;
	}

Joonwoo Park's avatar
Joonwoo Park committed
623
	if (is_vlan_dev(real_dev))
624
625
		subclass = 1;

626
	vlan_dev_set_lockdep_class(dev, subclass);
627
628
629
	return 0;
}

630
631
632
633
634
635
636
637
638
639
640
641
642
643
static void vlan_dev_uninit(struct net_device *dev)
{
	struct vlan_priority_tci_mapping *pm;
	struct vlan_dev_info *vlan = vlan_dev_info(dev);
	int i;

	for (i = 0; i < ARRAY_SIZE(vlan->egress_priority_map); i++) {
		while ((pm = vlan->egress_priority_map[i]) != NULL) {
			vlan->egress_priority_map[i] = pm->next;
			kfree(pm);
		}
	}
}

Patrick McHardy's avatar
Patrick McHardy committed
644
645
646
647
648
649
650
651
652
653
654
static u32 vlan_ethtool_get_rx_csum(struct net_device *dev)
{
	const struct vlan_dev_info *vlan = vlan_dev_info(dev);
	struct net_device *real_dev = vlan->real_dev;

	if (real_dev->ethtool_ops == NULL ||
	    real_dev->ethtool_ops->get_rx_csum == NULL)
		return 0;
	return real_dev->ethtool_ops->get_rx_csum(real_dev);
}

655
656
657
658
659
660
661
662
663
664
665
666
static u32 vlan_ethtool_get_flags(struct net_device *dev)
{
	const struct vlan_dev_info *vlan = vlan_dev_info(dev);
	struct net_device *real_dev = vlan->real_dev;

	if (!(real_dev->features & NETIF_F_HW_VLAN_RX) ||
	    real_dev->ethtool_ops == NULL ||
	    real_dev->ethtool_ops->get_flags == NULL)
		return 0;
	return real_dev->ethtool_ops->get_flags(real_dev);
}

Patrick McHardy's avatar
Patrick McHardy committed
667
668
669
static const struct ethtool_ops vlan_ethtool_ops = {
	.get_link		= ethtool_op_get_link,
	.get_rx_csum		= vlan_ethtool_get_rx_csum,
670
	.get_flags		= vlan_ethtool_get_flags,
Patrick McHardy's avatar
Patrick McHardy committed
671
672
};

673
674
675
676
677
678
679
680
681
void vlan_setup(struct net_device *dev)
{
	ether_setup(dev);

	dev->priv_flags		|= IFF_802_1Q_VLAN;
	dev->tx_queue_len	= 0;

	dev->change_mtu		= vlan_dev_change_mtu;
	dev->init		= vlan_dev_init;
682
	dev->uninit		= vlan_dev_uninit;
683
684
685
	dev->open		= vlan_dev_open;
	dev->stop		= vlan_dev_stop;
	dev->set_mac_address	= vlan_dev_set_mac_address;
686
687
	dev->set_rx_mode	= vlan_dev_set_rx_mode;
	dev->set_multicast_list	= vlan_dev_set_rx_mode;
688
689
690
	dev->change_rx_flags	= vlan_dev_change_rx_flags;
	dev->do_ioctl		= vlan_dev_ioctl;
	dev->destructor		= free_netdev;
Patrick McHardy's avatar
Patrick McHardy committed
691
	dev->ethtool_ops	= &vlan_ethtool_ops;
692
693
694

	memset(dev->broadcast, 0, ETH_ALEN);
}