diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /net/wanrouter/wanmain.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'net/wanrouter/wanmain.c')
-rw-r--r-- | net/wanrouter/wanmain.c | 888 |
1 files changed, 888 insertions, 0 deletions
diff --git a/net/wanrouter/wanmain.c b/net/wanrouter/wanmain.c new file mode 100644 index 000000000000..956c17f6c548 --- /dev/null +++ b/net/wanrouter/wanmain.c | |||
@@ -0,0 +1,888 @@ | |||
1 | /***************************************************************************** | ||
2 | * wanmain.c WAN Multiprotocol Router Module. Main code. | ||
3 | * | ||
4 | * This module is completely hardware-independent and provides | ||
5 | * the following common services for the WAN Link Drivers: | ||
6 | * o WAN device managenment (registering, unregistering) | ||
7 | * o Network interface management | ||
8 | * o Physical connection management (dial-up, incoming calls) | ||
9 | * o Logical connection management (switched virtual circuits) | ||
10 | * o Protocol encapsulation/decapsulation | ||
11 | * | ||
12 | * Author: Gideon Hack | ||
13 | * | ||
14 | * Copyright: (c) 1995-1999 Sangoma Technologies Inc. | ||
15 | * | ||
16 | * This program is free software; you can redistribute it and/or | ||
17 | * modify it under the terms of the GNU General Public License | ||
18 | * as published by the Free Software Foundation; either version | ||
19 | * 2 of the License, or (at your option) any later version. | ||
20 | * ============================================================================ | ||
21 | * Nov 24, 2000 Nenad Corbic Updated for 2.4.X kernels | ||
22 | * Nov 07, 2000 Nenad Corbic Fixed the Mulit-Port PPP for kernels 2.2.16 and | ||
23 | * greater. | ||
24 | * Aug 2, 2000 Nenad Corbic Block the Multi-Port PPP from running on | ||
25 | * kernels 2.2.16 or greater. The SyncPPP | ||
26 | * has changed. | ||
27 | * Jul 13, 2000 Nenad Corbic Added SyncPPP support | ||
28 | * Added extra debugging in device_setup(). | ||
29 | * Oct 01, 1999 Gideon Hack Update for s514 PCI card | ||
30 | * Dec 27, 1996 Gene Kozin Initial version (based on Sangoma's WANPIPE) | ||
31 | * Jan 16, 1997 Gene Kozin router_devlist made public | ||
32 | * Jan 31, 1997 Alan Cox Hacked it about a bit for 2.1 | ||
33 | * Jun 27, 1997 Alan Cox realigned with vendor code | ||
34 | * Oct 15, 1997 Farhan Thawar changed wan_encapsulate to add a pad byte of 0 | ||
35 | * Apr 20, 1998 Alan Cox Fixed 2.1 symbols | ||
36 | * May 17, 1998 K. Baranowski Fixed SNAP encapsulation in wan_encapsulate | ||
37 | * Dec 15, 1998 Arnaldo Melo support for firmwares of up to 128000 bytes | ||
38 | * check wandev->setup return value | ||
39 | * Dec 22, 1998 Arnaldo Melo vmalloc/vfree used in device_setup to allocate | ||
40 | * kernel memory and copy configuration data to | ||
41 | * kernel space (for big firmwares) | ||
42 | * Jun 02, 1999 Gideon Hack Updates for Linux 2.0.X and 2.2.X kernels. | ||
43 | *****************************************************************************/ | ||
44 | |||
45 | #include <linux/config.h> | ||
46 | #include <linux/stddef.h> /* offsetof(), etc. */ | ||
47 | #include <linux/errno.h> /* return codes */ | ||
48 | #include <linux/kernel.h> | ||
49 | #include <linux/init.h> | ||
50 | #include <linux/module.h> /* support for loadable modules */ | ||
51 | #include <linux/slab.h> /* kmalloc(), kfree() */ | ||
52 | #include <linux/mm.h> /* verify_area(), etc. */ | ||
53 | #include <linux/string.h> /* inline mem*, str* functions */ | ||
54 | |||
55 | #include <asm/byteorder.h> /* htons(), etc. */ | ||
56 | #include <linux/wanrouter.h> /* WAN router API definitions */ | ||
57 | |||
58 | #include <linux/vmalloc.h> /* vmalloc, vfree */ | ||
59 | #include <asm/uaccess.h> /* copy_to/from_user */ | ||
60 | #include <linux/init.h> /* __initfunc et al. */ | ||
61 | #include <net/syncppp.h> | ||
62 | |||
63 | #define KMEM_SAFETYZONE 8 | ||
64 | |||
65 | /***********FOR DEBUGGING PURPOSES********************************************* | ||
66 | static void * dbg_kmalloc(unsigned int size, int prio, int line) { | ||
67 | int i = 0; | ||
68 | void * v = kmalloc(size+sizeof(unsigned int)+2*KMEM_SAFETYZONE*8,prio); | ||
69 | char * c1 = v; | ||
70 | c1 += sizeof(unsigned int); | ||
71 | *((unsigned int *)v) = size; | ||
72 | |||
73 | for (i = 0; i < KMEM_SAFETYZONE; i++) { | ||
74 | c1[0] = 'D'; c1[1] = 'E'; c1[2] = 'A'; c1[3] = 'D'; | ||
75 | c1[4] = 'B'; c1[5] = 'E'; c1[6] = 'E'; c1[7] = 'F'; | ||
76 | c1 += 8; | ||
77 | } | ||
78 | c1 += size; | ||
79 | for (i = 0; i < KMEM_SAFETYZONE; i++) { | ||
80 | c1[0] = 'M'; c1[1] = 'U'; c1[2] = 'N'; c1[3] = 'G'; | ||
81 | c1[4] = 'W'; c1[5] = 'A'; c1[6] = 'L'; c1[7] = 'L'; | ||
82 | c1 += 8; | ||
83 | } | ||
84 | v = ((char *)v) + sizeof(unsigned int) + KMEM_SAFETYZONE*8; | ||
85 | printk(KERN_INFO "line %d kmalloc(%d,%d) = %p\n",line,size,prio,v); | ||
86 | return v; | ||
87 | } | ||
88 | static void dbg_kfree(void * v, int line) { | ||
89 | unsigned int * sp = (unsigned int *)(((char *)v) - (sizeof(unsigned int) + KMEM_SAFETYZONE*8)); | ||
90 | unsigned int size = *sp; | ||
91 | char * c1 = ((char *)v) - KMEM_SAFETYZONE*8; | ||
92 | int i = 0; | ||
93 | for (i = 0; i < KMEM_SAFETYZONE; i++) { | ||
94 | if ( c1[0] != 'D' || c1[1] != 'E' || c1[2] != 'A' || c1[3] != 'D' | ||
95 | || c1[4] != 'B' || c1[5] != 'E' || c1[6] != 'E' || c1[7] != 'F') { | ||
96 | printk(KERN_INFO "kmalloced block at %p has been corrupted (underrun)!\n",v); | ||
97 | printk(KERN_INFO " %4x: %2x %2x %2x %2x %2x %2x %2x %2x\n", i*8, | ||
98 | c1[0],c1[1],c1[2],c1[3],c1[4],c1[5],c1[6],c1[7] ); | ||
99 | } | ||
100 | c1 += 8; | ||
101 | } | ||
102 | c1 += size; | ||
103 | for (i = 0; i < KMEM_SAFETYZONE; i++) { | ||
104 | if ( c1[0] != 'M' || c1[1] != 'U' || c1[2] != 'N' || c1[3] != 'G' | ||
105 | || c1[4] != 'W' || c1[5] != 'A' || c1[6] != 'L' || c1[7] != 'L' | ||
106 | ) { | ||
107 | printk(KERN_INFO "kmalloced block at %p has been corrupted (overrun):\n",v); | ||
108 | printk(KERN_INFO " %4x: %2x %2x %2x %2x %2x %2x %2x %2x\n", i*8, | ||
109 | c1[0],c1[1],c1[2],c1[3],c1[4],c1[5],c1[6],c1[7] ); | ||
110 | } | ||
111 | c1 += 8; | ||
112 | } | ||
113 | printk(KERN_INFO "line %d kfree(%p)\n",line,v); | ||
114 | v = ((char *)v) - (sizeof(unsigned int) + KMEM_SAFETYZONE*8); | ||
115 | kfree(v); | ||
116 | } | ||
117 | |||
118 | #define kmalloc(x,y) dbg_kmalloc(x,y,__LINE__) | ||
119 | #define kfree(x) dbg_kfree(x,__LINE__) | ||
120 | *****************************************************************************/ | ||
121 | |||
122 | /* | ||
123 | * Function Prototypes | ||
124 | */ | ||
125 | |||
126 | /* | ||
127 | * WAN device IOCTL handlers | ||
128 | */ | ||
129 | |||
130 | static int wanrouter_device_setup(struct wan_device *wandev, | ||
131 | wandev_conf_t __user *u_conf); | ||
132 | static int wanrouter_device_stat(struct wan_device *wandev, | ||
133 | wandev_stat_t __user *u_stat); | ||
134 | static int wanrouter_device_shutdown(struct wan_device *wandev); | ||
135 | static int wanrouter_device_new_if(struct wan_device *wandev, | ||
136 | wanif_conf_t __user *u_conf); | ||
137 | static int wanrouter_device_del_if(struct wan_device *wandev, | ||
138 | char __user *u_name); | ||
139 | |||
140 | /* | ||
141 | * Miscellaneous | ||
142 | */ | ||
143 | |||
144 | static struct wan_device *wanrouter_find_device(char *name); | ||
145 | static int wanrouter_delete_interface(struct wan_device *wandev, char *name); | ||
146 | void lock_adapter_irq(spinlock_t *lock, unsigned long *smp_flags); | ||
147 | void unlock_adapter_irq(spinlock_t *lock, unsigned long *smp_flags); | ||
148 | |||
149 | |||
150 | |||
151 | /* | ||
152 | * Global Data | ||
153 | */ | ||
154 | |||
155 | static char wanrouter_fullname[] = "Sangoma WANPIPE Router"; | ||
156 | static char wanrouter_copyright[] = "(c) 1995-2000 Sangoma Technologies Inc."; | ||
157 | static char wanrouter_modname[] = ROUTER_NAME; /* short module name */ | ||
158 | struct wan_device* wanrouter_router_devlist; /* list of registered devices */ | ||
159 | |||
160 | /* | ||
161 | * Organize Unique Identifiers for encapsulation/decapsulation | ||
162 | */ | ||
163 | |||
164 | static unsigned char wanrouter_oui_ether[] = { 0x00, 0x00, 0x00 }; | ||
165 | #if 0 | ||
166 | static unsigned char wanrouter_oui_802_2[] = { 0x00, 0x80, 0xC2 }; | ||
167 | #endif | ||
168 | |||
169 | static int __init wanrouter_init(void) | ||
170 | { | ||
171 | int err; | ||
172 | |||
173 | printk(KERN_INFO "%s v%u.%u %s\n", | ||
174 | wanrouter_fullname, ROUTER_VERSION, ROUTER_RELEASE, | ||
175 | wanrouter_copyright); | ||
176 | |||
177 | err = wanrouter_proc_init(); | ||
178 | if (err) | ||
179 | printk(KERN_INFO "%s: can't create entry in proc filesystem!\n", | ||
180 | wanrouter_modname); | ||
181 | |||
182 | return err; | ||
183 | } | ||
184 | |||
185 | static void __exit wanrouter_cleanup (void) | ||
186 | { | ||
187 | wanrouter_proc_cleanup(); | ||
188 | } | ||
189 | |||
190 | /* | ||
191 | * This is just plain dumb. We should move the bugger to drivers/net/wan, | ||
192 | * slap it first in directory and make it module_init(). The only reason | ||
193 | * for subsys_initcall() here is that net goes after drivers (why, BTW?) | ||
194 | */ | ||
195 | subsys_initcall(wanrouter_init); | ||
196 | module_exit(wanrouter_cleanup); | ||
197 | |||
198 | /* | ||
199 | * Kernel APIs | ||
200 | */ | ||
201 | |||
202 | /* | ||
203 | * Register WAN device. | ||
204 | * o verify device credentials | ||
205 | * o create an entry for the device in the /proc/net/router directory | ||
206 | * o initialize internally maintained fields of the wan_device structure | ||
207 | * o link device data space to a singly-linked list | ||
208 | * o if it's the first device, then start kernel 'thread' | ||
209 | * o increment module use count | ||
210 | * | ||
211 | * Return: | ||
212 | * 0 Ok | ||
213 | * < 0 error. | ||
214 | * | ||
215 | * Context: process | ||
216 | */ | ||
217 | |||
218 | |||
219 | int register_wan_device(struct wan_device *wandev) | ||
220 | { | ||
221 | int err, namelen; | ||
222 | |||
223 | if ((wandev == NULL) || (wandev->magic != ROUTER_MAGIC) || | ||
224 | (wandev->name == NULL)) | ||
225 | return -EINVAL; | ||
226 | |||
227 | namelen = strlen(wandev->name); | ||
228 | if (!namelen || (namelen > WAN_DRVNAME_SZ)) | ||
229 | return -EINVAL; | ||
230 | |||
231 | if (wanrouter_find_device(wandev->name)) | ||
232 | return -EEXIST; | ||
233 | |||
234 | #ifdef WANDEBUG | ||
235 | printk(KERN_INFO "%s: registering WAN device %s\n", | ||
236 | wanrouter_modname, wandev->name); | ||
237 | #endif | ||
238 | |||
239 | /* | ||
240 | * Register /proc directory entry | ||
241 | */ | ||
242 | err = wanrouter_proc_add(wandev); | ||
243 | if (err) { | ||
244 | printk(KERN_INFO | ||
245 | "%s: can't create /proc/net/router/%s entry!\n", | ||
246 | wanrouter_modname, wandev->name); | ||
247 | return err; | ||
248 | } | ||
249 | |||
250 | /* | ||
251 | * Initialize fields of the wan_device structure maintained by the | ||
252 | * router and update local data. | ||
253 | */ | ||
254 | |||
255 | wandev->ndev = 0; | ||
256 | wandev->dev = NULL; | ||
257 | wandev->next = wanrouter_router_devlist; | ||
258 | wanrouter_router_devlist = wandev; | ||
259 | return 0; | ||
260 | } | ||
261 | |||
262 | /* | ||
263 | * Unregister WAN device. | ||
264 | * o shut down device | ||
265 | * o unlink device data space from the linked list | ||
266 | * o delete device entry in the /proc/net/router directory | ||
267 | * o decrement module use count | ||
268 | * | ||
269 | * Return: 0 Ok | ||
270 | * <0 error. | ||
271 | * Context: process | ||
272 | */ | ||
273 | |||
274 | |||
275 | int unregister_wan_device(char *name) | ||
276 | { | ||
277 | struct wan_device *wandev, *prev; | ||
278 | |||
279 | if (name == NULL) | ||
280 | return -EINVAL; | ||
281 | |||
282 | for (wandev = wanrouter_router_devlist, prev = NULL; | ||
283 | wandev && strcmp(wandev->name, name); | ||
284 | prev = wandev, wandev = wandev->next) | ||
285 | ; | ||
286 | if (wandev == NULL) | ||
287 | return -ENODEV; | ||
288 | |||
289 | #ifdef WANDEBUG | ||
290 | printk(KERN_INFO "%s: unregistering WAN device %s\n", | ||
291 | wanrouter_modname, name); | ||
292 | #endif | ||
293 | |||
294 | if (wandev->state != WAN_UNCONFIGURED) | ||
295 | wanrouter_device_shutdown(wandev); | ||
296 | |||
297 | if (prev) | ||
298 | prev->next = wandev->next; | ||
299 | else | ||
300 | wanrouter_router_devlist = wandev->next; | ||
301 | |||
302 | wanrouter_proc_delete(wandev); | ||
303 | return 0; | ||
304 | } | ||
305 | |||
306 | /* | ||
307 | * Encapsulate packet. | ||
308 | * | ||
309 | * Return: encapsulation header size | ||
310 | * < 0 - unsupported Ethertype | ||
311 | * | ||
312 | * Notes: | ||
313 | * 1. This function may be called on interrupt context. | ||
314 | */ | ||
315 | |||
316 | |||
317 | int wanrouter_encapsulate(struct sk_buff *skb, struct net_device *dev, | ||
318 | unsigned short type) | ||
319 | { | ||
320 | int hdr_len = 0; | ||
321 | |||
322 | switch (type) { | ||
323 | case ETH_P_IP: /* IP datagram encapsulation */ | ||
324 | hdr_len += 1; | ||
325 | skb_push(skb, 1); | ||
326 | skb->data[0] = NLPID_IP; | ||
327 | break; | ||
328 | |||
329 | case ETH_P_IPX: /* SNAP encapsulation */ | ||
330 | case ETH_P_ARP: | ||
331 | hdr_len += 7; | ||
332 | skb_push(skb, 7); | ||
333 | skb->data[0] = 0; | ||
334 | skb->data[1] = NLPID_SNAP; | ||
335 | memcpy(&skb->data[2], wanrouter_oui_ether, | ||
336 | sizeof(wanrouter_oui_ether)); | ||
337 | *((unsigned short*)&skb->data[5]) = htons(type); | ||
338 | break; | ||
339 | |||
340 | default: /* Unknown packet type */ | ||
341 | printk(KERN_INFO | ||
342 | "%s: unsupported Ethertype 0x%04X on interface %s!\n", | ||
343 | wanrouter_modname, type, dev->name); | ||
344 | hdr_len = -EINVAL; | ||
345 | } | ||
346 | return hdr_len; | ||
347 | } | ||
348 | |||
349 | |||
350 | /* | ||
351 | * Decapsulate packet. | ||
352 | * | ||
353 | * Return: Ethertype (in network order) | ||
354 | * 0 unknown encapsulation | ||
355 | * | ||
356 | * Notes: | ||
357 | * 1. This function may be called on interrupt context. | ||
358 | */ | ||
359 | |||
360 | |||
361 | unsigned short wanrouter_type_trans(struct sk_buff *skb, struct net_device *dev) | ||
362 | { | ||
363 | int cnt = skb->data[0] ? 0 : 1; /* there may be a pad present */ | ||
364 | unsigned short ethertype; | ||
365 | |||
366 | switch (skb->data[cnt]) { | ||
367 | case NLPID_IP: /* IP datagramm */ | ||
368 | ethertype = htons(ETH_P_IP); | ||
369 | cnt += 1; | ||
370 | break; | ||
371 | |||
372 | case NLPID_SNAP: /* SNAP encapsulation */ | ||
373 | if (memcmp(&skb->data[cnt + 1], wanrouter_oui_ether, | ||
374 | sizeof(wanrouter_oui_ether))){ | ||
375 | printk(KERN_INFO | ||
376 | "%s: unsupported SNAP OUI %02X-%02X-%02X " | ||
377 | "on interface %s!\n", wanrouter_modname, | ||
378 | skb->data[cnt+1], skb->data[cnt+2], | ||
379 | skb->data[cnt+3], dev->name); | ||
380 | return 0; | ||
381 | } | ||
382 | ethertype = *((unsigned short*)&skb->data[cnt+4]); | ||
383 | cnt += 6; | ||
384 | break; | ||
385 | |||
386 | /* add other protocols, e.g. CLNP, ESIS, ISIS, if needed */ | ||
387 | |||
388 | default: | ||
389 | printk(KERN_INFO | ||
390 | "%s: unsupported NLPID 0x%02X on interface %s!\n", | ||
391 | wanrouter_modname, skb->data[cnt], dev->name); | ||
392 | return 0; | ||
393 | } | ||
394 | skb->protocol = ethertype; | ||
395 | skb->pkt_type = PACKET_HOST; /* Physically point to point */ | ||
396 | skb_pull(skb, cnt); | ||
397 | skb->mac.raw = skb->data; | ||
398 | return ethertype; | ||
399 | } | ||
400 | |||
401 | |||
402 | /* | ||
403 | * WAN device IOCTL. | ||
404 | * o find WAN device associated with this node | ||
405 | * o execute requested action or pass command to the device driver | ||
406 | */ | ||
407 | |||
408 | int wanrouter_ioctl(struct inode *inode, struct file *file, | ||
409 | unsigned int cmd, unsigned long arg) | ||
410 | { | ||
411 | int err = 0; | ||
412 | struct proc_dir_entry *dent; | ||
413 | struct wan_device *wandev; | ||
414 | void __user *data = (void __user *)arg; | ||
415 | |||
416 | if (!capable(CAP_NET_ADMIN)) | ||
417 | return -EPERM; | ||
418 | |||
419 | if ((cmd >> 8) != ROUTER_IOCTL) | ||
420 | return -EINVAL; | ||
421 | |||
422 | dent = PDE(inode); | ||
423 | if ((dent == NULL) || (dent->data == NULL)) | ||
424 | return -EINVAL; | ||
425 | |||
426 | wandev = dent->data; | ||
427 | if (wandev->magic != ROUTER_MAGIC) | ||
428 | return -EINVAL; | ||
429 | |||
430 | switch (cmd) { | ||
431 | case ROUTER_SETUP: | ||
432 | err = wanrouter_device_setup(wandev, data); | ||
433 | break; | ||
434 | |||
435 | case ROUTER_DOWN: | ||
436 | err = wanrouter_device_shutdown(wandev); | ||
437 | break; | ||
438 | |||
439 | case ROUTER_STAT: | ||
440 | err = wanrouter_device_stat(wandev, data); | ||
441 | break; | ||
442 | |||
443 | case ROUTER_IFNEW: | ||
444 | err = wanrouter_device_new_if(wandev, data); | ||
445 | break; | ||
446 | |||
447 | case ROUTER_IFDEL: | ||
448 | err = wanrouter_device_del_if(wandev, data); | ||
449 | break; | ||
450 | |||
451 | case ROUTER_IFSTAT: | ||
452 | break; | ||
453 | |||
454 | default: | ||
455 | if ((cmd >= ROUTER_USER) && | ||
456 | (cmd <= ROUTER_USER_MAX) && | ||
457 | wandev->ioctl) | ||
458 | err = wandev->ioctl(wandev, cmd, arg); | ||
459 | else err = -EINVAL; | ||
460 | } | ||
461 | return err; | ||
462 | } | ||
463 | |||
464 | /* | ||
465 | * WAN Driver IOCTL Handlers | ||
466 | */ | ||
467 | |||
468 | /* | ||
469 | * Setup WAN link device. | ||
470 | * o verify user address space | ||
471 | * o allocate kernel memory and copy configuration data to kernel space | ||
472 | * o if configuration data includes extension, copy it to kernel space too | ||
473 | * o call driver's setup() entry point | ||
474 | */ | ||
475 | |||
476 | static int wanrouter_device_setup(struct wan_device *wandev, | ||
477 | wandev_conf_t __user *u_conf) | ||
478 | { | ||
479 | void *data = NULL; | ||
480 | wandev_conf_t *conf; | ||
481 | int err = -EINVAL; | ||
482 | |||
483 | if (wandev->setup == NULL) { /* Nothing to do ? */ | ||
484 | printk(KERN_INFO "%s: ERROR, No setup script: wandev->setup()\n", | ||
485 | wandev->name); | ||
486 | return 0; | ||
487 | } | ||
488 | |||
489 | conf = kmalloc(sizeof(wandev_conf_t), GFP_KERNEL); | ||
490 | if (conf == NULL){ | ||
491 | printk(KERN_INFO "%s: ERROR, Failed to allocate kernel memory !\n", | ||
492 | wandev->name); | ||
493 | return -ENOBUFS; | ||
494 | } | ||
495 | |||
496 | if (copy_from_user(conf, u_conf, sizeof(wandev_conf_t))) { | ||
497 | printk(KERN_INFO "%s: Failed to copy user config data to kernel space!\n", | ||
498 | wandev->name); | ||
499 | kfree(conf); | ||
500 | return -EFAULT; | ||
501 | } | ||
502 | |||
503 | if (conf->magic != ROUTER_MAGIC) { | ||
504 | kfree(conf); | ||
505 | printk(KERN_INFO "%s: ERROR, Invalid MAGIC Number\n", | ||
506 | wandev->name); | ||
507 | return -EINVAL; | ||
508 | } | ||
509 | |||
510 | if (conf->data_size && conf->data) { | ||
511 | if (conf->data_size > 128000 || conf->data_size < 0) { | ||
512 | printk(KERN_INFO | ||
513 | "%s: ERROR, Invalid firmware data size %i !\n", | ||
514 | wandev->name, conf->data_size); | ||
515 | kfree(conf); | ||
516 | return -EINVAL; | ||
517 | } | ||
518 | |||
519 | data = vmalloc(conf->data_size); | ||
520 | if (!data) { | ||
521 | printk(KERN_INFO | ||
522 | "%s: ERROR, Faild allocate kernel memory !\n", | ||
523 | wandev->name); | ||
524 | kfree(conf); | ||
525 | return -ENOBUFS; | ||
526 | } | ||
527 | if (!copy_from_user(data, conf->data, conf->data_size)) { | ||
528 | conf->data = data; | ||
529 | err = wandev->setup(wandev, conf); | ||
530 | } else { | ||
531 | printk(KERN_INFO | ||
532 | "%s: ERROR, Faild to copy from user data !\n", | ||
533 | wandev->name); | ||
534 | err = -EFAULT; | ||
535 | } | ||
536 | vfree(data); | ||
537 | } else { | ||
538 | printk(KERN_INFO | ||
539 | "%s: ERROR, No firmware found ! Firmware size = %i !\n", | ||
540 | wandev->name, conf->data_size); | ||
541 | } | ||
542 | |||
543 | kfree(conf); | ||
544 | return err; | ||
545 | } | ||
546 | |||
547 | /* | ||
548 | * Shutdown WAN device. | ||
549 | * o delete all not opened logical channels for this device | ||
550 | * o call driver's shutdown() entry point | ||
551 | */ | ||
552 | |||
553 | static int wanrouter_device_shutdown(struct wan_device *wandev) | ||
554 | { | ||
555 | struct net_device *dev; | ||
556 | int err=0; | ||
557 | |||
558 | if (wandev->state == WAN_UNCONFIGURED) | ||
559 | return 0; | ||
560 | |||
561 | printk(KERN_INFO "\n%s: Shutting Down!\n",wandev->name); | ||
562 | |||
563 | for (dev = wandev->dev; dev;) { | ||
564 | err = wanrouter_delete_interface(wandev, dev->name); | ||
565 | if (err) | ||
566 | return err; | ||
567 | /* The above function deallocates the current dev | ||
568 | * structure. Therefore, we cannot use dev->priv | ||
569 | * as the next element: wandev->dev points to the | ||
570 | * next element */ | ||
571 | dev = wandev->dev; | ||
572 | } | ||
573 | |||
574 | if (wandev->ndev) | ||
575 | return -EBUSY; /* there are opened interfaces */ | ||
576 | |||
577 | if (wandev->shutdown) | ||
578 | err=wandev->shutdown(wandev); | ||
579 | |||
580 | return err; | ||
581 | } | ||
582 | |||
583 | /* | ||
584 | * Get WAN device status & statistics. | ||
585 | */ | ||
586 | |||
587 | static int wanrouter_device_stat(struct wan_device *wandev, | ||
588 | wandev_stat_t __user *u_stat) | ||
589 | { | ||
590 | wandev_stat_t stat; | ||
591 | |||
592 | memset(&stat, 0, sizeof(stat)); | ||
593 | |||
594 | /* Ask device driver to update device statistics */ | ||
595 | if ((wandev->state != WAN_UNCONFIGURED) && wandev->update) | ||
596 | wandev->update(wandev); | ||
597 | |||
598 | /* Fill out structure */ | ||
599 | stat.ndev = wandev->ndev; | ||
600 | stat.state = wandev->state; | ||
601 | |||
602 | if (copy_to_user(u_stat, &stat, sizeof(stat))) | ||
603 | return -EFAULT; | ||
604 | |||
605 | return 0; | ||
606 | } | ||
607 | |||
608 | /* | ||
609 | * Create new WAN interface. | ||
610 | * o verify user address space | ||
611 | * o copy configuration data to kernel address space | ||
612 | * o allocate network interface data space | ||
613 | * o call driver's new_if() entry point | ||
614 | * o make sure there is no interface name conflict | ||
615 | * o register network interface | ||
616 | */ | ||
617 | |||
618 | static int wanrouter_device_new_if(struct wan_device *wandev, | ||
619 | wanif_conf_t __user *u_conf) | ||
620 | { | ||
621 | wanif_conf_t *cnf; | ||
622 | struct net_device *dev = NULL; | ||
623 | #ifdef CONFIG_WANPIPE_MULTPPP | ||
624 | struct ppp_device *pppdev=NULL; | ||
625 | #endif | ||
626 | int err; | ||
627 | |||
628 | if ((wandev->state == WAN_UNCONFIGURED) || (wandev->new_if == NULL)) | ||
629 | return -ENODEV; | ||
630 | |||
631 | cnf = kmalloc(sizeof(wanif_conf_t), GFP_KERNEL); | ||
632 | if (!cnf) | ||
633 | return -ENOBUFS; | ||
634 | |||
635 | err = -EFAULT; | ||
636 | if (copy_from_user(cnf, u_conf, sizeof(wanif_conf_t))) | ||
637 | goto out; | ||
638 | |||
639 | err = -EINVAL; | ||
640 | if (cnf->magic != ROUTER_MAGIC) | ||
641 | goto out; | ||
642 | |||
643 | if (cnf->config_id == WANCONFIG_MPPP) { | ||
644 | #ifdef CONFIG_WANPIPE_MULTPPP | ||
645 | pppdev = kmalloc(sizeof(struct ppp_device), GFP_KERNEL); | ||
646 | err = -ENOBUFS; | ||
647 | if (pppdev == NULL) | ||
648 | goto out; | ||
649 | memset(pppdev, 0, sizeof(struct ppp_device)); | ||
650 | pppdev->dev = kmalloc(sizeof(struct net_device), GFP_KERNEL); | ||
651 | if (pppdev->dev == NULL) { | ||
652 | kfree(pppdev); | ||
653 | err = -ENOBUFS; | ||
654 | goto out; | ||
655 | } | ||
656 | memset(pppdev->dev, 0, sizeof(struct net_device)); | ||
657 | err = wandev->new_if(wandev, (struct net_device *)pppdev, cnf); | ||
658 | dev = pppdev->dev; | ||
659 | #else | ||
660 | printk(KERN_INFO "%s: Wanpipe Mulit-Port PPP support has not been compiled in!\n", | ||
661 | wandev->name); | ||
662 | err = -EPROTONOSUPPORT; | ||
663 | goto out; | ||
664 | #endif | ||
665 | } else { | ||
666 | dev = kmalloc(sizeof(struct net_device), GFP_KERNEL); | ||
667 | err = -ENOBUFS; | ||
668 | if (dev == NULL) | ||
669 | goto out; | ||
670 | memset(dev, 0, sizeof(struct net_device)); | ||
671 | err = wandev->new_if(wandev, dev, cnf); | ||
672 | } | ||
673 | |||
674 | if (!err) { | ||
675 | /* Register network interface. This will invoke init() | ||
676 | * function supplied by the driver. If device registered | ||
677 | * successfully, add it to the interface list. | ||
678 | */ | ||
679 | |||
680 | if (dev->name == NULL) { | ||
681 | err = -EINVAL; | ||
682 | } else { | ||
683 | |||
684 | #ifdef WANDEBUG | ||
685 | printk(KERN_INFO "%s: registering interface %s...\n", | ||
686 | wanrouter_modname, dev->name); | ||
687 | #endif | ||
688 | |||
689 | err = register_netdev(dev); | ||
690 | if (!err) { | ||
691 | struct net_device *slave = NULL; | ||
692 | unsigned long smp_flags=0; | ||
693 | |||
694 | lock_adapter_irq(&wandev->lock, &smp_flags); | ||
695 | |||
696 | if (wandev->dev == NULL) { | ||
697 | wandev->dev = dev; | ||
698 | } else { | ||
699 | for (slave=wandev->dev; | ||
700 | *((struct net_device **)slave->priv); | ||
701 | slave = *((struct net_device **)slave->priv)); | ||
702 | |||
703 | *((struct net_device **)slave->priv) = dev; | ||
704 | } | ||
705 | ++wandev->ndev; | ||
706 | |||
707 | unlock_adapter_irq(&wandev->lock, &smp_flags); | ||
708 | err = 0; /* done !!! */ | ||
709 | goto out; | ||
710 | } | ||
711 | } | ||
712 | if (wandev->del_if) | ||
713 | wandev->del_if(wandev, dev); | ||
714 | } | ||
715 | |||
716 | /* This code has moved from del_if() function */ | ||
717 | if (dev->priv) { | ||
718 | kfree(dev->priv); | ||
719 | dev->priv = NULL; | ||
720 | } | ||
721 | |||
722 | #ifdef CONFIG_WANPIPE_MULTPPP | ||
723 | if (cnf->config_id == WANCONFIG_MPPP) | ||
724 | kfree(pppdev); | ||
725 | else | ||
726 | kfree(dev); | ||
727 | #else | ||
728 | /* Sync PPP is disabled */ | ||
729 | if (cnf->config_id != WANCONFIG_MPPP) | ||
730 | kfree(dev); | ||
731 | #endif | ||
732 | |||
733 | out: | ||
734 | kfree(cnf); | ||
735 | return err; | ||
736 | } | ||
737 | |||
738 | |||
739 | /* | ||
740 | * Delete WAN logical channel. | ||
741 | * o verify user address space | ||
742 | * o copy configuration data to kernel address space | ||
743 | */ | ||
744 | |||
745 | static int wanrouter_device_del_if(struct wan_device *wandev, char __user *u_name) | ||
746 | { | ||
747 | char name[WAN_IFNAME_SZ + 1]; | ||
748 | int err = 0; | ||
749 | |||
750 | if (wandev->state == WAN_UNCONFIGURED) | ||
751 | return -ENODEV; | ||
752 | |||
753 | memset(name, 0, sizeof(name)); | ||
754 | |||
755 | if (copy_from_user(name, u_name, WAN_IFNAME_SZ)) | ||
756 | return -EFAULT; | ||
757 | |||
758 | err = wanrouter_delete_interface(wandev, name); | ||
759 | if (err) | ||
760 | return err; | ||
761 | |||
762 | /* If last interface being deleted, shutdown card | ||
763 | * This helps with administration at leaf nodes | ||
764 | * (You can tell if the person at the other end of the phone | ||
765 | * has an interface configured) and avoids DoS vulnerabilities | ||
766 | * in binary driver files - this fixes a problem with the current | ||
767 | * Sangoma driver going into strange states when all the network | ||
768 | * interfaces are deleted and the link irrecoverably disconnected. | ||
769 | */ | ||
770 | |||
771 | if (!wandev->ndev && wandev->shutdown) | ||
772 | err = wandev->shutdown(wandev); | ||
773 | |||
774 | return err; | ||
775 | } | ||
776 | |||
777 | /* | ||
778 | * Miscellaneous Functions | ||
779 | */ | ||
780 | |||
781 | /* | ||
782 | * Find WAN device by name. | ||
783 | * Return pointer to the WAN device data space or NULL if device not found. | ||
784 | */ | ||
785 | |||
786 | static struct wan_device *wanrouter_find_device(char *name) | ||
787 | { | ||
788 | struct wan_device *wandev; | ||
789 | |||
790 | for (wandev = wanrouter_router_devlist; | ||
791 | wandev && strcmp(wandev->name, name); | ||
792 | wandev = wandev->next); | ||
793 | return wandev; | ||
794 | } | ||
795 | |||
796 | /* | ||
797 | * Delete WAN logical channel identified by its name. | ||
798 | * o find logical channel by its name | ||
799 | * o call driver's del_if() entry point | ||
800 | * o unregister network interface | ||
801 | * o unlink channel data space from linked list of channels | ||
802 | * o release channel data space | ||
803 | * | ||
804 | * Return: 0 success | ||
805 | * -ENODEV channel not found. | ||
806 | * -EBUSY interface is open | ||
807 | * | ||
808 | * Note: If (force != 0), then device will be destroyed even if interface | ||
809 | * associated with it is open. It's caller's responsibility to make | ||
810 | * sure that opened interfaces are not removed! | ||
811 | */ | ||
812 | |||
813 | static int wanrouter_delete_interface(struct wan_device *wandev, char *name) | ||
814 | { | ||
815 | struct net_device *dev = NULL, *prev = NULL; | ||
816 | unsigned long smp_flags=0; | ||
817 | |||
818 | lock_adapter_irq(&wandev->lock, &smp_flags); | ||
819 | dev = wandev->dev; | ||
820 | prev = NULL; | ||
821 | while (dev && strcmp(name, dev->name)) { | ||
822 | struct net_device **slave = dev->priv; | ||
823 | prev = dev; | ||
824 | dev = *slave; | ||
825 | } | ||
826 | unlock_adapter_irq(&wandev->lock, &smp_flags); | ||
827 | |||
828 | if (dev == NULL) | ||
829 | return -ENODEV; /* interface not found */ | ||
830 | |||
831 | if (netif_running(dev)) | ||
832 | return -EBUSY; /* interface in use */ | ||
833 | |||
834 | if (wandev->del_if) | ||
835 | wandev->del_if(wandev, dev); | ||
836 | |||
837 | lock_adapter_irq(&wandev->lock, &smp_flags); | ||
838 | if (prev) { | ||
839 | struct net_device **prev_slave = prev->priv; | ||
840 | struct net_device **slave = dev->priv; | ||
841 | |||
842 | *prev_slave = *slave; | ||
843 | } else { | ||
844 | struct net_device **slave = dev->priv; | ||
845 | wandev->dev = *slave; | ||
846 | } | ||
847 | --wandev->ndev; | ||
848 | unlock_adapter_irq(&wandev->lock, &smp_flags); | ||
849 | |||
850 | printk(KERN_INFO "%s: unregistering '%s'\n", wandev->name, dev->name); | ||
851 | |||
852 | /* Due to new interface linking method using dev->priv, | ||
853 | * this code has moved from del_if() function.*/ | ||
854 | if (dev->priv){ | ||
855 | kfree(dev->priv); | ||
856 | dev->priv=NULL; | ||
857 | } | ||
858 | |||
859 | unregister_netdev(dev); | ||
860 | |||
861 | free_netdev(dev); | ||
862 | |||
863 | return 0; | ||
864 | } | ||
865 | |||
866 | void lock_adapter_irq(spinlock_t *lock, unsigned long *smp_flags) | ||
867 | { | ||
868 | spin_lock_irqsave(lock, *smp_flags); | ||
869 | } | ||
870 | |||
871 | |||
872 | void unlock_adapter_irq(spinlock_t *lock, unsigned long *smp_flags) | ||
873 | { | ||
874 | spin_unlock_irqrestore(lock, *smp_flags); | ||
875 | } | ||
876 | |||
877 | EXPORT_SYMBOL(register_wan_device); | ||
878 | EXPORT_SYMBOL(unregister_wan_device); | ||
879 | EXPORT_SYMBOL(wanrouter_encapsulate); | ||
880 | EXPORT_SYMBOL(wanrouter_type_trans); | ||
881 | EXPORT_SYMBOL(lock_adapter_irq); | ||
882 | EXPORT_SYMBOL(unlock_adapter_irq); | ||
883 | |||
884 | MODULE_LICENSE("GPL"); | ||
885 | |||
886 | /* | ||
887 | * End | ||
888 | */ | ||