eth.c 10.6 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
3
4
5
6
7
8
9
/*
 * INET		An implementation of the TCP/IP protocol suite for the LINUX
 *		operating system.  INET is implemented using the  BSD Socket
 *		interface as the means of communication with the user level.
 *
 *		Ethernet-type device handling.
 *
 * Version:	@(#)eth.c	1.0.7	05/25/93
 *
10
 * Authors:	Ross Biro
Linus Torvalds's avatar
Linus Torvalds committed
11
12
13
14
 *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
 *		Mark Evans, <evansmp@uhura.aston.ac.uk>
 *		Florian  La Roche, <rzsfl@rz.uni-sb.de>
 *		Alan Cox, <gw4pts@gw4pts.ampr.org>
15
 *
Linus Torvalds's avatar
Linus Torvalds committed
16
17
18
19
20
21
 * Fixes:
 *		Mr Linux	: Arp problems
 *		Alan Cox	: Generic queue tidyup (very tiny here)
 *		Alan Cox	: eth_header ntohs should be htons
 *		Alan Cox	: eth_rebuild_header missing an htons and
 *				  minor other things.
22
 *		Tegge		: Arp bug fixes.
Linus Torvalds's avatar
Linus Torvalds committed
23
24
25
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
51
52
53
 *		Florian		: Removed many unnecessary functions, code cleanup
 *				  and changes for new arp and skbuff.
 *		Alan Cox	: Redid header building to reflect new format.
 *		Alan Cox	: ARP only when compiled with CONFIG_INET
 *		Greg Page	: 802.2 and SNAP stuff.
 *		Alan Cox	: MAC layer pointers/new format.
 *		Paul Gortmaker	: eth_copy_and_sum shouldn't csum padding.
 *		Alan Cox	: Protect against forwarding explosions with
 *				  older network drivers and IFF_ALLMULTI.
 *	Christer Weinigel	: Better rebuild header message.
 *             Andrew Morton    : 26Feb01: kill ether_setup() - use netdev_boot_setup().
 *
 *		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/types.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/inet.h>
#include <linux/ip.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/errno.h>
#include <linux/init.h>
54
#include <linux/if_ether.h>
Linus Torvalds's avatar
Linus Torvalds committed
55
56
57
58
59
#include <net/dst.h>
#include <net/arp.h>
#include <net/sock.h>
#include <net/ipv6.h>
#include <net/ip.h>
60
#include <net/dsa.h>
Linus Torvalds's avatar
Linus Torvalds committed
61
62
63
64
65
#include <asm/uaccess.h>
#include <asm/system.h>

__setup("ether=", netdev_boot_setup);

Stephen Hemminger's avatar
Stephen Hemminger committed
66
67
68
69
70
71
72
73
/**
 * eth_header - create the Ethernet header
 * @skb:	buffer to alter
 * @dev:	source device
 * @type:	Ethernet type field
 * @daddr: destination address (NULL leave destination address)
 * @saddr: source address (NULL use device source address)
 * @len:   packet length (<= skb->len)
Linus Torvalds's avatar
Linus Torvalds committed
74
 *
Stephen Hemminger's avatar
Stephen Hemminger committed
75
 *
Octavian Purdila's avatar
Octavian Purdila committed
76
77
 * Set the protocol type. For a packet of type ETH_P_802_3/2 we put the length
 * in here instead.
Linus Torvalds's avatar
Linus Torvalds committed
78
 */
79
80
81
int eth_header(struct sk_buff *skb, struct net_device *dev,
	       unsigned short type,
	       const void *daddr, const void *saddr, unsigned len)
