OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [net/] [core/] [dv.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1278 phoenix
/*
2
 * INET         An implementation of the TCP/IP protocol suite for the LINUX
3
 *              operating system.  INET is implemented using the  BSD Socket
4
 *              interface as the means of communication with the user level.
5
 *
6
 *              Generic frame diversion
7
 *
8
 * Version:     @(#)eth.c       0.41    09/09/2000
9
 *
10
 * Authors:
11
 *              Benoit LOCHER:  initial integration within the kernel with support for ethernet
12
 *              Dave Miller:    improvement on the code (correctness, performance and source files)
13
 *
14
 */
15
#include <linux/types.h>
16
#include <linux/kernel.h>
17
#include <linux/sched.h>
18
#include <linux/string.h>
19
#include <linux/mm.h>
20
#include <linux/socket.h>
21
#include <linux/in.h>
22
#include <linux/inet.h>
23
#include <linux/ip.h>
24
#include <linux/udp.h>
25
#include <linux/netdevice.h>
26
#include <linux/etherdevice.h>
27
#include <linux/skbuff.h>
28
#include <linux/errno.h>
29
#include <linux/init.h>
30
#include <net/dst.h>
31
#include <net/arp.h>
32
#include <net/sock.h>
33
#include <net/ipv6.h>
34
#include <net/ip.h>
35
#include <asm/uaccess.h>
36
#include <asm/system.h>
37
#include <asm/checksum.h>
38
#include <linux/divert.h>
39
#include <linux/sockios.h>
40
 
41
const char sysctl_divert_version[32]="0.46";    /* Current version */
42
 
43
int __init dv_init(void)
44
{
45
        printk(KERN_INFO "NET4: Frame Diverter %s\n", sysctl_divert_version);
46
        return 0;
47
}
48
 
49
/*
50
 * Allocate a divert_blk for a device. This must be an ethernet nic.
51
 */
52
int alloc_divert_blk(struct net_device *dev)
53
{
54
        int alloc_size = (sizeof(struct divert_blk) + 3) & ~3;
55
 
56
        if (dev->type == ARPHRD_ETHER) {
57
                printk(KERN_DEBUG "divert: allocating divert_blk for %s\n",
58
                       dev->name);
59
 
60
                dev->divert = (struct divert_blk *)
61
                        kmalloc(alloc_size, GFP_KERNEL);
62
                if (dev->divert == NULL) {
63
                        printk(KERN_DEBUG "divert: unable to allocate divert_blk for %s\n",
64
                               dev->name);
65
                        return -ENOMEM;
66
                } else {
67
                        memset(dev->divert, 0, sizeof(struct divert_blk));
68
                }
69
                dev_hold(dev);
70
        } else {
71
                printk(KERN_DEBUG "divert: not allocating divert_blk for non-ethernet device %s\n",
72
                       dev->name);
73
 
74
                dev->divert = NULL;
75
        }
76
        return 0;
77
}
78
 
79
/*
80
 * Free a divert_blk allocated by the above function, if it was
81
 * allocated on that device.
82
 */
83
void free_divert_blk(struct net_device *dev)
84
{
85
        if (dev->divert) {
86
                kfree(dev->divert);
87
                dev->divert=NULL;
88
                dev_put(dev);
89
                printk(KERN_DEBUG "divert: freeing divert_blk for %s\n",
90
                       dev->name);
91
        } else {
92
                printk(KERN_DEBUG "divert: no divert_blk to free, %s not ethernet\n",
93
                       dev->name);
94
        }
95
}
96
 
97
/*
98
 * Adds a tcp/udp (source or dest) port to an array
99
 */
100
int add_port(u16 ports[], u16 port)
101
{
102
        int i;
103
 
104
        if (port == 0)
105
                return -EINVAL;
106
 
107
        /* Storing directly in network format for performance,
108
         * thanks Dave :)
109
         */
110
        port = htons(port);
111
 
112
        for (i = 0; i < MAX_DIVERT_PORTS; i++) {
113
                if (ports[i] == port)
114
                        return -EALREADY;
115
        }
116
 
117
        for (i = 0; i < MAX_DIVERT_PORTS; i++) {
118
                if (ports[i] == 0) {
119
                        ports[i] = port;
120
                        return 0;
121
                }
122
        }
123
 
124
        return -ENOBUFS;
125
}
126
 
127
/*
128
 * Removes a port from an array tcp/udp (source or dest)
129
 */
130
int remove_port(u16 ports[], u16 port)
131
{
132
        int i;
133
 
134
        if (port == 0)
135
                return -EINVAL;
136
 
137
        /* Storing directly in network format for performance,
138
         * thanks Dave !
139
         */
140
        port = htons(port);
141
 
142
        for (i = 0; i < MAX_DIVERT_PORTS; i++) {
143
                if (ports[i] == port) {
144
                        ports[i] = 0;
145
                        return 0;
146
                }
147
        }
148
 
149
        return -EINVAL;
150
}
151
 
152
/* Some basic sanity checks on the arguments passed to divert_ioctl() */
153
int check_args(struct divert_cf *div_cf, struct net_device **dev)
154
{
155
        char devname[32];
156
        int ret;
157
 
158
        if (dev == NULL)
159
                return -EFAULT;
160
 
161
        /* GETVERSION: all other args are unused */
162
        if (div_cf->cmd == DIVCMD_GETVERSION)
163
                return 0;
164
 
165
        /* Network device index should reasonably be between 0 and 1000 :) */
166
        if (div_cf->dev_index < 0 || div_cf->dev_index > 1000)
167
                return -EINVAL;
168
 
169
        /* Let's try to find the ifname */
170
        sprintf(devname, "eth%d", div_cf->dev_index);
171
        *dev = dev_get_by_name(devname);
172
 
173
        /* dev should NOT be null */
174
        if (*dev == NULL)
175
                return -EINVAL;
176
 
177
        ret = 0;
178
 
179
        /* user issuing the ioctl must be a super one :) */
180
        if (!capable(CAP_SYS_ADMIN)) {
181
                ret = -EPERM;
182
                goto out;
183
        }
184
 
185
        /* Device must have a divert_blk member NOT null */
186
        if ((*dev)->divert == NULL)
187
                ret = -EINVAL;
188
out:
189
        dev_put(*dev);
190
        return ret;
191
}
192
 
193
/*
194
 * control function of the diverter
195
 */
196
#define DVDBG(a)        \
197
        printk(KERN_DEBUG "divert_ioctl() line %d %s\n", __LINE__, (a))
198
 
199
int divert_ioctl(unsigned int cmd, struct divert_cf *arg)
200
{
201
        struct divert_cf        div_cf;
202
        struct divert_blk       *div_blk;
203
        struct net_device       *dev;
204
        int                     ret;
205
 
206
        switch (cmd) {
207
        case SIOCGIFDIVERT:
208
                DVDBG("SIOCGIFDIVERT, copy_from_user");
209
                if (copy_from_user(&div_cf, arg, sizeof(struct divert_cf)))
210
                        return -EFAULT;
211
                DVDBG("before check_args");
212
                ret = check_args(&div_cf, &dev);
213
                if (ret)
214
                        return ret;
215
                DVDBG("after checkargs");
216
                div_blk = dev->divert;
217
 
218
                DVDBG("befre switch()");
219
                switch (div_cf.cmd) {
220
                case DIVCMD_GETSTATUS:
221
                        /* Now, just give the user the raw divert block
222
                         * for him to play with :)
223
                         */
224
                        if (copy_to_user(div_cf.arg1.ptr, dev->divert,
225
                                         sizeof(struct divert_blk)))
226
                                return -EFAULT;
227
                        break;
228
 
229
                case DIVCMD_GETVERSION:
230
                        DVDBG("GETVERSION: checking ptr");
231
                        if (div_cf.arg1.ptr == NULL)
232
                                return -EINVAL;
233
                        DVDBG("GETVERSION: copying data to userland");
234
                        if (copy_to_user(div_cf.arg1.ptr,
235
                                         sysctl_divert_version, 32))
236
                                return -EFAULT;
237
                        DVDBG("GETVERSION: data copied");
238
                        break;
239
 
240
                default:
241
                        return -EINVAL;
242
                };
243
 
244
                break;
245
 
246
        case SIOCSIFDIVERT:
247
                if (copy_from_user(&div_cf, arg, sizeof(struct divert_cf)))
248
                        return -EFAULT;
249
 
250
                ret = check_args(&div_cf, &dev);
251
                if (ret)
252
                        return ret;
253
 
254
                div_blk = dev->divert;
255
 
256
                switch(div_cf.cmd) {
257
                case DIVCMD_RESET:
258
                        div_blk->divert = 0;
259
                        div_blk->protos = DIVERT_PROTO_NONE;
260
                        memset(div_blk->tcp_dst, 0,
261
                               MAX_DIVERT_PORTS * sizeof(u16));
262
                        memset(div_blk->tcp_src, 0,
263
                               MAX_DIVERT_PORTS * sizeof(u16));
264
                        memset(div_blk->udp_dst, 0,
265
                               MAX_DIVERT_PORTS * sizeof(u16));
266
                        memset(div_blk->udp_src, 0,
267
                               MAX_DIVERT_PORTS * sizeof(u16));
268
                        return 0;
269
 
270
                case DIVCMD_DIVERT:
271
                        switch(div_cf.arg1.int32) {
272
                        case DIVARG1_ENABLE:
273
                                if (div_blk->divert)
274
                                        return -EALREADY;
275
                                div_blk->divert = 1;
276
                                break;
277
 
278
                        case DIVARG1_DISABLE:
279
                                if (!div_blk->divert)
280
                                        return -EALREADY;
281
                                div_blk->divert = 0;
282
                                break;
283
 
284
                        default:
285
                                return -EINVAL;
286
                        };
287
 
288
                        break;
289
 
290
                case DIVCMD_IP:
291
                        switch(div_cf.arg1.int32) {
292
                        case DIVARG1_ENABLE:
293
                                if (div_blk->protos & DIVERT_PROTO_IP)
294
                                        return -EALREADY;
295
                                div_blk->protos |= DIVERT_PROTO_IP;
296
                                break;
297
 
298
                        case DIVARG1_DISABLE:
299
                                if (!(div_blk->protos & DIVERT_PROTO_IP))
300
                                        return -EALREADY;
301
                                div_blk->protos &= ~DIVERT_PROTO_IP;
302
                                break;
303
 
304
                        default:
305
                                return -EINVAL;
306
                        };
307
 
308
                        break;
309
 
310
                case DIVCMD_TCP:
311
                        switch(div_cf.arg1.int32) {
312
                        case DIVARG1_ENABLE:
313
                                if (div_blk->protos & DIVERT_PROTO_TCP)
314
                                        return -EALREADY;
315
                                div_blk->protos |= DIVERT_PROTO_TCP;
316
                                break;
317
 
318
                        case DIVARG1_DISABLE:
319
                                if (!(div_blk->protos & DIVERT_PROTO_TCP))
320
                                        return -EALREADY;
321
                                div_blk->protos &= ~DIVERT_PROTO_TCP;
322
                                break;
323
 
324
                        default:
325
                                return -EINVAL;
326
                        };
327
 
328
                        break;
329
 
330
                case DIVCMD_TCPDST:
331
                        switch(div_cf.arg1.int32) {
332
                        case DIVARG1_ADD:
333
                                return add_port(div_blk->tcp_dst,
334
                                                div_cf.arg2.uint16);
335
 
336
                        case DIVARG1_REMOVE:
337
                                return remove_port(div_blk->tcp_dst,
338
                                                   div_cf.arg2.uint16);
339
 
340
                        default:
341
                                return -EINVAL;
342
                        };
343
 
344
                        break;
345
 
346
                case DIVCMD_TCPSRC:
347
                        switch(div_cf.arg1.int32) {
348
                        case DIVARG1_ADD:
349
                                return add_port(div_blk->tcp_src,
350
                                                div_cf.arg2.uint16);
351
 
352
                        case DIVARG1_REMOVE:
353
                                return remove_port(div_blk->tcp_src,
354
                                                   div_cf.arg2.uint16);
355
 
356
                        default:
357
                                return -EINVAL;
358
                        };
359
 
360
                        break;
361
 
362
                case DIVCMD_UDP:
363
                        switch(div_cf.arg1.int32) {
364
                        case DIVARG1_ENABLE:
365
                                if (div_blk->protos & DIVERT_PROTO_UDP)
366
                                        return -EALREADY;
367
                                div_blk->protos |= DIVERT_PROTO_UDP;
368
                                break;
369
 
370
                        case DIVARG1_DISABLE:
371
                                if (!(div_blk->protos & DIVERT_PROTO_UDP))
372
                                        return -EALREADY;
373
                                div_blk->protos &= ~DIVERT_PROTO_UDP;
374
                                break;
375
 
376
                        default:
377
                                return -EINVAL;
378
                        };
379
 
380
                        break;
381
 
382
                case DIVCMD_UDPDST:
383
                        switch(div_cf.arg1.int32) {
384
                        case DIVARG1_ADD:
385
                                return add_port(div_blk->udp_dst,
386
                                                div_cf.arg2.uint16);
387
 
388
                        case DIVARG1_REMOVE:
389
                                return remove_port(div_blk->udp_dst,
390
                                                   div_cf.arg2.uint16);
391
 
392
                        default:
393
                                return -EINVAL;
394
                        };
395
 
396
                        break;
397
 
398
                case DIVCMD_UDPSRC:
399
                        switch(div_cf.arg1.int32) {
400
                        case DIVARG1_ADD:
401
                                return add_port(div_blk->udp_src,
402
                                                div_cf.arg2.uint16);
403
 
404
                        case DIVARG1_REMOVE:
405
                                return remove_port(div_blk->udp_src,
406
                                                   div_cf.arg2.uint16);
407
 
408
                        default:
409
                                return -EINVAL;
410
                        };
411
 
412
                        break;
413
 
414
                case DIVCMD_ICMP:
415
                        switch(div_cf.arg1.int32) {
416
                        case DIVARG1_ENABLE:
417
                                if (div_blk->protos & DIVERT_PROTO_ICMP)
418
                                        return -EALREADY;
419
                                div_blk->protos |= DIVERT_PROTO_ICMP;
420
                                break;
421
 
422
                        case DIVARG1_DISABLE:
423
                                if (!(div_blk->protos & DIVERT_PROTO_ICMP))
424
                                        return -EALREADY;
425
                                div_blk->protos &= ~DIVERT_PROTO_ICMP;
426
                                break;
427
 
428
                        default:
429
                                return -EINVAL;
430
                        };
431
 
432
                        break;
433
 
434
                default:
435
                        return -EINVAL;
436
                };
437
 
438
                break;
439
 
440
        default:
441
                return -EINVAL;
442
        };
443
 
444
        return 0;
445
}
446
 
447
 
448
/*
449
 * Check if packet should have its dest mac address set to the box itself
450
 * for diversion
451
 */
452
 
453
#define ETH_DIVERT_FRAME(skb) \
454
        memcpy(skb->mac.ethernet, skb->dev->dev_addr, ETH_ALEN); \
455
        skb->pkt_type=PACKET_HOST
456
 
457
void divert_frame(struct sk_buff *skb)
458
{
459
        struct ethhdr                   *eth = skb->mac.ethernet;
460
        struct iphdr                    *iph;
461
        struct tcphdr                   *tcph;
462
        struct udphdr                   *udph;
463
        struct divert_blk               *divert = skb->dev->divert;
464
        int                             i, src, dst;
465
        unsigned char                   *skb_data_end = skb->data + skb->len;
466
 
467
        /* Packet is already aimed at us, return */
468
        if (!memcmp(eth, skb->dev->dev_addr, ETH_ALEN))
469
                return;
470
 
471
        /* proto is not IP, do nothing */
472
        if (eth->h_proto != htons(ETH_P_IP))
473
                return;
474
 
475
        /* Divert all IP frames ? */
476
        if (divert->protos & DIVERT_PROTO_IP) {
477
                ETH_DIVERT_FRAME(skb);
478
                return;
479
        }
480
 
481
        /* Check for possible (maliciously) malformed IP frame (thanks Dave) */
482
        iph = (struct iphdr *) skb->data;
483
        if (((iph->ihl<<2)+(unsigned char*)(iph)) >= skb_data_end) {
484
                printk(KERN_INFO "divert: malformed IP packet !\n");
485
                return;
486
        }
487
 
488
        switch (iph->protocol) {
489
        /* Divert all ICMP frames ? */
490
        case IPPROTO_ICMP:
491
                if (divert->protos & DIVERT_PROTO_ICMP) {
492
                        ETH_DIVERT_FRAME(skb);
493
                        return;
494
                }
495
                break;
496
 
497
        /* Divert all TCP frames ? */
498
        case IPPROTO_TCP:
499
                if (divert->protos & DIVERT_PROTO_TCP) {
500
                        ETH_DIVERT_FRAME(skb);
501
                        return;
502
                }
503
 
504
                /* Check for possible (maliciously) malformed IP
505
                 * frame (thanx Dave)
506
                 */
507
                tcph = (struct tcphdr *)
508
                        (((unsigned char *)iph) + (iph->ihl<<2));
509
                if (((unsigned char *)(tcph+1)) >= skb_data_end) {
510
                        printk(KERN_INFO "divert: malformed TCP packet !\n");
511
                        return;
512
                }
513
 
514
                /* Divert some tcp dst/src ports only ?*/
515
                for (i = 0; i < MAX_DIVERT_PORTS; i++) {
516
                        dst = divert->tcp_dst[i];
517
                        src = divert->tcp_src[i];
518
                        if ((dst && dst == tcph->dest) ||
519
                            (src && src == tcph->source)) {
520
                                ETH_DIVERT_FRAME(skb);
521
                                return;
522
                        }
523
                }
524
                break;
525
 
526
        /* Divert all UDP frames ? */
527
        case IPPROTO_UDP:
528
                if (divert->protos & DIVERT_PROTO_UDP) {
529
                        ETH_DIVERT_FRAME(skb);
530
                        return;
531
                }
532
 
533
                /* Check for possible (maliciously) malformed IP
534
                 * packet (thanks Dave)
535
                 */
536
                udph = (struct udphdr *)
537
                        (((unsigned char *)iph) + (iph->ihl<<2));
538
                if (((unsigned char *)(udph+1)) >= skb_data_end) {
539
                        printk(KERN_INFO
540
                               "divert: malformed UDP packet !\n");
541
                        return;
542
                }
543
 
544
                /* Divert some udp dst/src ports only ? */
545
                for (i = 0; i < MAX_DIVERT_PORTS; i++) {
546
                        dst = divert->udp_dst[i];
547
                        src = divert->udp_src[i];
548
                        if ((dst && dst == udph->dest) ||
549
                            (src && src == udph->source)) {
550
                                ETH_DIVERT_FRAME(skb);
551
                                return;
552
                        }
553
                }
554
                break;
555
        };
556
 
557
        return;
558
}
559
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.