Blame view

drivers/net/wan/dlci.c 10.9 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
  /*
   * DLCI		Implementation of Frame Relay protocol for Linux, according to
   *		RFC 1490.  This generic device provides en/decapsulation for an
   *		underlying hardware driver.  Routes & IPs are assigned to these
   *		interfaces.  Requires 'dlcicfg' program to create usable 
   *		interfaces, the initial one, 'dlci' is for IOCTL use only.
   *
   * Version:	@(#)dlci.c	0.35	4 Jan 1997
   *
   * Author:	Mike McLagan <mike.mclagan@linux.org>
   *
   * Changes:
   *
   *		0.15	Mike Mclagan	Packet freeing, bug in kmalloc call
   *					DLCI_RET handling
   *		0.20	Mike McLagan	More conservative on which packets
   *					are returned for retry and which are
   *					are dropped.  If DLCI_RET_DROP is
   *					returned from the FRAD, the packet is
   *				 	sent back to Linux for re-transmission
   *		0.25	Mike McLagan	Converted to use SIOC IOCTL calls
   *		0.30	Jim Freeman	Fixed to allow IPX traffic
   *		0.35	Michael Elizabeth	Fixed incorrect memcpy_fromfs
   *
   *		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.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
  #include <linux/module.h>
  #include <linux/kernel.h>
  #include <linux/types.h>
  #include <linux/fcntl.h>
  #include <linux/interrupt.h>
  #include <linux/ptrace.h>
  #include <linux/ioport.h>
  #include <linux/in.h>
  #include <linux/init.h>
  #include <linux/slab.h>
  #include <linux/string.h>
  #include <linux/errno.h>
  #include <linux/netdevice.h>
  #include <linux/skbuff.h>
  #include <linux/if_arp.h>
  #include <linux/if_frad.h>
  #include <linux/bitops.h>
  
  #include <net/sock.h>
  
  #include <asm/system.h>
  #include <asm/io.h>
  #include <asm/dma.h>
  #include <asm/uaccess.h>
  
  static const char version[] = "DLCI driver v0.35, 4 Jan 1997, mike.mclagan@linux.org";
  
  static LIST_HEAD(dlci_devs);
  
  static void dlci_setup(struct net_device *);
  
  /* 
   * these encapsulate the RFC 1490 requirements as well as 
   * deal with packet transmission and reception, working with
   * the upper network layers 
   */
  
  static int dlci_header(struct sk_buff *skb, struct net_device *dev, 
3b04ddde0   Stephen Hemminger   [NET]: Move hardw...
68
69
  		       unsigned short type, const void *daddr,
  		       const void *saddr, unsigned len)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70