Linus Torvalds's avatar
Linus Torvalds committed
82
{
83
	struct ethhdr *eth = (struct ethhdr *)skb_push(skb, ETH_HLEN);
Linus Torvalds's avatar
Linus Torvalds committed
84

Octavian Purdila's avatar
Octavian Purdila committed
85
	if (type != ETH_P_802_3 && type != ETH_P_802_2)
Linus Torvalds's avatar
Linus Torvalds committed
86
87
88
89
90
		eth->h_proto = htons(type);
	else
		eth->h_proto = htons(len);

	/*
91
	 *      Set the source hardware address.
Linus Torvalds's avatar
Linus Torvalds committed
92
	 */
93
94

	if (!saddr)
95
		saddr = dev->dev_addr;
96
	memcpy(eth->h_source, saddr, ETH_ALEN);
Linus Torvalds's avatar
Linus Torvalds committed
97

98
	if (daddr) {
99
		memcpy(eth->h_dest, daddr, ETH_ALEN);
100
101
		return ETH_HLEN;
	}
102

Linus Torvalds's avatar
Linus Torvalds committed
103
	/*
104
	 *      Anyway, the loopback-device should never use this function...
Linus Torvalds's avatar
Linus Torvalds committed
105
106
	 */

107
	if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) {
108
		memset(eth->h_dest, 0, ETH_ALEN);
Linus Torvalds's avatar
Linus Torvalds committed
109
110
		return ETH_HLEN;
	}
111

Linus Torvalds's avatar
Linus Torvalds committed
112
113
	return -ETH_HLEN;
}
114
EXPORT_SYMBOL(eth_header);
Linus Torvalds's avatar
Linus Torvalds committed
115

Stephen Hemminger's avatar
Stephen Hemminger committed
116
117
118
119
120
121
/**
 * eth_rebuild_header- rebuild the Ethernet MAC header.
 * @skb: socket buffer to update
 *
 * This is called after an ARP or IPV6 ndisc it's resolution on this
 * sk_buff. We now let protocol (ARP) fill in the other fields.
Linus Torvalds's avatar
Linus Torvalds committed
122
 *
Stephen Hemminger's avatar
Stephen Hemminger committed
123
124
 * This routine CANNOT use cached dst->neigh!
 * Really, it is used only when dst->neigh is wrong.
Linus Torvalds's avatar
Linus Torvalds committed
125
126
127
128
129
130
 */
int eth_rebuild_header(struct sk_buff *skb)
{
	struct ethhdr *eth = (struct ethhdr *)skb->data;
	struct net_device *dev = skb->dev;

131
	switch (eth->h_proto) {
Linus Torvalds's avatar
Linus Torvalds committed
132
#ifdef CONFIG_INET
133
	case htons(ETH_P_IP):
134
135
		return arp_find(eth->h_dest, skb);
#endif
Linus Torvalds's avatar
Linus Torvalds committed
136
137
	default:
		printk(KERN_DEBUG
138
		       "%s: unable to resolve type %X addresses.\n",
139
		       dev->name, ntohs(eth->h_proto));
140

141
		memcpy(eth->h_source, dev->dev_addr, ETH_ALEN);
Linus Torvalds's avatar
Linus Torvalds committed
142
143
144
145
146
		break;
	}

	return 0;
}
147
EXPORT_SYMBOL(eth_rebuild_header);
Linus Torvalds's avatar
Linus Torvalds committed
148

Stephen Hemminger's avatar
Stephen Hemminger committed
149
150
151
152
153
154
155
156
/**
 * eth_type_trans - determine the packet's protocol ID.
 * @skb: received socket data
 * @dev: receiving network device
 *
 * The rule here is that we
 * 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.
Linus Torvalds's avatar
Linus Torvalds committed
157
 */
