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
61
  
  #define KMEM_SAFETYZONE 8
7be6065b3   Wang Chen   netdevice wanrout...
62
  #define DEV_TO_SLAVE(dev)	(*((struct net_device **)netdev_priv(dev)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
63
64
65
66
67
68
69
  /*
   * 	Function Prototypes
   */
  
  /*
   *	WAN device IOCTL handlers
   */
15fd0cd9a   Arnd Bergmann   net: autoconvert ...
70
  static DEFINE_MUTEX(wanrouter_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
  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...
87
88
89
90
  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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
  
  
  
  /*
   *	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
106
  #if 0
97353cb4c   Adrian Bunk   [NET] net/wanrout...
107
  static unsigned char wanrouter_oui_ether[] = { 0x00, 0x00, 0x00 };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
  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...
252
  #if 0
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
  /*
   *	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...
282
283
  		skb_copy_to_linear_data_offset(skb, 2, wanrouter_oui_ether,
  					       sizeof(wanrouter_oui_ether));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
  		*((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 *...
307
  __be16 wanrouter_type_trans(struct sk_buff *skb, struct net_device *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
308
309
  {
  	int cnt = skb->data[0] ? 0 : 1;	/* there may be a pad present */
ab611487d   Alexey Dobriyan   [NET]: __be'ify *...
310
  	__be16 ethertype;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
311
312
313
314
315
316
  
  	switch (skb->data[cnt]) {
  	case NLPID_IP:		/* IP datagramm */
  		ethertype = htons(ETH_P_IP);
  		cnt += 1;
  		break;
4ba6122b4   YOSHIFUJI Hideaki   [NET] WANROUTER: ...
317
  	case NLPID_SNAP:	/* SNAP encapsulation */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
318
319
  		if (memcmp(&skb->data[cnt + 1], wanrouter_oui_ether,
  			   sizeof(wanrouter_oui_ether))){
4ba6122b4   YOSHIFUJI Hideaki   [NET] WANROUTER: ...
320
  			printk(KERN_INFO
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
321
322
323
324
325
326
327
  				"%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 *...
328
  		ethertype = *((__be16*)&skb->data[cnt+4]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
  		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...
344
  	skb_reset_mac_header(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
345
346
  	return ethertype;
  }
97353cb4c   Adrian Bunk   [NET] net/wanrout...
347
  #endif  /*  0  */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
348
349
350
351
352
353
  
  /*
   *	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...
354
  long wanrouter_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
355
  {
866988eda   Alan Cox   wanrouter: Push d...
356
  	struct inode *inode = file->f_path.dentry->d_inode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
  	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 ...
375
  	mutex_lock(&wanrouter_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
406
  	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 ...
407
  	mutex_unlock(&wanrouter_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
457
  	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: ...
458
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
459
460
461
  	}
  
  	if (conf->data_size && conf->data) {
75202e768   Bill Nottingham   [NET]: Fix compar...
462
  		if (conf->data_size > 128000) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
463
464
465
466
467
  			printk(KERN_INFO
  			    "%s: ERROR, Invalid firmware data size %i !
  ",
  					wandev->name, conf->data_size);
  			kfree(conf);
4ba6122b4   YOSHIFUJI Hideaki   [NET] WANROUTER: ...
468
  			return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
469
470
471
472
473
  		}
  
  		data = vmalloc(conf->data_size);
  		if (!data) {
  			printk(KERN_INFO
426d31071   Paul Bolle   fix printk typo '...
474
475
  				"%s: ERROR, Failed allocate kernel memory !
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
476
477
478
479
480
481
482
483
484
  				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 '...
485
486
  			     "%s: ERROR, Failed to copy from user data !
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
524
  			       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...
525
  		 * structure. Therefore, we cannot use netdev_priv(dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
579
  		 * 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
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
  	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
598
599
600
601
602
  		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
603
  	} else {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
633
  		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...
634
635
636
  					     DEV_TO_SLAVE(slave);
  					     slave = DEV_TO_SLAVE(slave))
  						DEV_TO_SLAVE(slave) = dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
637
638
639
640
641
642
643
644
645
646
  				}
  				++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...
647
  		free_netdev(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
648
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
  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: ...
664
  	int err = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
  
  	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: ...
686
687
  	if (!wandev->ndev && wandev->shutdown)
  		err = wandev->shutdown(wandev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
736
  
  	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...
737
  		struct net_device **slave = netdev_priv(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
  		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...
754
755
  		struct net_device **prev_slave = netdev_priv(prev);
  		struct net_device **slave = netdev_priv(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
756
757
758
  
  		*prev_slave = *slave;
  	} else {
7be6065b3   Wang Chen   netdevice wanrout...
759
  		struct net_device **slave = netdev_priv(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
760
761
762
763
764
765
766
  		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
767
768
769
770
771
772
  	unregister_netdev(dev);
  
  	free_netdev(dev);
  
  	return 0;
  }
97353cb4c   Adrian Bunk   [NET] net/wanrout...
773
  static void lock_adapter_irq(spinlock_t *lock, unsigned long *smp_flags)
9ee62630f   Hannes Eder   wanrouter: fix sp...
774
  	__acquires(lock)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
775
  {
4ba6122b4   YOSHIFUJI Hideaki   [NET] WANROUTER: ...
776
  	spin_lock_irqsave(lock, *smp_flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
777
  }
97353cb4c   Adrian Bunk   [NET] net/wanrout...
778
  static void unlock_adapter_irq(spinlock_t *lock, unsigned long *smp_flags)
9ee62630f   Hannes Eder   wanrouter: fix sp...
779
  	__releases(lock)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
780
781
782
783
784
785
  {
  	spin_unlock_irqrestore(lock, *smp_flags);
  }
  
  EXPORT_SYMBOL(register_wan_device);
  EXPORT_SYMBOL(unregister_wan_device);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
786
787
788
789
790
791
  
  MODULE_LICENSE("GPL");
  
  /*
   *	End
   */