summaryrefslogtreecommitdiffstats
path: root/uClinux-2.4.20-uc1/drivers/net/ethertap.c
blob: 6bb635be09c9f6ae50a0a18c4430824d545bdd6f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
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
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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
/*
 *	Ethertap: A network device for bouncing packets via user space
 *
 *	This is a very simple ethernet driver. It bounces ethernet frames
 *	to user space on /dev/tap0->/dev/tap15 and expects ethernet frames
 *	to be written back to it. By default it does not ARP. If you turn ARP
 *	on it will attempt to ARP the user space and reply to ARPS from the
 *	user space.
 *
 *	As this is an ethernet device you can use it for appletalk, IPX etc
 *	even for building bridging tunnels.
 */
 
#include <linux/config.h>
#include <linux/module.h>

#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/errno.h>

#include <linux/netdevice.h>
#include <linux/inetdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/init.h>

#include <net/sock.h>
#include <linux/netlink.h>

/*
 *	Index to functions.
 */

int	    ethertap_probe(struct net_device *dev);
static int  ethertap_open(struct net_device *dev);
static int  ethertap_start_xmit(struct sk_buff *skb, struct net_device *dev);
static int  ethertap_close(struct net_device *dev);
static struct net_device_stats *ethertap_get_stats(struct net_device *dev);
static void ethertap_rx(struct sock *sk, int len);
#ifdef CONFIG_ETHERTAP_MC
static void set_multicast_list(struct net_device *dev);
#endif

static int ethertap_debug;

static struct net_device *tap_map[32];	/* Returns the tap device for a given netlink */

/*
 *	Board-specific info in dev->priv.
 */

struct net_local
{
	struct sock	*nl;
#ifdef CONFIG_ETHERTAP_MC
	__u32		groups;
#endif
	struct net_device_stats stats;
};

/*
 *	To call this a probe is a bit misleading, however for real
 *	hardware it would have to check what was present.
 */
 
int __init ethertap_probe(struct net_device *dev)
{
	SET_MODULE_OWNER(dev);

	memcpy(dev->dev_addr, "\xFE\xFD\x00\x00\x00\x00", 6);
	if (dev->mem_start & 0xf)
		ethertap_debug = dev->mem_start & 0x7;

	/*
	 *	Initialize the device structure.
	 */

	dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
	if (dev->priv == NULL)
		return -ENOMEM;
	memset(dev->priv, 0, sizeof(struct net_local));

	/*
	 *	The tap specific entries in the device structure.
	 */

	dev->open = ethertap_open;
	dev->hard_start_xmit = ethertap_start_xmit;
	dev->stop = ethertap_close;
	dev->get_stats = ethertap_get_stats;
#ifdef CONFIG_ETHERTAP_MC
	dev->set_multicast_list = set_multicast_list;
#endif

	/*
	 *	Setup the generic properties
	 */

	ether_setup(dev);

	dev->tx_queue_len = 0;
	dev->flags|=IFF_NOARP;
	tap_map[dev->base_addr]=dev;

	return 0;
}

/*
 *	Open/initialize the board.
 */

static int ethertap_open(struct net_device *dev)
{
	struct net_local *lp = (struct net_local*)dev->priv;

	if (ethertap_debug > 2)
		printk("%s: Doing ethertap_open()...", dev->name);

	lp->nl = netlink_kernel_create(dev->base_addr, ethertap_rx);
	if (lp->nl == NULL)
		return -ENOBUFS;
	netif_start_queue(dev);
	return 0;
}

#ifdef CONFIG_ETHERTAP_MC
static unsigned ethertap_mc_hash(__u8 *dest)
{
	unsigned idx = 0;
	idx ^= dest[0];
	idx ^= dest[1];
	idx ^= dest[2];
	idx ^= dest[3];
	idx ^= dest[4];
	idx ^= dest[5];
	return 1U << (idx&0x1F);
}

static void set_multicast_list(struct net_device *dev)
{
	unsigned groups = ~0;
	struct net_local *lp = (struct net_local *)dev->priv;

	if (!(dev->flags&(IFF_NOARP|IFF_PROMISC|IFF_ALLMULTI))) {
		struct dev_mc_list *dmi;

		groups = ethertap_mc_hash(dev->broadcast);

		for (dmi=dev->mc_list; dmi; dmi=dmi->next) {
			if (dmi->dmi_addrlen != 6)
				continue;
			groups |= ethertap_mc_hash(dmi->dmi_addr);
		}
	}
	lp->groups = groups;
	if (lp->nl)
		lp->nl->protinfo.af_netlink.groups = groups;
}
#endif

/*
 *	We transmit by throwing the packet at netlink. We have to clone
 *	it for 2.0 so that we dev_kfree_skb() the locked original.
 */
 
static int ethertap_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
	struct net_local *lp = (struct net_local *)dev->priv;
#ifdef CONFIG_ETHERTAP_MC
	struct ethhdr *eth = (struct ethhdr*)skb->data;
#endif

	if (skb_headroom(skb) < 2) {
		static int once;
	  	struct sk_buff *skb2;

		if (!once) {
			once = 1;
			printk(KERN_DEBUG "%s: not aligned xmit by protocol %04x\n", dev->name, skb->protocol);
		}

		skb2 = skb_realloc_headroom(skb, 2);
		dev_kfree_skb(skb);
		if (skb2 == NULL)
			return 0;
		skb = skb2;
	}
	__skb_push(skb, 2);

	/* Make the same thing, which loopback does. */
	if (skb_shared(skb)) {
	  	struct sk_buff *skb2 = skb;
	  	skb = skb_clone(skb, GFP_ATOMIC);	/* Clone the buffer */
	  	if (skb==NULL) {
			dev_kfree_skb(skb2);
			return 0;
		}
	  	dev_kfree_skb(skb2);
	}
	/* ... but do not orphan it here, netlink does it in any case. */

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