158
__be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev)
Linus Torvalds's avatar
Linus Torvalds committed
159
160
{
	struct ethhdr *eth;
161

162
	skb->dev = dev;
163
	skb_reset_mac_header(skb);
164
	skb_pull_inline(skb, ETH_HLEN);
Linus Torvalds's avatar
Linus Torvalds committed
165
	eth = eth_hdr(skb);
166

167
168
	if (unlikely(is_multicast_ether_addr(eth->h_dest))) {
		if (!compare_ether_addr_64bits(eth->h_dest, dev->broadcast))
169
			skb->pkt_type = PACKET_BROADCAST;
Linus Torvalds's avatar
Linus Torvalds committed
170
		else
171
			skb->pkt_type = PACKET_MULTICAST;
Linus Torvalds's avatar
Linus Torvalds committed
172
	}
173

Linus Torvalds's avatar
Linus Torvalds committed
174
	/*
175
176
	 *      This ALLMULTI check should be redundant by 1.4
	 *      so don't forget to remove it.
Linus Torvalds's avatar
Linus Torvalds committed
177
	 *
178
179
	 *      Seems, you forgot to remove it. All silly devices
	 *      seems to set IFF_PROMISC.
Linus Torvalds's avatar
Linus Torvalds committed
180
	 */
181
182

	else if (1 /*dev->flags&IFF_PROMISC */ ) {
183
		if (unlikely(compare_ether_addr_64bits(eth->h_dest, dev->dev_addr)))
184
			skb->pkt_type = PACKET_OTHERHOST;
Linus Torvalds's avatar
Linus Torvalds committed
185
	}
186

187
188
189
190
191
192
193
194
	/*
	 * Some variants of DSA tagging don't have an ethertype field
	 * at all, so we check here whether one of those tagging
	 * variants has been configured on the receiving interface,
	 * and if so, set skb->protocol without looking at the packet.
	 */
	if (netdev_uses_dsa_tags(dev))
		return htons(ETH_P_DSA);
195
196
	if (netdev_uses_trailer_tags(dev))
		return htons(ETH_P_TRAILER);
197

Linus Torvalds's avatar
Linus Torvalds committed
198
199
	if (ntohs(eth->h_proto) >= 1536)
		return eth->h_proto;
200

Linus Torvalds's avatar
Linus Torvalds committed
201
	/*
202
203
204
205
	 *      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.
Linus Torvalds's avatar
Linus Torvalds committed
206
	 */
207
	if (skb->len >= 2 && *(unsigned short *)(skb->data) == 0xFFFF)
Linus Torvalds's avatar
Linus Torvalds committed
208
		return htons(ETH_P_802_3);
209

Linus Torvalds's avatar
Linus Torvalds committed
210
	/*
211
	 *      Real 802.2 LLC
Linus Torvalds's avatar
Linus Torvalds committed
212
213
214
	 */
	return htons(ETH_P_802_2);
}
215
EXPORT_SYMBOL(eth_type_trans);
Linus Torvalds's avatar
Linus Torvalds committed
216

Stephen Hemminger's avatar
Stephen Hemminger committed
217
218
219
220
221
/**
 * eth_header_parse - extract hardware address from packet
 * @skb: packet to extract header from
 * @haddr: destination buffer
 */
222
int eth_header_parse(const struct sk_buff *skb, unsigned char *haddr)
Linus Torvalds's avatar
Linus Torvalds committed
223
{
224
	const struct ethhdr *eth = eth_hdr(skb);
Linus Torvalds's avatar
Linus Torvalds committed
225
226
227
	memcpy(haddr, eth->h_source, ETH_ALEN);
	return ETH_ALEN;
}
228
EXPORT_SYMBOL(eth_header_parse);
Linus Torvalds's avatar
Linus Torvalds committed
229

Stephen Hemminger's avatar
Stephen Hemminger committed
230
231
232
233
234
235
/**
 * eth_header_cache - fill cache entry from neighbour
 * @neigh: source neighbour
 * @hh: destination cache entry
 * Create an Ethernet header template from the neighbour.
 */
