summaryrefslogtreecommitdiffstats
path: root/net/wanrouter/wanmain.c
blob: 0c91e4c44e8ac29f252df1e9398d4fa2a70b77fe (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
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
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
457
458
459
460
461
462
463
464
465
466
467
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
519
520
521
522
523
524
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
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
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
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
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
/*****************************************************************************
* 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:
*		 o WAN device managenment (registering, unregistering)
*		 o Network interface management
*		 o Physical connection management (dial-up, incomming calls)
*		 o Logical connection management (switched virtual circuits)
*		 o Protocol encapsulation/decapsulation
*
* Author:	Gene Kozin	<genek@compuserve.com>
*
* Copyright:	(c) 1995-1997 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.
* ============================================================================
* 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)
* May 19, 1999  Arnaldo Melo    __initfunc in wanrouter_init
*****************************************************************************/

#include <linux/config.h>
#include <linux/stddef.h>	/* offsetof(), etc. */
#include <linux/errno.h>	/* return codes */
#include <linux/kernel.h>
#include <linux/module.h>	/* support for loadable modules */
#include <linux/malloc.h>	/* kmalloc(), kfree() */
#include <linux/mm.h>		/* verify_area(), etc. */
#include <linux/string.h>	/* inline mem*, str* functions */
#include <linux/vmalloc.h>	/* vmalloc, vfree */
#include <asm/segment.h>	/* kernel <-> user copy */
#include <asm/byteorder.h>	/* htons(), etc. */
#include <asm/uaccess.h>	/* copy_to/from_user */
#include <linux/wanrouter.h>	/* WAN router API definitions */
#include <linux/init.h>		/* __initfunc et al. */

/****** Defines and Macros **************************************************/

#ifndef	min
#define min(a,b) (((a)<(b))?(a):(b))
#endif
#ifndef	max
#define max(a,b) (((a)>(b))?(a):(b))
#endif

/****** Function Prototypes *************************************************/

/*
 *	Kernel loadable module interface.
 */

#ifdef MODULE
int init_module (void);
void cleanup_module (void);
#endif

/*
 *	WAN device IOCTL handlers 
 */

static int device_setup (wan_device_t* wandev, wandev_conf_t* u_conf);
static int device_stat (wan_device_t* wandev, wandev_stat_t* u_stat);
static int device_shutdown (wan_device_t* wandev);
static int device_new_if (wan_device_t* wandev, wanif_conf_t* u_conf);
static int device_del_if (wan_device_t* wandev, char* u_name);

/*
 *	Miscellaneous 
 */

static wan_device_t* find_device (char* name);
static int delete_interface (wan_device_t* wandev, char* name, int forse);

/*
 *	Global Data
 */

static char fullname[]		= "WAN Router";
static char copyright[]		= "(c) 1995-1997 Sangoma Technologies Inc.";
static char modname[]		= ROUTER_NAME;	/* short module name */
wan_device_t * router_devlist	= NULL;	/* list of registered devices */
static int devcnt		= 0;

/*
 *	Organizationally Unique Identifiers for encapsulation/decapsulation
 */
 
static unsigned char oui_ether[] = { 0x00, 0x00, 0x00 };
#if 0
static unsigned char oui_802_2[] = { 0x00, 0x80, 0xC2 };
#endif

#ifndef MODULE
__initfunc(int wanrouter_init(void))
{
	int err;
	extern int wanpipe_init(void),
		   cyclomx_init(void);

	printk(KERN_INFO "%s v%u.%u %s\n",
		fullname, ROUTER_VERSION, ROUTER_RELEASE, copyright);
	err = wanrouter_proc_init();
	if (err)
		printk(KERN_ERR "%s: can't create entry in proc filesystem!\n",
				modname);		

	/*
	 *	Initialise compiled in boards
	 */		
	 
#ifdef CONFIG_VENDOR_SANGOMA
	wanpipe_init();
#endif	
#ifdef CONFIG_CYCLADES_SYNC
	cyclomx_init();
#endif
	return err;
}

#else

/*
 *	Kernel Loadable Module Entry Points
 */

/*
 *	Module 'insert' entry point.
 *	o print announcement
 *	o initialize static data
 *	o create /proc/net/router directory and static entries
 *
 *	Return:	0	Ok
 *		< 0	error.
 *	Context:	process
 */

int init_module	(void)
{
	int err;

	printk(KERN_INFO "%s v%u.%u %s\n",
		fullname, ROUTER_VERSION, ROUTER_RELEASE, copyright);
	err = wanrouter_proc_init();
	if (err) printk(KERN_ERR
		"%s: can't create entry in proc filesystem!\n", modname);
	return err;
}

/*
 *	Module 'remove' entry point.
 *	o delete /proc/net/router directory and static entries.
 */
 
void cleanup_module (void)
{
	wanrouter_proc_cleanup();
}

#endif

/*
 *	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(wan_device_t* 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 (find_device(wandev->name) != NULL)
		return -EEXIST;

#ifdef WANDEBUG		
	printk(KERN_INFO "%s: registering WAN device %s\n",
		modname, wandev->name);
#endif
	/*
	 *	Register /proc directory entry 
	 */
	err = wanrouter_proc_add(wandev);
	if (err)
	{
		printk(KERN_ERR
			"%s: can't create /proc/net/router/%s entry!\n",
			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 = router_devlist;
	router_devlist = wandev;
	++devcnt;
        MOD_INC_USE_COUNT;	/* prevent module from unloading */
	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)
{
	wan_device_t *wandev, *prev;

	if (name == NULL)
		return -EINVAL;

	for (wandev = 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\n", modname, name);
#endif
	
	if (wandev->state != WAN_UNCONFIGURED)
	{
		while(wandev->dev)
			delete_interface(wandev, wandev->dev->name, 1);
		if (wandev->shutdown)	
			wandev->shutdown(wandev);
	}
	if (prev)
		prev->next = wandev->next;
	else
		router_devlist = wandev->next;
	--devcnt;
	wanrouter_proc_delete(wandev);
        MOD_DEC_USE_COUNT;
	return 0;
}

/*
 *	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 device* dev)
{
	int hdr_len = 0;

	switch (skb->protocol)
	{
	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;
		memcpy(&skb->data[2], oui_ether, sizeof(oui_ether));
		*((unsigned short*)&skb->data[5]) = htons(skb->protocol);
		break;

	default:		/* Unknown packet type */
		printk(KERN_INFO
			"%s: unsupported Ethertype 0x%04X on interface %s!\n",
			modname, skb->protocol, 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.
 */


unsigned short wanrouter_type_trans (struct sk_buff* skb, struct device* dev)
{
	int cnt = skb->data[0] ? 0 : 1;	/* there may be a pad present */
	unsigned short ethertype;

	switch (skb->data[cnt])
	{
	case NLPID_IP:		/* IP datagramm */
		ethertype = htons(ETH_P_IP);
		cnt += 1;
		break;

        case NLPID_SNAP:	/* SNAP encapsulation */
		if (memcmp(&skb->data[cnt + 1], oui_ether, sizeof(oui_ether)))
		{
          		printk(KERN_INFO
				"%s: unsupported SNAP OUI %02X-%02X-%02X "
				"on interface %s!\n", modname,
				skb->data[cnt+1], skb->data[cnt+2],
				skb->data[cnt+3], dev->name);
			;
			return 0;
		}	
		ethertype = *((unsigned short*)&skb->data[cnt+4]);
		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!\n",
			modname, skb->data[cnt], dev->name)
		;
		return 0;
	}
	skb->protocol = ethertype;
	skb->pkt_type = PACKET_HOST;	/*	Physically point to point */
	skb->mac.raw  = skb->data;
	skb_pull(skb, cnt);
	return ethertype;
}

/*
 *	WAN device IOCTL.
 *	o find WAN device associated with this node
 *	o execute requested action or pass command to the device driver
 */

int wanrouter_ioctl(struct inode* inode, struct file* file,
		unsigned int cmd, unsigned long arg)
{
	int err = 0;
	struct proc_dir_entry* dent;
	wan_device_t* wandev;

	if (!capable(CAP_NET_ADMIN)){
		return -EPERM;
	}
		
	if ((cmd >> 8) != ROUTER_IOCTL)
		return -EINVAL;
		
	dent = inode->u.generic_ip;
	if ((dent == NULL) || (dent->data == NULL))
		return -EINVAL;
		
	wandev = dent->data;
	if (wandev->magic != ROUTER_MAGIC)
		return -EINVAL;
		
	switch (cmd)
	{
	case ROUTER_SETUP:
		err = device_setup(wandev, (void*)arg);
		break;

	case ROUTER_DOWN:
		err = device_shutdown(wandev);
		break;

	case ROUTER_STAT:
		err = device_stat(wandev, (void*)arg);
		break;

	case ROUTER_IFNEW:
		err = device_new_if(wandev, (void*)arg);
		break;

	case ROUTER_IFDEL:
		err = device_del_if(wandev, (void*)arg);
		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;
	}
	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 device_setup (wan_device_t* wandev, wandev_conf_t* u_conf)
{
	void* data;
	wandev_conf_t *conf;
	int err= -EINVAL;

	if (wandev->setup == NULL)	/* Nothing to do ? */
		return 0;
	
	conf = kmalloc(sizeof(wandev_conf_t), GFP_KERNEL);
	if (conf == NULL)
		return -ENOBUFS;
		
	if(copy_from_user(conf, u_conf, sizeof(wandev_conf_t)))
	{
		kfree(conf);
		return -EFAULT;
	}
	
	if (conf->magic != ROUTER_MAGIC)
		goto bail;

	if (conf->data_size && conf->data)
	{
		if(conf->data_size > 128000 || conf->data_size < 0){
			goto bail;
		}
		data = vmalloc(conf->data_size);
		if (data)
		{
			if(!copy_from_user(data, conf->data, conf->data_size))
			{
				conf->data=data;
				err = wandev->setup(wandev,conf);
			}
			else 
				err = -EFAULT;
		}
		else
			err = -ENOBUFS;
			
		if (data)
			vfree(data);
	}
bail:
	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 device_shutdown (wan_device_t* wandev)
{
	struct device* dev;

	if (wandev->state == WAN_UNCONFIGURED)
		return 0;
		
	for (dev = wandev->dev; dev;)
	{
		if (delete_interface(wandev, dev->name, 0))
			dev = dev->slave;
	}
	if (wandev->ndev)
		return -EBUSY;	/* there are opened interfaces  */
		
	if (wandev->shutdown)
		return wandev->shutdown(wandev);
	return 0;
}

/*
 *	Get WAN device status & statistics.
 */

static int device_stat (wan_device_t* wandev, wandev_stat_t* 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 device_new_if (wan_device_t* wandev, wanif_conf_t* u_conf)
{
	wanif_conf_t conf;
	struct device* dev;
	int err;

	if ((wandev->state == WAN_UNCONFIGURED) || (wandev->new_if == NULL))
		return -ENODEV;
		
	if(copy_from_user(&conf, u_conf, sizeof(wanif_conf_t)))
		return -EFAULT;
		
	if (conf.magic != ROUTER_MAGIC)
		return -EINVAL;
		
	dev = kmalloc(sizeof(struct device), GFP_KERNEL);
	if (dev == NULL)
		return -ENOBUFS;
		
	memset(dev, 0, sizeof(struct device));
	err = wandev->new_if(wandev, dev, &conf);
	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 if (dev_get(dev->name))
			err = -EEXIST;	/* name already exists */
		else
		{
#ifdef WANDEBUG		
			printk(KERN_INFO "%s: registering interface %s...\n",
				modname, dev->name);
#endif				
			err = register_netdev(dev);
			if (!err)
			{
				cli();	/***** critical section start *****/
				dev->slave = wandev->dev;
				wandev->dev = dev;
				++wandev->ndev;
				sti();	/****** critical section end ******/
				return 0;	/* done !!! */
			}
		}
		if (wandev->del_if)
			wandev->del_if(wandev, dev);
	}
	kfree(dev);
	return err;
}

/*
 *	Delete WAN logical channel.
 *	 o verify user address space
 *	 o copy configuration data to kernel address space
 */

static int device_del_if (wan_device_t* wandev, char* u_name)
{
	char name[WAN_IFNAME_SZ + 1];

	if (wandev->state == WAN_UNCONFIGURED)
		return -ENODEV;
		
	memset(name, 0, sizeof(name));
	if(copy_from_user(name, u_name, WAN_IFNAME_SZ))
		return -EFAULT;
	return delete_interface(wandev, name, 0);
}

/*
 *	Miscellaneous Functions
 */

/*
 *	Find WAN device by name.
 *	Return pointer to the WAN device data space or NULL if device not found.
 */

static wan_device_t* find_device (char* name)
{
	wan_device_t* wandev;

	for (wandev = 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 delete_interface (wan_device_t* wandev, char* name, int force)
{
	struct device *dev, *prev;

	for (dev = wandev->dev, prev = NULL;
		dev && strcmp(name, dev->name);
		prev = dev, dev = dev->slave);

	if (dev == NULL)
		return -ENODEV;	/* interface not found */

	if (dev->start)
	{
		if (force)
		{
			printk(KERN_WARNING
				"%s: deleting opened interface %s!\n",modname, name);
		}
		else
			return -EBUSY;	/* interface in use */
	}
	if (wandev->del_if)
		wandev->del_if(wandev, dev);

	cli();			/***** critical section start *****/
	if (prev)
		prev->slave = dev->slave;
	else
		wandev->dev = dev->slave;
	--wandev->ndev;
	sti();			/****** critical section end ******/

	printk("Unregistering '%s'\n", dev->name);
	unregister_netdev(dev);
	kfree(dev);
	return 0;
}

EXPORT_SYMBOL(register_wan_device);
EXPORT_SYMBOL(unregister_wan_device);
EXPORT_SYMBOL(wanrouter_encapsulate);
EXPORT_SYMBOL(wanrouter_type_trans);

/*
 *	End
 */