Blame view

net/wanrouter/wanmain.c 19.1 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
  /*****************************************************************************
  * wanmain.c	WAN Multiprotocol Router Module. Main code.
  *
  *		This module is completely hardware-independent and provides
  *		the following common services for the WAN Link Drivers:
0779bf2d2   Matt LaPlante   Fix misc .c/.h co...
6
  *		 o WAN device management (registering, unregistering)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
  *		 o Network interface management
  *		 o Physical connection management (dial-up, incoming calls)
  *		 o Logical connection management (switched virtual circuits)
  *		 o Protocol encapsulation/decapsulation
  *
  * Author:	Gideon Hack
  *
  * Copyright:	(c) 1995-1999 Sangoma Technologies Inc.
  *
  *		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.
  * ============================================================================
  * Nov 24, 2000  Nenad Corbic	Updated for 2.4.X kernels
  * Nov 07, 2000  Nenad Corbic	Fixed the Mulit-Port PPP for kernels 2.2.16 and
  *  				greater.
  * Aug 2,  2000  Nenad Corbic	Block the Multi-Port PPP from running on
  *  			        kernels 2.2.16 or greater.  The SyncPPP
  *  			        has changed.
  * Jul 13, 2000  Nenad Corbic	Added SyncPPP support
  * 				Added extra debugging in device_setup().
  * Oct 01, 1999  Gideon Hack     Update for s514 PCI card
  * Dec 27, 1996	Gene Kozin	Initial version (based on Sangoma's WANPIPE)
  * Jan 16, 1997	Gene Kozin	router_devlist made public
  * Jan 31, 1997  Alan Cox	Hacked it about a bit for 2.1
  * Jun 27, 1997  Alan Cox	realigned with vendor code
  * Oct 15, 1997  Farhan Thawar   changed wan_encapsulate to add a pad byte of 0
  * Apr 20, 1998	Alan Cox	Fixed 2.1 symbols
  * May 17, 1998  K. Baranowski	Fixed SNAP encapsulation in wan_encapsulate
  * Dec 15, 1998  Arnaldo Melo    support for firmwares of up to 128000 bytes
  *                               check wandev->setup return value
  * Dec 22, 1998  Arnaldo Melo    vmalloc/vfree used in device_setup to allocate
  *                               kernel memory and copy configuration data to
  *                               kernel space (for big firmwares)
  * Jun 02, 1999  Gideon Hack	Updates for Linux 2.0.X and 2.2.X kernels.
  *****************************************************************************/
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
44
  #include <linux/stddef.h>	/* offsetof(), etc. */
4fc268d24   Randy Dunlap   [PATCH] capable/c...
45
  #include <linux/capability.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
46
47
  #include <linux/errno.h>	/* return codes */
  #include <linux/kernel.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
48
  #include <linux/module.h>	/* support for loadable modules */
e49332bd1   Jesper Juhl   [PATCH] misc veri...
49
  #include <linux/slab.h>		/* kmalloc(), kfree() */
15fd0cd9a   Arnd Bergmann   net: autoconvert ...
50
  #include <linux/mutex.h>
e49332bd1   Jesper Juhl   [PATCH] misc veri...
51
  #include <linux/mm.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
52
53
54
55
56
57
58
59
  #include <linux/string.h>	/* inline mem*, str* functions */
  
  #include <asm/byteorder.h>	/* htons(), etc. */
  #include <linux/wanrouter.h>	/* WAN router API definitions */
  
  #include <linux/vmalloc.h>	/* vmalloc, vfree */
  #include <asm/uaccess.h>        /* copy_to/from_user */
  #include <linux/init.h>         /* __initfunc et al. */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
60

7be6065b3   Wang Chen   netdevice wanrout...
61
  #define DEV_TO_SLAVE(dev)	(*((struct net_device **)netdev_priv(dev)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
62
63
64
65
66
67
68
  /*
   * 	Function Prototypes
   */
  
  /*
   *	WAN device IOCTL handlers
   */