71
72
73
74
  {
  	struct frhdr		hdr;
  	struct dlci_local	*dlp;
  	unsigned int		hlen;
  	char			*dest;
8f15ea42b   Wang Chen   netdevice: safe c...
75
  	dlp = netdev_priv(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
76
77
  
  	hdr.control = FRAD_I_UI;
48b3d3efb   Rudy Matela   wan: Frame Relay/...
78
  	switch (type)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
  	{
  		case ETH_P_IP:
  			hdr.IP_NLPID = FRAD_P_IP;
  			hlen = sizeof(hdr.control) + sizeof(hdr.IP_NLPID);
  			break;
  
  		/* feel free to add other types, if necessary */
  
  		default:
  			hdr.pad = FRAD_P_PADDING;
  			hdr.NLPID = FRAD_P_SNAP;
  			memset(hdr.OUI, 0, sizeof(hdr.OUI));
  			hdr.PID = htons(type);
  			hlen = sizeof(hdr);
  			break;
  	}
  
  	dest = skb_push(skb, hlen);
  	if (!dest)
  		return(0);
  
  	memcpy(dest, &hdr, hlen);
  
  	return(hlen);
  }
  
  static void dlci_receive(struct sk_buff *skb, struct net_device *dev)
  {
  	struct dlci_local *dlp;
  	struct frhdr		*hdr;
  	int					process, header;
8f15ea42b   Wang Chen   netdevice: safe c...
110
  	dlp = netdev_priv(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
111
112
113
114
  	if (!pskb_may_pull(skb, sizeof(*hdr))) {
  		printk(KERN_NOTICE "%s: invalid data no header
  ",
  		       dev->name);
07d117cf0   Stephen Hemminger   dlci: convert to ...
115
  		dev->stats.rx_errors++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
116
117
118
119
120
121
122
123
124
125
126
127
128
  		kfree_skb(skb);
  		return;
  	}
  
  	hdr = (struct frhdr *) skb->data;
  	process = 0;
  	header = 0;
  	skb->dev = dev;
  
  	if (hdr->control != FRAD_I_UI)
  	{
  		printk(KERN_NOTICE "%s: Invalid header flag 0x%02X.
  ", dev->name, hdr->control);
07d117cf0   Stephen Hemminger   dlci: convert to ...
129
  		dev->stats.rx_errors++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
130
131
  	}
  	else
48b3d3efb   Rudy Matela   wan: Frame Relay/...
132
  		switch (hdr->IP_NLPID)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
133
134
135
136
137
138
  		{
  			case FRAD_P_PADDING:
  				if (hdr->NLPID != FRAD_P_SNAP)
  				{
  					printk(KERN_NOTICE "%s: Unsupported NLPID 0x%02X.
  ", dev->name, hdr->NLPID);
07d117cf0   Stephen Hemminger   dlci: convert to ...
139
  					dev->stats.rx_errors++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
140
141
142
143
144
145
146
  					break;
  				}
  	 
  				if (hdr->OUI[0] + hdr->OUI[1] + hdr->OUI[2] != 0)
  				{
  					printk(KERN_NOTICE "%s: Unsupported organizationally unique identifier 0x%02X-%02X-%02X.
  ", dev->name, hdr->OUI[0], hdr->OUI[1], hdr->OUI[2]);
07d117cf0   Stephen Hemminger   dlci: convert to ...
147
  					dev->stats.rx_errors++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
  					break;
  				}
  
  				/* at this point, it's an EtherType frame */
  				header = sizeof(struct frhdr);
  				/* Already in network order ! */
  				skb->protocol = hdr->PID;
  				process = 1;
  				break;
  
  			case FRAD_P_IP:
  				header = sizeof(hdr->control) + sizeof(hdr->IP_NLPID);
  				skb->protocol = htons(ETH_P_IP);
  				process = 1;
  				break;
  
  			case FRAD_P_SNAP:
  			case FRAD_P_Q933:
  			case FRAD_P_CLNP:
  				printk(KERN_NOTICE "%s: Unsupported NLPID 0x%02X.
  ", dev->name, hdr->pad);
07d117cf0   Stephen Hemminger   dlci: convert to ...
169
  				dev->stats.rx_errors++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
170
171
172
173
174
  				break;
  
  			default:
  				printk(KERN_NOTICE "%s: Invalid pad byte 0x%02X.
  ", dev->name, hdr->pad);
07d117cf0   Stephen Hemminger   dlci: convert to ...
175
  				dev->stats.rx_errors++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
176
177
178
179
180
181
  				break;				
  		}
  
  	if (process)
  	{
  		/* we've set up the protocol, so discard the header */
459a98ed8   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
182
  		skb_reset_mac_header(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
183
  		skb_pull(skb, header);
07d117cf0   Stephen Hemminger   dlci: convert to ...
184
  		dev->stats.rx_bytes += skb->len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
185
  		netif_rx(skb);
07d117cf0   Stephen Hemminger   dlci: convert to ...
186
  		dev->stats.rx_packets++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
187
188
189
190
  	}
  	else
  		dev_kfree_skb(skb);
  }
384824281   Stephen Hemminger   wan: dlci/sdla tr...
191
  static netdev_tx_t dlci_transmit(struct sk_buff *skb, struct net_device *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
192
  {
384824281   Stephen Hemminger   wan: dlci/sdla tr...
193
  	struct dlci_local *dlp = netdev_priv(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
194

384824281   Stephen Hemminger   wan: dlci/sdla tr...
195
196
197
  	if (skb)
  		dlp->slave->netdev_ops->ndo_start_xmit(skb, dlp->slave);
  	return NETDEV_TX_OK;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
198
199
200
201
202
203
204
205
  }
  
  static int dlci_config(struct net_device *dev, struct dlci_conf __user *conf, int get)
  {
  	struct dlci_conf	config;
  	struct dlci_local	*dlp;
  	struct frad_local	*flp;
  	int			err;
8f15ea42b   Wang Chen   netdevice: safe c...
206
  	dlp = netdev_priv(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
207

8f15ea42b   Wang Chen   netdevice: safe c...
208
  	flp = netdev_priv(dlp->slave);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
209
210
211
  
  	if (!get)
  	{
48b3d3efb   Rudy Matela   wan: Frame Relay/...
212
  		if (copy_from_user(&config, conf, sizeof(struct dlci_conf)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
213
214
215
216
217
218
219
220
221
222
223
224
225
  			return -EFAULT;
  		if (config.flags & ~DLCI_VALID_FLAGS)
  			return(-EINVAL);
  		memcpy(&dlp->config, &config, sizeof(struct dlci_conf));
  		dlp->configured = 1;
  	}
  
  	err = (*flp->dlci_conf)(dlp->slave, dev, get);
  	if (err)
  		return(err);
  
  	if (get)
  	{
48b3d3efb   Rudy Matela   wan: Frame Relay/...
226
  		if (copy_to_user(conf, &dlp->config, sizeof(struct dlci_conf)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
227
228
229
230
231
232
233
234
235
236
237
238
  			return -EFAULT;
  	}
  
  	return(0);
  }
  
  static int dlci_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
  {
  	struct dlci_local *dlp;
  
  	if (!capable(CAP_NET_ADMIN))
  		return(-EPERM);
8f15ea42b   Wang Chen   netdevice: safe c...
239
  	dlp = netdev_priv(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
240

48b3d3efb   Rudy Matela   wan: Frame Relay/...
241
  	switch (cmd)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
  	{
  		case DLCI_GET_SLAVE:
  			if (!*(short *)(dev->dev_addr))
  				return(-EINVAL);
  
  			strncpy(ifr->ifr_slave, dlp->slave->name, sizeof(ifr->ifr_slave));
  			break;
  
  		case DLCI_GET_CONF:
  		case DLCI_SET_CONF:
  			if (!*(short *)(dev->dev_addr))
  				return(-EINVAL);
  
  			return(dlci_config(dev, ifr->ifr_data, cmd == DLCI_GET_CONF));
  			break;
  
  		default: 
  			return(-EOPNOTSUPP);
  	}
  	return(0);
  }
  
  static int dlci_change_mtu(struct net_device *dev, int new_mtu)
  {
49e8abaf6   Stephen Hemminger   dlci: convert to ...
266
  	struct dlci_local *dlp = netdev_priv(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
267

49e8abaf6   Stephen Hemminger   dlci: convert to ...
268
  	return dev_set_mtu(dlp->slave, new_mtu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
269
270
271
272
273
274
275
  }
  
  static int dlci_open(struct net_device *dev)
  {
  	struct dlci_local	*dlp;
  	struct frad_local	*flp;
  	int			err;
8f15ea42b   Wang Chen   netdevice: safe c...
276
  	dlp = netdev_priv(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
277
278
279
280
281
282
  
  	if (!*(short *)(dev->dev_addr))
  		return(-EINVAL);
  
  	if (!netif_running(dlp->slave))
  		return(-ENOTCONN);
8f15ea42b   Wang Chen   netdevice: safe c...
283
  	flp = netdev_priv(dlp->slave);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
  	err = (*flp->activate)(dlp->slave, dev);
  	if (err)
  		return(err);
  
  	netif_start_queue(dev);
  
  	return 0;
  }
  
  static int dlci_close(struct net_device *dev)
  {
  	struct dlci_local	*dlp;
  	struct frad_local	*flp;
  	int			err;
  
  	netif_stop_queue(dev);
8f15ea42b   Wang Chen   netdevice: safe c...
300
  	dlp = netdev_priv(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
301

8f15ea42b   Wang Chen   netdevice: safe c...
302
  	flp = netdev_priv(dlp->slave);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
303
304
305
306
  	err = (*flp->deactivate)(dlp->slave, dev);
  
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
307
308
309
310
311
312
313
314
315
  static int dlci_add(struct dlci_add *dlci)
  {
  	struct net_device	*master, *slave;
  	struct dlci_local	*dlp;
  	struct frad_local	*flp;
  	int			err = -EINVAL;
  
  
  	/* validate slave device */
881d966b4   Eric W. Biederman   [NET]: Make the d...
316
  	slave = dev_get_by_name(&init_net, dlci->devname);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
317
318
  	if (!slave)
  		return -ENODEV;
8f15ea42b   Wang Chen   netdevice: safe c...
319
  	if (slave->type != ARPHRD_FRAD || netdev_priv(slave) == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
  		goto err1;
  
  	/* create device name */
  	master = alloc_netdev( sizeof(struct dlci_local), "dlci%d",
  			      dlci_setup);
  	if (!master) {
  		err = -ENOMEM;
  		goto err1;
  	}
  
  	/* make sure same slave not already registered */
  	rtnl_lock();
  	list_for_each_entry(dlp, &dlci_devs, list) {
  		if (dlp->slave == slave) {
  			err = -EBUSY;
  			goto err2;
  		}
  	}
  
  	err = dev_alloc_name(master, master->name);
  	if (err < 0)
  		goto err2;
  
  	*(short *)(master->dev_addr) = dlci->dlci;
8f15ea42b   Wang Chen   netdevice: safe c...
344
  	dlp = netdev_priv(master);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
345
346
  	dlp->slave = slave;
  	dlp->master = master;
8f15ea42b   Wang Chen   netdevice: safe c...
347
  	flp = netdev_priv(slave);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
  	err = (*flp->assoc)(slave, master);
  	if (err < 0)
  		goto err2;
  
  	err = register_netdevice(master);
  	if (err < 0) 
  		goto err2;
  
  	strcpy(dlci->devname, master->name);
  
  	list_add(&dlp->list, &dlci_devs);
  	rtnl_unlock();
  
  	return(0);
  
   err2:
  	rtnl_unlock();
  	free_netdev(master);
   err1:
  	dev_put(slave);
  	return(err);
  }
  
  static int dlci_del(struct dlci_add *dlci)
  {
  	struct dlci_local	*dlp;
  	struct frad_local	*flp;
  	struct net_device	*master, *slave;
  	int			err;
  
  	/* validate slave device */
881d966b4   Eric W. Biederman   [NET]: Make the d...
379
  	master = __dev_get_by_name(&init_net, dlci->devname);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
380
381
382
383
384
385
  	if (!master)
  		return(-ENODEV);
  
  	if (netif_running(master)) {
  		return(-EBUSY);
  	}
8f15ea42b   Wang Chen   netdevice: safe c...
386
  	dlp = netdev_priv(master);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
387
  	slave = dlp->slave;
8f15ea42b   Wang Chen   netdevice: safe c...
388
  	flp = netdev_priv(slave);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
  
  	rtnl_lock();
  	err = (*flp->deassoc)(slave, master);
  	if (!err) {
  		list_del(&dlp->list);
  
  		unregister_netdevice(master);
  
  		dev_put(slave);
  	}
  	rtnl_unlock();
  
  	return(err);
  }
  
  static int dlci_ioctl(unsigned int cmd, void __user *arg)
  {
  	struct dlci_add add;
  	int err;
  	
  	if (!capable(CAP_NET_ADMIN))
  		return(-EPERM);
48b3d3efb   Rudy Matela   wan: Frame Relay/...
411
  	if (copy_from_user(&add, arg, sizeof(struct dlci_add)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
412
413
414
415
416
417
418
419
  		return -EFAULT;
  
  	switch (cmd)
  	{
  		case SIOCADDDLCI:
  			err = dlci_add(&add);
  
  			if (!err)
48b3d3efb   Rudy Matela   wan: Frame Relay/...
420
  				if (copy_to_user(arg, &add, sizeof(struct dlci_add)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
421
422
423
424
425
426
427
428
429
430
431
432
433
  					return -EFAULT;
  			break;
  
  		case SIOCDELDLCI:
  			err = dlci_del(&add);
  			break;
  
  		default:
  			err = -EINVAL;
  	}
  
  	return(err);
  }
3b04ddde0   Stephen Hemminger   [NET]: Move hardw...
434
435
436
  static const struct header_ops dlci_header_ops = {
  	.create	= dlci_header,
  };
49e8abaf6   Stephen Hemminger   dlci: convert to ...
437
438
439
440
441
442
443
  static const struct net_device_ops dlci_netdev_ops = {
  	.ndo_open	= dlci_open,
  	.ndo_stop	= dlci_close,
  	.ndo_do_ioctl	= dlci_dev_ioctl,
  	.ndo_start_xmit	= dlci_transmit,
  	.ndo_change_mtu	= dlci_change_mtu,
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
444
445
  static void dlci_setup(struct net_device *dev)
  {
8f15ea42b   Wang Chen   netdevice: safe c...
446
  	struct dlci_local *dlp = netdev_priv(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
447
448
  
  	dev->flags		= 0;
3b04ddde0   Stephen Hemminger   [NET]: Move hardw...
449
  	dev->header_ops		= &dlci_header_ops;
49e8abaf6   Stephen Hemminger   dlci: convert to ...
450
  	dev->netdev_ops		= &dlci_netdev_ops;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
  	dev->destructor		= free_netdev;
  
  	dlp->receive		= dlci_receive;
  
  	dev->type		= ARPHRD_DLCI;
  	dev->hard_header_len	= sizeof(struct frhdr);
  	dev->addr_len		= sizeof(short);
  
  }
  
  /* if slave is unregistering, then cleanup master */
  static int dlci_dev_event(struct notifier_block *unused,
  			  unsigned long event, void *ptr)
  {
  	struct net_device *dev = (struct net_device *) ptr;
c346dca10   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
466
  	if (dev_net(dev) != &init_net)
e9dc86534   Eric W. Biederman   [NET]: Make devic...
467
  		return NOTIFY_DONE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
  	if (event == NETDEV_UNREGISTER) {
  		struct dlci_local *dlp;
  
  		list_for_each_entry(dlp, &dlci_devs, list) {
  			if (dlp->slave == dev) {
  				list_del(&dlp->list);
  				unregister_netdevice(dlp->master);
  				dev_put(dlp->slave);
  				break;
  			}
  		}
  	}
  	return NOTIFY_DONE;
  }
  
  static struct notifier_block dlci_notifier = {
  	.notifier_call = dlci_dev_event,
  };
  
  static int __init init_dlci(void)
  {
  	dlci_ioctl_set(dlci_ioctl);
  	register_netdevice_notifier(&dlci_notifier);
  
  	printk("%s.
  ", version);
  
  	return 0;
  }
  
  static void __exit dlci_exit(void)
  {
  	struct dlci_local	*dlp, *nxt;
  	
  	dlci_ioctl_set(NULL);
  	unregister_netdevice_notifier(&dlci_notifier);
  
  	rtnl_lock();
  	list_for_each_entry_safe(dlp, nxt, &dlci_devs, list) {
  		unregister_netdevice(dlp->master);
  		dev_put(dlp->slave);
  	}
  	rtnl_unlock();
  }
  
  module_init(init_dlci);
  module_exit(dlci_exit);
  
  MODULE_AUTHOR("Mike McLagan");
  MODULE_DESCRIPTION("Frame Relay DLCI layer");
  MODULE_LICENSE("GPL");