236
int eth_header_cache(const struct neighbour *neigh, struct hh_cache *hh)
Linus Torvalds's avatar
Linus Torvalds committed
237
{
Al Viro's avatar
Al Viro committed
238
	__be16 type = hh->hh_type;
Linus Torvalds's avatar
Linus Torvalds committed
239
	struct ethhdr *eth;
240
	const struct net_device *dev = neigh->dev;
Linus Torvalds's avatar
Linus Torvalds committed
241

242
243
	eth = (struct ethhdr *)
	    (((u8 *) hh->hh_data) + (HH_DATA_OFF(sizeof(*eth))));
Linus Torvalds's avatar
Linus Torvalds committed
244

245
	if (type == htons(ETH_P_802_3))
Linus Torvalds's avatar
Linus Torvalds committed
246
247
248
		return -1;

	eth->h_proto = type;
249
250
	memcpy(eth->h_source, dev->dev_addr, ETH_ALEN);
	memcpy(eth->h_dest, neigh->ha, ETH_ALEN);
Linus Torvalds's avatar
Linus Torvalds committed
251
252
253
	hh->hh_len = ETH_HLEN;
	return 0;
}
254
EXPORT_SYMBOL(eth_header_cache);
Linus Torvalds's avatar
Linus Torvalds committed
255

Stephen Hemminger's avatar
Stephen Hemminger committed
256
257
258
259
260
261
/**
 * eth_header_cache_update - update cache entry
 * @hh: destination cache entry
 * @dev: network device
 * @haddr: new hardware address
 *
Linus Torvalds's avatar
Linus Torvalds committed
262
263
 * Called by Address Resolution module to notify changes in address.
 */
264
265
266
void eth_header_cache_update(struct hh_cache *hh,
			     const struct net_device *dev,
			     const unsigned char *haddr)
Linus Torvalds's avatar
Linus Torvalds committed
267
{
268
	memcpy(((u8 *) hh->hh_data) + HH_DATA_OFF(sizeof(struct ethhdr)),
269
	       haddr, ETH_ALEN);
Linus Torvalds's avatar
Linus Torvalds committed
270
}
271
EXPORT_SYMBOL(eth_header_cache_update);
Linus Torvalds's avatar
Linus Torvalds committed
272

Stephen Hemminger's avatar
Stephen Hemminger committed
273
274
275
276
277
278
279
280
281
/**
 * eth_mac_addr - set new Ethernet hardware address
 * @dev: network device
 * @p: socket address
 * Change hardware address of device.
 *
 * This doesn't change hardware matching, so needs to be overridden
 * for most real devices.
 */
282
int eth_mac_addr(struct net_device *dev, void *p)
Linus Torvalds's avatar
Linus Torvalds committed
283
{
284
	struct sockaddr *addr = p;
285

Linus Torvalds's avatar
Linus Torvalds committed
286
287
	if (netif_running(dev))
		return -EBUSY;
288
289
	if (!is_valid_ether_addr(addr->sa_data))
		return -EADDRNOTAVAIL;
290
	memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
Linus Torvalds's avatar
Linus Torvalds committed
291
292
	return 0;
}
293
EXPORT_SYMBOL(eth_mac_addr);
Linus Torvalds's avatar
Linus Torvalds committed
294

Stephen Hemminger's avatar
Stephen Hemminger committed
295
296
297
298
299
300
301
302
/**
 * eth_change_mtu - set new MTU size
 * @dev: network device
 * @new_mtu: new Maximum Transfer Unit
 *
 * Allow changing MTU size. Needs to be overridden for devices
 * supporting jumbo frames.
 */
303
int eth_change_mtu(struct net_device *dev, int new_mtu)
Linus Torvalds's avatar
Linus Torvalds committed
304
{
305
	if (new_mtu < 68 || new_mtu > ETH_DATA_LEN)
Linus Torvalds's avatar
Linus Torvalds committed
306
307
308
309
		return -EINVAL;
	dev->mtu = new_mtu;
	return 0;
}
310
EXPORT_SYMBOL(eth_change_mtu);
Linus Torvalds's avatar
Linus Torvalds committed
311

312
int eth_validate_addr(struct net_device *dev)
313
314
{
	if (!is_valid_ether_addr(dev->dev_addr))
315
		return -EADDRNOTAVAIL;
316
317
318

	return 0;
}
319
EXPORT_SYMBOL(eth_validate_addr);
320