#ifndef CONFIG_ETHERTAP_MC
	netlink_broadcast(lp->nl, skb, 0, ~0, GFP_ATOMIC);
#else
	if (dev->flags&IFF_NOARP) {
		netlink_broadcast(lp->nl, skb, 0, ~0, GFP_ATOMIC);
		return 0;
	}

	if (!(eth->h_dest[0]&1)) {
		/* Unicast packet */
		__u32 pid;
		memcpy(&pid, eth->h_dest+2, 4);
		netlink_unicast(lp->nl, skb, ntohl(pid), MSG_DONTWAIT);
	} else
		netlink_broadcast(lp->nl, skb, 0, ethertap_mc_hash(eth->h_dest), GFP_ATOMIC);
#endif
	return 0;
}

static __inline__ int ethertap_rx_skb(struct sk_buff *skb, struct net_device *dev)
{
	struct net_local *lp = (struct net_local *)dev->priv;
#ifdef CONFIG_ETHERTAP_MC
	struct ethhdr *eth = (struct ethhdr*)(skb->data + 2);
#endif
	int len = skb->len;

	if (len < 16) {
		printk(KERN_DEBUG "%s : rx len = %d\n", dev->name, len);
		kfree_skb(skb);
		return -EINVAL;
	}
	if (NETLINK_CREDS(skb)->uid) {
		printk(KERN_INFO "%s : user %d\n", dev->name, NETLINK_CREDS(skb)->uid);
		kfree_skb(skb);
		return -EPERM;
	}

#ifdef CONFIG_ETHERTAP_MC
	if (!(dev->flags&(IFF_NOARP|IFF_PROMISC))) {
		int drop = 0;

		if (eth->h_dest[0]&1) {
			if (!(ethertap_mc_hash(eth->h_dest)&lp->groups))
				drop = 1;
		} else if (memcmp(eth->h_dest, dev->dev_addr, 6) != 0)
			drop = 1;

		if (drop) {
			if (ethertap_debug > 3)
				printk(KERN_DEBUG "%s : not for us\n", dev->name);
			kfree_skb(skb);
			return -EINVAL;
		}
	}
#endif

	if (skb_shared(skb)) {
	  	struct sk_buff *skb2 = skb;
	  	skb = skb_clone(skb, GFP_KERNEL);	/* Clone the buffer */
	  	if (skb==NULL) {
			kfree_skb(skb2);
			return -ENOBUFS;
		}
	  	kfree_skb(skb2);
	} else
		skb_orphan(skb);

	skb_pull(skb, 2);
	skb->dev = dev;
	skb->protocol=eth_type_trans(skb,dev);
	memset(skb->cb, 0, sizeof(skb->cb));
	lp->stats.rx_packets++;
	lp->stats.rx_bytes+=len;
	netif_rx(skb);
	dev->last_rx = jiffies;
	return len;
}

/*
 *	The typical workload of the driver:
 *	Handle the ether interface interrupts.
 *
 *	(In this case handle the packets posted from user space..)
 */

static void ethertap_rx(struct sock *sk, int len)
{
	struct net_device *dev = tap_map[sk->protocol];
	struct sk_buff *skb;

	if (dev==NULL) {
		printk(KERN_CRIT "ethertap: bad unit!\n");
		skb_queue_purge(&sk->receive_queue);
		return;
	}

	if (ethertap_debug > 3)
		printk("%s: ethertap_rx()\n", dev->name);

	while ((skb = skb_dequeue(&sk->receive_queue)) != NULL)
		ethertap_rx_skb(skb, dev);
}

static int ethertap_close(struct net_device *dev)
{
	struct net_local *lp = (struct net_local *)dev->priv;
	struct sock *sk = lp->nl;

	if (ethertap_debug > 2)
		printk("%s: Shutting down.\n", dev->name);

	netif_stop_queue(dev);

	if (sk) {
		lp->nl = NULL;
		sock_release(sk->socket);
	}

	return 0;
}

static struct net_device_stats *ethertap_get_stats(struct net_device *dev)
{
	struct net_local *lp = (struct net_local *)dev->priv;
	return &lp->stats;
}

#ifdef MODULE

static int unit;
MODULE_PARM(unit,"i");
MODULE_PARM_DESC(unit,"Ethertap device number");

static struct net_device dev_ethertap =
{
	" ",
	0, 0, 0, 0,
	1, 5,
	0, 0, 0, NULL, ethertap_probe
};

int init_module(void)
{
	dev_ethertap.base_addr=unit+NETLINK_TAPBASE;
	sprintf(dev_ethertap.name,"tap%d",unit);
	if (dev_get(dev_ethertap.name))
	{
		printk(KERN_INFO "%s already loaded.\n", dev_ethertap.name);
		return -EBUSY;
	}
	if (register_netdev(&dev_ethertap) != 0)
		return -EIO;
	return 0;
}

void cleanup_module(void)
{
	tap_map[dev_ethertap.base_addr]=NULL;
	unregister_netdev(&dev_ethertap);

	/*
	 *	Free up the private structure.
	 */

	kfree(dev_ethertap.priv);
	dev_ethertap.priv = NULL;	/* gets re-allocated by ethertap_probe */
}

#endif /* MODULE */
MODULE_LICENSE("GPL");