15fd0cd9a   Arnd Bergmann   net: autoconvert ...
69
  static DEFINE_MUTEX(wanrouter_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
  static int wanrouter_device_setup(struct wan_device *wandev,
  				  wandev_conf_t __user *u_conf);
  static int wanrouter_device_stat(struct wan_device *wandev,
  				 wandev_stat_t __user *u_stat);
  static int wanrouter_device_shutdown(struct wan_device *wandev);
  static int wanrouter_device_new_if(struct wan_device *wandev,
  				   wanif_conf_t __user *u_conf);
  static int wanrouter_device_del_if(struct wan_device *wandev,
  				   char __user *u_name);
  
  /*
   *	Miscellaneous
   */
  
  static struct wan_device *wanrouter_find_device(char *name);
  static int wanrouter_delete_interface(struct wan_device *wandev, char *name);
9ee62630f   Hannes Eder   wanrouter: fix sp...
86
87
88
89
  static void lock_adapter_irq(spinlock_t *lock, unsigned long *smp_flags)
  	__acquires(lock);
  static void unlock_adapter_irq(spinlock_t *lock, unsigned long *smp_flags)
  	__releases(lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
  
  
  
  /*
   *	Global Data
   */
  
  static char wanrouter_fullname[]  = "Sangoma WANPIPE Router";
  static char wanrouter_copyright[] = "(c) 1995-2000 Sangoma Technologies Inc.";
  static char wanrouter_modname[] = ROUTER_NAME; /* short module name */
  struct wan_device* wanrouter_router_devlist; /* list of registered devices */
  
  /*
   *	Organize Unique Identifiers for encapsulation/decapsulation
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
105
  #if 0
97353cb4c   Adrian Bunk   [NET] net/wanrout...
106
  static unsigned char wanrouter_oui_ether[] = { 0x00, 0x00, 0x00 };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
  static unsigned char wanrouter_oui_802_2[] = { 0x00, 0x80, 0xC2 };
  #endif
  
  static int __init wanrouter_init(void)
  {
  	int err;
  
  	printk(KERN_INFO "%s v%u.%u %s
  ",
  	       wanrouter_fullname, ROUTER_VERSION, ROUTER_RELEASE,
  	       wanrouter_copyright);
  
  	err = wanrouter_proc_init();
  	if (err)
  		printk(KERN_INFO "%s: can't create entry in proc filesystem!
  ",
  		       wanrouter_modname);
  
  	return err;
  }
  
  static void __exit wanrouter_cleanup (void)
  {
  	wanrouter_proc_cleanup();
  }
  
  /*
   * This is just plain dumb.  We should move the bugger to drivers/net/wan,
   * slap it first in directory and make it module_init().  The only reason
   * for subsys_initcall() here is that net goes after drivers (why, BTW?)
   */
  subsys_initcall(wanrouter_init);
  module_exit(wanrouter_cleanup);
  
  /*
   * 	Kernel APIs
   */
  
  /*
   * 	Register WAN device.
   * 	o verify device credentials
   * 	o create an entry for the device in the /proc/net/router directory
   * 	o initialize internally maintained fields of the wan_device structure
   * 	o link device data space to a singly-linked list
   * 	o if it's the first device, then start kernel 'thread'
   * 	o increment module use count
   *
   * 	Return:
   *	0	Ok
   *	< 0	error.
   *
   * 	Context:	process
   */
  
  
  int register_wan_device(struct wan_device *wandev)
  {
  	int err, namelen;
  
  	if ((wandev == NULL) || (wandev->magic != ROUTER_MAGIC) ||
  	    (wandev->name == NULL))
  		return -EINVAL;
  
  	namelen = strlen(wandev->name);
  	if (!namelen || (namelen > WAN_DRVNAME_SZ))
  		return -EINVAL;
  
  	if (wanrouter_find_device(wandev->name))
  		return -EEXIST;
  
  #ifdef WANDEBUG
  	printk(KERN_INFO "%s: registering WAN device %s
  ",
  	       wanrouter_modname, wandev->name);
  #endif
  
  	/*
  	 *	Register /proc directory entry
  	 */
  	err = wanrouter_proc_add(wandev);
  	if (err) {
  		printk(KERN_INFO
  			"%s: can't create /proc/net/router/%s entry!
  ",
  			wanrouter_modname, wandev->name);
  		return err;
  	}
  
  	/*
  	 *	Initialize fields of the wan_device structure maintained by the
  	 *	router and update local data.
  	 */
  
  	wandev->ndev = 0;
  	wandev->dev  = NULL;
  	wandev->next = wanrouter_router_devlist;
  	wanrouter_router_devlist = wandev;
  	return 0;
  }
  
  /*
   *	Unregister WAN device.
   *	o shut down device
   *	o unlink device data space from the linked list
   *	o delete device entry in the /proc/net/router directory
   *	o decrement module use count
   *
   *	Return:		0	Ok
   *			<0	error.
   *	Context:	process
   */
  
  
  int unregister_wan_device(char *name)
  {
  	struct wan_device *wandev, *prev;
  
  	if (name == NULL)
  		return -EINVAL;
  
  	for (wandev = wanrouter_router_devlist, prev = NULL;
  		wandev && strcmp(wandev->name, name);
  		prev = wandev, wandev = wandev->next)
  		;
  	if (wandev == NULL)
  		return -ENODEV;
  
  #ifdef WANDEBUG
  	printk(KERN_INFO "%s: unregistering WAN device %s
  ",
  	       wanrouter_modname, name);
  #endif
  
  	if (wandev->state != WAN_UNCONFIGURED)
  		wanrouter_device_shutdown(wandev);
  
  	if (prev)
  		prev->next = wandev->next;
  	else
  		wanrouter_router_devlist = wandev->next;
  
  	wanrouter_proc_delete(wandev);
  	return 0;
  }
97353cb4c   Adrian Bunk   [NET] net/wanrout...
251
  #if 0
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
  /*
   *	Encapsulate packet.
   *
   *	Return:	encapsulation header size
   *		< 0	- unsupported Ethertype
   *
   *	Notes:
   *	1. This function may be called on interrupt context.
   */
  
  
  int wanrouter_encapsulate(struct sk_buff *skb, struct net_device *dev,
  			  unsigned short type)
  {
  	int hdr_len = 0;
  
  	switch (type) {
  	case ETH_P_IP:		/* IP datagram encapsulation */
  		hdr_len += 1;
  		skb_push(skb, 1);
  		skb->data[0] = NLPID_IP;
  		break;
  
  	case ETH_P_IPX:		/* SNAP encapsulation */
  	case ETH_P_ARP:
  		hdr_len += 7;
  		skb_push(skb, 7);
  		skb->data[0] = 0;
  		skb->data[1] = NLPID_SNAP;
27d7ff46a   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
281
282
  		skb_copy_to_linear_data_offset(skb, 2, wanrouter_oui_ether,
  					       sizeof(wanrouter_oui_ether));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
  		*((unsigned short*)&skb->data[5]) = htons(type);
  		break;
  
  	default:		/* Unknown packet type */
  		printk(KERN_INFO
  			"%s: unsupported Ethertype 0x%04X on interface %s!
  ",
  			wanrouter_modname, type, dev->name);
  		hdr_len = -EINVAL;
  	}
  	return hdr_len;
  }
  
  
  /*
   *	Decapsulate packet.
   *
   *	Return:	Ethertype (in network order)
   *			0	unknown encapsulation
   *
   *	Notes:
   *	1. This function may be called on interrupt context.
   */
ab611487d   Alexey Dobriyan   [NET]: __be'ify *...
306
  __be16 wanrouter_type_trans(struct sk_buff *skb, struct net_device *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
307
308
  {
  	int cnt = skb->data[0] ? 0 : 1;	/* there may be a pad present */
ab611487d   Alexey Dobriyan   [NET]: __be'ify *...
309
  	__be16 ethertype;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
310
311
312
313
314
315
  
  	switch (skb->data[cnt]) {
  	case NLPID_IP:		/* IP datagramm */
  		ethertype = htons(ETH_P_IP);
  		cnt += 1;
  		break;
4ba6122b4   YOSHIFUJI Hideaki   [NET] WANROUTER: ...
316
  	case NLPID_SNAP:	/* SNAP encapsulation */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
317
318
  		if (memcmp(&skb->data[cnt + 1], wanrouter_oui_ether,
  			   sizeof(wanrouter_oui_ether))){
4ba6122b4   YOSHIFUJI Hideaki   [NET] WANROUTER: ...
319
  			printk(KERN_INFO
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
320
321
322
323
324
325
326
  				"%s: unsupported SNAP OUI %02X-%02X-%02X "
  				"on interface %s!
  ", wanrouter_modname,
  				skb->data[cnt+1], skb->data[cnt+2],
  				skb->data[cnt+3], dev->name);
  			return 0;
  		}
ab611487d   Alexey Dobriyan   [NET]: __be'ify *...
327
  		ethertype = *((__be16*)&skb->data[cnt+4]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
  		cnt += 6;
  		break;
  
  	/* add other protocols, e.g. CLNP, ESIS, ISIS, if needed */
  
  	default:
  		printk(KERN_INFO
  			"%s: unsupported NLPID 0x%02X on interface %s!
  ",
  			wanrouter_modname, skb->data[cnt], dev->name);
  		return 0;
  	}
  	skb->protocol = ethertype;
  	skb->pkt_type = PACKET_HOST;	/*	Physically point to point */
  	skb_pull(skb, cnt);
459a98ed8   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
343
  	skb_reset_mac_header(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
344
345
  	return ethertype;
  }
97353cb4c   Adrian Bunk   [NET] net/wanrout...
346
  #endif  /*  0  */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
347
348
349
350
351
352
  
  /*
   *	WAN device IOCTL.
   *	o find WAN device associated with this node
   *	o execute requested action or pass command to the device driver
   */
866988eda   Alan Cox   wanrouter: Push d...
353
  long wanrouter_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
354
  {
866988eda   Alan Cox   wanrouter: Push d...
355
  	struct inode *inode = file->f_path.dentry->d_inode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
  	int err = 0;
  	struct proc_dir_entry *dent;
  	struct wan_device *wandev;
  	void __user *data = (void __user *)arg;
  
  	if (!capable(CAP_NET_ADMIN))
  		return -EPERM;
  
  	if ((cmd >> 8) != ROUTER_IOCTL)
  		return -EINVAL;
  
  	dent = PDE(inode);
  	if ((dent == NULL) || (dent->data == NULL))
  		return -EINVAL;
  
  	wandev = dent->data;
  	if (wandev->magic != ROUTER_MAGIC)
  		return -EINVAL;
15fd0cd9a   Arnd Bergmann   net: autoconvert ...
374
  	mutex_lock(&wanrouter_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
  	switch (cmd) {
  	case ROUTER_SETUP:
  		err = wanrouter_device_setup(wandev, data);
  		break;
  
  	case ROUTER_DOWN:
  		err = wanrouter_device_shutdown(wandev);
  		break;
  
  	case ROUTER_STAT:
  		err = wanrouter_device_stat(wandev, data);
  		break;
  
  	case ROUTER_IFNEW:
  		err = wanrouter_device_new_if(wandev, data);
  		break;
  
  	case ROUTER_IFDEL:
  		err = wanrouter_device_del_if(wandev, data);
  		break;
  
  	case ROUTER_IFSTAT:
  		break;
  
  	default:
  		if ((cmd >= ROUTER_USER) &&
  		    (cmd <= ROUTER_USER_MAX) &&
  		    wandev->ioctl)
  			err = wandev->ioctl(wandev, cmd, arg);
  		else err = -EINVAL;
  	}
15fd0cd9a   Arnd Bergmann   net: autoconvert ...
406
  	mutex_unlock(&wanrouter_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
  	return err;
  }
  
  /*
   *	WAN Driver IOCTL Handlers
   */
  
  /*
   *	Setup WAN link device.
   *	o verify user address space
   *	o allocate kernel memory and copy configuration data to kernel space
   *	o if configuration data includes extension, copy it to kernel space too
   *	o call driver's setup() entry point
   */
  
  static int wanrouter_device_setup(struct wan_device *wandev,
  				  wandev_conf_t __user *u_conf)
  {
  	void *data = NULL;
  	wandev_conf_t *conf;
  	int err = -EINVAL;
  
  	if (wandev->setup == NULL) {	/* Nothing to do ? */
  		printk(KERN_INFO "%s: ERROR, No setup script: wandev->setup()
  ",
  				wandev->name);
  		return 0;
  	}
  
  	conf = kmalloc(sizeof(wandev_conf_t), GFP_KERNEL);
  	if (conf == NULL){
  		printk(KERN_INFO "%s: ERROR, Failed to allocate kernel memory !
  ",
  				wandev->name);
  		return -ENOBUFS;
  	}
  
  	if (copy_from_user(conf, u_conf, sizeof(wandev_conf_t))) {
  		printk(KERN_INFO "%s: Failed to copy user config data to kernel space!
  ",
  				wandev->name);
  		kfree(conf);
  		return -EFAULT;
  	}
  
  	if (conf->magic != ROUTER_MAGIC) {
  		kfree(conf);
  		printk(KERN_INFO "%s: ERROR, Invalid MAGIC Number
  ",
  				wandev->name);
4ba6122b4   YOSHIFUJI Hideaki   [NET] WANROUTER: ...
457
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
458
459
460
  	}
  
  	if (conf->data_size && conf->data) {
75202e768   Bill Nottingham   [NET]: Fix compar...
461
  		if (conf->data_size > 128000) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
462
463
464
465
466
  			printk(KERN_INFO
  			    "%s: ERROR, Invalid firmware data size %i !
  ",
  					wandev->name, conf->data_size);
  			kfree(conf);
4ba6122b4   YOSHIFUJI Hideaki   [NET] WANROUTER: ...
467
  			return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
468
469
470
471
472
  		}
  
  		data = vmalloc(conf->data_size);
  		if (!data) {
  			printk(KERN_INFO
426d31071   Paul Bolle   fix printk typo '...
473
474
  				"%s: ERROR, Failed allocate kernel memory !
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
475
476
477
478
479
480
481
482
483
  				wandev->name);
  			kfree(conf);
  			return -ENOBUFS;
  		}
  		if (!copy_from_user(data, conf->data, conf->data_size)) {
  			conf->data = data;
  			err = wandev->setup(wandev, conf);
  		} else {
  			printk(KERN_INFO
426d31071   Paul Bolle   fix printk typo '...
484
485
  			     "%s: ERROR, Failed to copy from user data !
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
519
520
521
522
523
  			       wandev->name);
  			err = -EFAULT;
  		}
  		vfree(data);
  	} else {
  		printk(KERN_INFO
  		    "%s: ERROR, No firmware found ! Firmware size = %i !
  ",
  				wandev->name, conf->data_size);
  	}
  
  	kfree(conf);
  	return err;
  }
  
  /*
   *	Shutdown WAN device.
   *	o delete all not opened logical channels for this device
   *	o call driver's shutdown() entry point
   */
  
  static int wanrouter_device_shutdown(struct wan_device *wandev)
  {
  	struct net_device *dev;
  	int err=0;
  
  	if (wandev->state == WAN_UNCONFIGURED)
  		return 0;
  
  	printk(KERN_INFO "
  %s: Shutting Down!
  ",wandev->name);
  
  	for (dev = wandev->dev; dev;) {
  		err = wanrouter_delete_interface(wandev, dev->name);
  		if (err)
  			return err;
  		/* The above function deallocates the current dev
7be6065b3   Wang Chen   netdevice wanrout...
524
  		 * structure. Therefore, we cannot use netdev_priv(dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
  		 * as the next element: wandev->dev points to the
  		 * next element */
  		dev = wandev->dev;
  	}
  
  	if (wandev->ndev)
  		return -EBUSY;	/* there are opened interfaces  */
  
  	if (wandev->shutdown)
  		err=wandev->shutdown(wandev);
  
  	return err;
  }
  
  /*
   *	Get WAN device status & statistics.
   */
  
  static int wanrouter_device_stat(struct wan_device *wandev,
  				 wandev_stat_t __user *u_stat)
  {
  	wandev_stat_t stat;
  
  	memset(&stat, 0, sizeof(stat));
  
  	/* Ask device driver to update device statistics */
  	if ((wandev->state != WAN_UNCONFIGURED) && wandev->update)
  		wandev->update(wandev);
  
  	/* Fill out structure */
  	stat.ndev  = wandev->ndev;
  	stat.state = wandev->state;
  
  	if (copy_to_user(u_stat, &stat, sizeof(stat)))
  		return -EFAULT;
  
  	return 0;
  }
  
  /*
   *	Create new WAN interface.
   *	o verify user address space
   *	o copy configuration data to kernel address space
   *	o allocate network interface data space
   *	o call driver's new_if() entry point
   *	o make sure there is no interface name conflict
   *	o register network interface
   */
  
  static int wanrouter_device_new_if(struct wan_device *wandev,
  				   wanif_conf_t __user *u_conf)
  {
  	wanif_conf_t *cnf;
  	struct net_device *dev = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
  	int err;
  
  	if ((wandev->state == WAN_UNCONFIGURED) || (wandev->new_if == NULL))
  		return -ENODEV;
  
  	cnf = kmalloc(sizeof(wanif_conf_t), GFP_KERNEL);
  	if (!cnf)
  		return -ENOBUFS;
  
  	err = -EFAULT;
  	if (copy_from_user(cnf, u_conf, sizeof(wanif_conf_t)))
  		goto out;
  
  	err = -EINVAL;
  	if (cnf->magic != ROUTER_MAGIC)
  		goto out;
  
  	if (cnf->config_id == WANCONFIG_MPPP) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
597
598
599
600
601
  		printk(KERN_INFO "%s: Wanpipe Mulit-Port PPP support has not been compiled in!
  ",
  				wandev->name);
  		err = -EPROTONOSUPPORT;
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
602
  	} else {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
  		err = wandev->new_if(wandev, dev, cnf);
  	}
  
  	if (!err) {
  		/* Register network interface. This will invoke init()
  		 * function supplied by the driver.  If device registered
  		 * successfully, add it to the interface list.
  		 */
  
  		if (dev->name == NULL) {
  			err = -EINVAL;
  		} else {
  
  			#ifdef WANDEBUG
  			printk(KERN_INFO "%s: registering interface %s...
  ",
  				wanrouter_modname, dev->name);
  			#endif
  
  			err = register_netdev(dev);
  			if (!err) {
  				struct net_device *slave = NULL;
  				unsigned long smp_flags=0;
  
  				lock_adapter_irq(&wandev->lock, &smp_flags);
  
  				if (wandev->dev == NULL) {
  					wandev->dev = dev;
  				} else {
  					for (slave=wandev->dev;
7be6065b3   Wang Chen   netdevice wanrout...
633
634
635
  					     DEV_TO_SLAVE(slave);
  					     slave = DEV_TO_SLAVE(slave))
  						DEV_TO_SLAVE(slave) = dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
636
637
638
639
640
641
642
643
644
645
  				}
  				++wandev->ndev;
  
  				unlock_adapter_irq(&wandev->lock, &smp_flags);
  				err = 0;	/* done !!! */
  				goto out;
  			}
  		}
  		if (wandev->del_if)
  			wandev->del_if(wandev, dev);
7be6065b3   Wang Chen   netdevice wanrout...
646
  		free_netdev(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
647
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
  out:
  	kfree(cnf);
  	return err;
  }
  
  
  /*
   *	Delete WAN logical channel.
   *	 o verify user address space
   *	 o copy configuration data to kernel address space
   */
  
  static int wanrouter_device_del_if(struct wan_device *wandev, char __user *u_name)
  {
  	char name[WAN_IFNAME_SZ + 1];
4ba6122b4   YOSHIFUJI Hideaki   [NET] WANROUTER: ...
663
  	int err = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
  
  	if (wandev->state == WAN_UNCONFIGURED)
  		return -ENODEV;
  
  	memset(name, 0, sizeof(name));
  
  	if (copy_from_user(name, u_name, WAN_IFNAME_SZ))
  		return -EFAULT;
  
  	err = wanrouter_delete_interface(wandev, name);
  	if (err)
  		return err;
  
  	/* If last interface being deleted, shutdown card
  	 * This helps with administration at leaf nodes
  	 * (You can tell if the person at the other end of the phone
  	 * has an interface configured) and avoids DoS vulnerabilities
  	 * in binary driver files - this fixes a problem with the current
  	 * Sangoma driver going into strange states when all the network
  	 * interfaces are deleted and the link irrecoverably disconnected.
  	 */
4ba6122b4   YOSHIFUJI Hideaki   [NET] WANROUTER: ...
685
686
  	if (!wandev->ndev && wandev->shutdown)
  		err = wandev->shutdown(wandev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
  
  	return err;
  }
  
  /*
   *	Miscellaneous Functions
   */
  
  /*
   *	Find WAN device by name.
   *	Return pointer to the WAN device data space or NULL if device not found.
   */
  
  static struct wan_device *wanrouter_find_device(char *name)
  {
  	struct wan_device *wandev;
  
  	for (wandev = wanrouter_router_devlist;
  	     wandev && strcmp(wandev->name, name);
  		wandev = wandev->next);
  	return wandev;
  }
  
  /*
   *	Delete WAN logical channel identified by its name.
   *	o find logical channel by its name
   *	o call driver's del_if() entry point
   *	o unregister network interface
   *	o unlink channel data space from linked list of channels
   *	o release channel data space
   *
   *	Return:	0		success
   *		-ENODEV		channel not found.
   *		-EBUSY		interface is open
   *
   *	Note: If (force != 0), then device will be destroyed even if interface
   *	associated with it is open. It's caller's responsibility to make
   *	sure that opened interfaces are not removed!
   */
  
  static int wanrouter_delete_interface(struct wan_device *wandev, char *name)
  {
  	struct net_device *dev = NULL, *prev = NULL;
  	unsigned long smp_flags=0;
  
  	lock_adapter_irq(&wandev->lock, &smp_flags);
  	dev = wandev->dev;
  	prev = NULL;
  	while (dev && strcmp(name, dev->name)) {
7be6065b3   Wang Chen   netdevice wanrout...
736
  		struct net_device **slave = netdev_priv(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
  		prev = dev;
  		dev = *slave;
  	}
  	unlock_adapter_irq(&wandev->lock, &smp_flags);
  
  	if (dev == NULL)
  		return -ENODEV;	/* interface not found */
  
  	if (netif_running(dev))
  		return -EBUSY;	/* interface in use */
  
  	if (wandev->del_if)
  		wandev->del_if(wandev, dev);
  
  	lock_adapter_irq(&wandev->lock, &smp_flags);
  	if (prev) {
7be6065b3   Wang Chen   netdevice wanrout...
753
754
  		struct net_device **prev_slave = netdev_priv(prev);
  		struct net_device **slave = netdev_priv(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
755
756
757
  
  		*prev_slave = *slave;
  	} else {
7be6065b3   Wang Chen   netdevice wanrout...
758
  		struct net_device **slave = netdev_priv(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
759
760
761
762
763
764
765
  		wandev->dev = *slave;
  	}
  	--wandev->ndev;
  	unlock_adapter_irq(&wandev->lock, &smp_flags);
  
  	printk(KERN_INFO "%s: unregistering '%s'
  ", wandev->name, dev->name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
766
767
768
769
770
771
  	unregister_netdev(dev);
  
  	free_netdev(dev);
  
  	return 0;
  }
97353cb4c   Adrian Bunk   [NET] net/wanrout...
772
  static void lock_adapter_irq(spinlock_t *lock, unsigned long *smp_flags)
9ee62630f   Hannes Eder   wanrouter: fix sp...
773
  	__acquires(lock)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
774
  {
4ba6122b4   YOSHIFUJI Hideaki   [NET] WANROUTER: ...
775
  	spin_lock_irqsave(lock, *smp_flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
776
  }
97353cb4c   Adrian Bunk   [NET] net/wanrout...
777
  static void unlock_adapter_irq(spinlock_t *lock, unsigned long *smp_flags)
9ee62630f   Hannes Eder   wanrouter: fix sp...
778
  	__releases(lock)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
779
780
781
782
783
784
  {
  	spin_unlock_irqrestore(lock, *smp_flags);
  }
  
  EXPORT_SYMBOL(register_wan_device);
  EXPORT_SYMBOL(unregister_wan_device);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
785
786
787
788
789
790
  
  MODULE_LICENSE("GPL");
  
  /*
   *	End
   */