321
322
323
324
325
326
327
328
const struct header_ops eth_header_ops ____cacheline_aligned = {
	.create		= eth_header,
	.parse		= eth_header_parse,
	.rebuild	= eth_rebuild_header,
	.cache		= eth_header_cache,
	.cache_update	= eth_header_cache_update,
};

Stephen Hemminger's avatar
Stephen Hemminger committed
329
330
331
332
/**
 * ether_setup - setup Ethernet network device
 * @dev: network device
 * Fill in the fields of the device structure with Ethernet-generic values.
Linus Torvalds's avatar
Linus Torvalds committed
333
334
335
 */
void ether_setup(struct net_device *dev)
{
336
	dev->header_ops		= &eth_header_ops;
Linus Torvalds's avatar
Linus Torvalds committed
337
338
	dev->type		= ARPHRD_ETHER;
	dev->hard_header_len 	= ETH_HLEN;
339
	dev->mtu		= ETH_DATA_LEN;
Linus Torvalds's avatar
Linus Torvalds committed
340
	dev->addr_len		= ETH_ALEN;
341
	dev->tx_queue_len	= 1000;	/* Ethernet wants good queues */
Linus Torvalds's avatar
Linus Torvalds committed
342
	dev->flags		= IFF_BROADCAST|IFF_MULTICAST;
343

344
	memset(dev->broadcast, 0xFF, ETH_ALEN);
Linus Torvalds's avatar
Linus Torvalds committed
345
346
347
348
349

}
EXPORT_SYMBOL(ether_setup);

/**
350
 * alloc_etherdev_mqs - Allocates and sets up an Ethernet device
Linus Torvalds's avatar
Linus Torvalds committed
351
 * @sizeof_priv: Size of additional driver-private structure to be allocated
Stephen Hemminger's avatar
Stephen Hemminger committed
352
 *	for this Ethernet device
353
 * @txqs: The number of TX queues this device has.
Randy Dunlap's avatar
Randy Dunlap committed
354
 * @rxqs: The number of RX queues this device has.
Linus Torvalds's avatar
Linus Torvalds committed
355
 *
Stephen Hemminger's avatar
Stephen Hemminger committed
356
 * Fill in the fields of the device structure with Ethernet-generic
Linus Torvalds's avatar
Linus Torvalds committed
357
358
359
 * values. Basically does everything except registering the device.
 *
 * Constructs a new net device, complete with a private data area of
Stephen Hemminger's avatar
Stephen Hemminger committed
360
 * size (sizeof_priv).  A 32-byte (not bit) alignment is enforced for
Linus Torvalds's avatar
Linus Torvalds committed
361
362
363
 * this private data area.
 */

364
365
struct net_device *alloc_etherdev_mqs(int sizeof_priv, unsigned int txqs,
				      unsigned int rxqs)
Linus Torvalds's avatar
Linus Torvalds committed
366
{
367
	return alloc_netdev_mqs(sizeof_priv, "eth%d", ether_setup, txqs, rxqs);
Linus Torvalds's avatar
Linus Torvalds committed
368
}
369
EXPORT_SYMBOL(alloc_etherdev_mqs);
370

371
static size_t _format_mac_addr(char *buf, int buflen,
372
			       const unsigned char *addr, int len)
373
{
374
375
376
377
378
379
380
	int i;
	char *cp = buf;

	for (i = 0; i < len; i++) {
		cp += scnprintf(cp, buflen - (cp - buf), "%02x", addr[i]);
		if (i == len - 1)
			break;
381
		cp += scnprintf(cp, buflen - (cp - buf), ":");
382
383
384
385
386
387
388
389
390
	}
	return cp - buf;
}

ssize_t sysfs_format_mac(char *buf, const unsigned char *addr, int len)
{
	size_t l;

	l = _format_mac_addr(buf, PAGE_SIZE, addr, len);
391
	l += scnprintf(buf + l, PAGE_SIZE - l, "\n");
Eric Dumazet's avatar
Eric Dumazet committed
392
	return (ssize_t)l;
393
394
}
EXPORT_SYMBOL(sysfs_format_mac);