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

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [net/] [ipv6/] [netfilter/] [ip6_tables.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1275 phoenix
/*
2
 * Packet matching code.
3
 *
4
 * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
5
 * Copyright (C) 2000-2002 Netfilter core team <coreteam@netfilter.org>
6
 *
7
 * 19 Jan 2002 Harald Welte <laforge@gnumonks.org>
8
 *      - increase module usage count as soon as we have rules inside
9
 *        a table
10
 * 06 Jun 2002 Andras Kis-Szabo <kisza@sch.bme.hu>
11
 *      - new extension header parser code
12
 */
13
#include <linux/config.h>
14
#include <linux/skbuff.h>
15
#include <linux/kmod.h>
16
#include <linux/vmalloc.h>
17
#include <linux/netdevice.h>
18
#include <linux/module.h>
19
#include <linux/tcp.h>
20
#include <linux/udp.h>
21
#include <linux/icmpv6.h>
22
#include <net/ip.h>
23
#include <asm/uaccess.h>
24
#include <asm/semaphore.h>
25
#include <linux/proc_fs.h>
26
 
27
#include <linux/netfilter_ipv6/ip6_tables.h>
28
 
29
#define IPV6_HDR_LEN    (sizeof(struct ipv6hdr))
30
#define IPV6_OPTHDR_LEN (sizeof(struct ipv6_opt_hdr))
31
 
32
/*#define DEBUG_IP_FIREWALL*/
33
/*#define DEBUG_ALLOW_ALL*/ /* Useful for remote debugging */
34
/*#define DEBUG_IP_FIREWALL_USER*/
35
 
36
#ifdef DEBUG_IP_FIREWALL
37
#define dprintf(format, args...)  printk(format , ## args)
38
#else
39
#define dprintf(format, args...)
40
#endif
41
 
42
#ifdef DEBUG_IP_FIREWALL_USER
43
#define duprintf(format, args...) printk(format , ## args)
44
#else
45
#define duprintf(format, args...)
46
#endif
47
 
48
#ifdef CONFIG_NETFILTER_DEBUG
49
#define IP_NF_ASSERT(x)                                         \
50
do {                                                            \
51
        if (!(x))                                               \
52
                printk("IP_NF_ASSERT: %s:%s:%u\n",              \
53
                       __FUNCTION__, __FILE__, __LINE__);       \
54
} while(0)
55
#else
56
#define IP_NF_ASSERT(x)
57
#endif
58
#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
59
 
60
/* Mutex protects lists (only traversed in user context). */
61
static DECLARE_MUTEX(ip6t_mutex);
62
 
63
/* Must have mutex */
64
#define ASSERT_READ_LOCK(x) IP_NF_ASSERT(down_trylock(&ip6t_mutex) != 0)
65
#define ASSERT_WRITE_LOCK(x) IP_NF_ASSERT(down_trylock(&ip6t_mutex) != 0)
66
#include <linux/netfilter_ipv4/lockhelp.h>
67
#include <linux/netfilter_ipv4/listhelp.h>
68
 
69
#if 0
70
/* All the better to debug you with... */
71
#define static
72
#define inline
73
#endif
74
 
75
/* Locking is simple: we assume at worst case there will be one packet
76
   in user context and one from bottom halves (or soft irq if Alexey's
77
   softnet patch was applied).
78
 
79
   We keep a set of rules for each CPU, so we can avoid write-locking
80
   them; doing a readlock_bh() stops packets coming through if we're
81
   in user context.
82
 
83
   To be cache friendly on SMP, we arrange them like so:
84
   [ n-entries ]
85
   ... cache-align padding ...
86
   [ n-entries ]
87
 
88
   Hence the start of any table is given by get_table() below.  */
89
 
90
/* The table itself */
91
struct ip6t_table_info
92
{
93
        /* Size per table */
94
        unsigned int size;
95
        /* Number of entries: FIXME. --RR */
96
        unsigned int number;
97
        /* Initial number of entries. Needed for module usage count */
98
        unsigned int initial_entries;
99
 
100
        /* Entry points and underflows */
101
        unsigned int hook_entry[NF_IP6_NUMHOOKS];
102
        unsigned int underflow[NF_IP6_NUMHOOKS];
103
 
104
        /* ip6t_entry tables: one per CPU */
105
        char entries[0] ____cacheline_aligned;
106
};
107
 
108
static LIST_HEAD(ip6t_target);
109
static LIST_HEAD(ip6t_match);
110
static LIST_HEAD(ip6t_tables);
111
#define ADD_COUNTER(c,b,p) do { (c).bcnt += (b); (c).pcnt += (p); } while(0)
112
 
113
#ifdef CONFIG_SMP
114
#define TABLE_OFFSET(t,p) (SMP_ALIGN((t)->size)*(p))
115
#else
116
#define TABLE_OFFSET(t,p) 0
117
#endif
118
 
119
#if 0
120
#define down(x) do { printk("DOWN:%u:" #x "\n", __LINE__); down(x); } while(0)
121
#define down_interruptible(x) ({ int __r; printk("DOWNi:%u:" #x "\n", __LINE__); __r = down_interruptible(x); if (__r != 0) printk("ABORT-DOWNi:%u\n", __LINE__); __r; })
122
#define up(x) do { printk("UP:%u:" #x "\n", __LINE__); up(x); } while(0)
123
#endif
124
 
125
static int ip6_masked_addrcmp(struct in6_addr addr1, struct in6_addr mask,
126
                              struct in6_addr addr2)
127
{
128
        int i;
129
        for( i = 0; i < 16; i++){
130
                if((addr1.s6_addr[i] & mask.s6_addr[i]) !=
131
                   (addr2.s6_addr[i] & mask.s6_addr[i]))
132
                        return 1;
133
        }
134
        return 0;
135
}
136
 
137
/* Check for an extension */
138
int
139
ip6t_ext_hdr(u8 nexthdr)
140
{
141
        return ( (nexthdr == IPPROTO_HOPOPTS)   ||
142
                 (nexthdr == IPPROTO_ROUTING)   ||
143
                 (nexthdr == IPPROTO_FRAGMENT)  ||
144
                 (nexthdr == IPPROTO_ESP)       ||
145
                 (nexthdr == IPPROTO_AH)        ||
146
                 (nexthdr == IPPROTO_NONE)      ||
147
                 (nexthdr == IPPROTO_DSTOPTS) );
148
}
149
 
150
/* Returns whether matches rule or not. */
151
static inline int
152
ip6_packet_match(const struct sk_buff *skb,
153
                 const struct ipv6hdr *ipv6,
154
                 const char *indev,
155
                 const char *outdev,
156
                 const struct ip6t_ip6 *ip6info,
157
                 int isfrag)
158
{
159
        size_t i;
160
        unsigned long ret;
161
 
162
#define FWINV(bool,invflg) ((bool) ^ !!(ip6info->invflags & invflg))
163
 
164
        if (FWINV(ip6_masked_addrcmp(ipv6->saddr,ip6info->smsk,ip6info->src),
165
                  IP6T_INV_SRCIP)
166
            || FWINV(ip6_masked_addrcmp(ipv6->daddr,ip6info->dmsk,ip6info->dst),
167
                     IP6T_INV_DSTIP)) {
168
                dprintf("Source or dest mismatch.\n");
169
/*
170
                dprintf("SRC: %u. Mask: %u. Target: %u.%s\n", ip->saddr,
171
                        ipinfo->smsk.s_addr, ipinfo->src.s_addr,
172
                        ipinfo->invflags & IP6T_INV_SRCIP ? " (INV)" : "");
173
                dprintf("DST: %u. Mask: %u. Target: %u.%s\n", ip->daddr,
174
                        ipinfo->dmsk.s_addr, ipinfo->dst.s_addr,
175
                        ipinfo->invflags & IP6T_INV_DSTIP ? " (INV)" : "");*/
176
                return 0;
177
        }
178
 
179
        /* Look for ifname matches; this should unroll nicely. */
180
        for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
181
                ret |= (((const unsigned long *)indev)[i]
182
                        ^ ((const unsigned long *)ip6info->iniface)[i])
183
                        & ((const unsigned long *)ip6info->iniface_mask)[i];
184
        }
185
 
186
        if (FWINV(ret != 0, IP6T_INV_VIA_IN)) {
187
                dprintf("VIA in mismatch (%s vs %s).%s\n",
188
                        indev, ip6info->iniface,
189
                        ip6info->invflags&IP6T_INV_VIA_IN ?" (INV)":"");
190
                return 0;
191
        }
192
 
193
        for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
194
                ret |= (((const unsigned long *)outdev)[i]
195
                        ^ ((const unsigned long *)ip6info->outiface)[i])
196
                        & ((const unsigned long *)ip6info->outiface_mask)[i];
197
        }
198
 
199
        if (FWINV(ret != 0, IP6T_INV_VIA_OUT)) {
200
                dprintf("VIA out mismatch (%s vs %s).%s\n",
201
                        outdev, ip6info->outiface,
202
                        ip6info->invflags&IP6T_INV_VIA_OUT ?" (INV)":"");
203
                return 0;
204
        }
205
 
206
/* ... might want to do something with class and flowlabel here ... */
207
 
208
        /* look for the desired protocol header */
209
        if((ip6info->flags & IP6T_F_PROTO)) {
210
                u_int8_t currenthdr = ipv6->nexthdr;
211
                struct ipv6_opt_hdr *hdrptr;
212
                u_int16_t ptr;          /* Header offset in skb */
213
                u_int16_t hdrlen;       /* Header */
214
 
215
                ptr = IPV6_HDR_LEN;
216
 
217
                while (ip6t_ext_hdr(currenthdr)) {
218
                        /* Is there enough space for the next ext header? */
219
                        if (skb->len - ptr < IPV6_OPTHDR_LEN)
220
                                return 0;
221
 
222
                        /* NONE or ESP: there isn't protocol part */
223
                        /* If we want to count these packets in '-p all',
224
                         * we will change the return 0 to 1*/
225
                        if ((currenthdr == IPPROTO_NONE) ||
226
                                (currenthdr == IPPROTO_ESP))
227
                                return 0;
228
 
229
                        hdrptr = (struct ipv6_opt_hdr *)(skb->data + ptr);
230
 
231
                        /* Size calculation */
232
                        if (currenthdr == IPPROTO_FRAGMENT) {
233
                                hdrlen = 8;
234
                        } else if (currenthdr == IPPROTO_AH)
235
                                hdrlen = (hdrptr->hdrlen+2)<<2;
236
                        else
237
                                hdrlen = ipv6_optlen(hdrptr);
238
 
239
                        currenthdr = hdrptr->nexthdr;
240
                        ptr += hdrlen;
241
                        /* ptr is too large */
242
                        if ( ptr > skb->len )
243
                                return 0;
244
                }
245
 
246
                /* currenthdr contains the protocol header */
247
 
248
                dprintf("Packet protocol %hi ?= %s%hi.\n",
249
                                currenthdr,
250
                                ip6info->invflags & IP6T_INV_PROTO ? "!":"",
251
                                ip6info->proto);
252
 
253
                if (ip6info->proto == currenthdr) {
254
                        if(ip6info->invflags & IP6T_INV_PROTO) {
255
                                return 0;
256
                        }
257
                        return 1;
258
                }
259
 
260
                /* We need match for the '-p all', too! */
261
                if ((ip6info->proto != 0) &&
262
                        !(ip6info->invflags & IP6T_INV_PROTO))
263
                        return 0;
264
        }
265
        return 1;
266
}
267
 
268
/* should be ip6 safe */
269
static inline int
270
ip6_checkentry(const struct ip6t_ip6 *ipv6)
271
{
272
        if (ipv6->flags & ~IP6T_F_MASK) {
273
                duprintf("Unknown flag bits set: %08X\n",
274
                         ipv6->flags & ~IP6T_F_MASK);
275
                return 0;
276
        }
277
        if (ipv6->invflags & ~IP6T_INV_MASK) {
278
                duprintf("Unknown invflag bits set: %08X\n",
279
                         ipv6->invflags & ~IP6T_INV_MASK);
280
                return 0;
281
        }
282
        return 1;
283
}
284
 
285
static unsigned int
286
ip6t_error(struct sk_buff **pskb,
287
          unsigned int hooknum,
288
          const struct net_device *in,
289
          const struct net_device *out,
290
          const void *targinfo,
291
          void *userinfo)
292
{
293
        if (net_ratelimit())
294
                printk("ip6_tables: error: `%s'\n", (char *)targinfo);
295
 
296
        return NF_DROP;
297
}
298
 
299
static inline
300
int do_match(struct ip6t_entry_match *m,
301
             const struct sk_buff *skb,
302
             const struct net_device *in,
303
             const struct net_device *out,
304
             int offset,
305
             const void *hdr,
306
             u_int16_t datalen,
307
             int *hotdrop)
308
{
309
        /* Stop iteration if it doesn't match */
310
        if (!m->u.kernel.match->match(skb, in, out, m->data,
311
                                      offset, hdr, datalen, hotdrop))
312
                return 1;
313
        else
314
                return 0;
315
}
316
 
317
static inline struct ip6t_entry *
318
get_entry(void *base, unsigned int offset)
319
{
320
        return (struct ip6t_entry *)(base + offset);
321
}
322
 
323
/* Returns one of the generic firewall policies, like NF_ACCEPT. */
324
unsigned int
325
ip6t_do_table(struct sk_buff **pskb,
326
              unsigned int hook,
327
              const struct net_device *in,
328
              const struct net_device *out,
329
              struct ip6t_table *table,
330
              void *userdata)
331
{
332
        static const char nulldevname[IFNAMSIZ] = { 0 };
333
        u_int16_t offset = 0;
334
        struct ipv6hdr *ipv6;
335
        void *protohdr;
336
        u_int16_t datalen;
337
        int hotdrop = 0;
338
        /* Initializing verdict to NF_DROP keeps gcc happy. */
339
        unsigned int verdict = NF_DROP;
340
        const char *indev, *outdev;
341
        void *table_base;
342
        struct ip6t_entry *e, *back;
343
 
344
        /* Initialization */
345
        ipv6 = (*pskb)->nh.ipv6h;
346
        protohdr = (u_int32_t *)((char *)ipv6 + IPV6_HDR_LEN);
347
        datalen = (*pskb)->len - IPV6_HDR_LEN;
348
        indev = in ? in->name : nulldevname;
349
        outdev = out ? out->name : nulldevname;
350
 
351
        /* We handle fragments by dealing with the first fragment as
352
         * if it was a normal packet.  All other fragments are treated
353
         * normally, except that they will NEVER match rules that ask
354
         * things we don't know, ie. tcp syn flag or ports).  If the
355
         * rule is also a fragment-specific rule, non-fragments won't
356
         * match it. */
357
 
358
        read_lock_bh(&table->lock);
359
        IP_NF_ASSERT(table->valid_hooks & (1 << hook));
360
        table_base = (void *)table->private->entries
361
                + TABLE_OFFSET(table->private,
362
                                cpu_number_map(smp_processor_id()));
363
        e = get_entry(table_base, table->private->hook_entry[hook]);
364
 
365
#ifdef CONFIG_NETFILTER_DEBUG
366
        /* Check noone else using our table */
367
        if (((struct ip6t_entry *)table_base)->comefrom != 0xdead57ac
368
            && ((struct ip6t_entry *)table_base)->comefrom != 0xeeeeeeec) {
369
                printk("ASSERT: CPU #%u, %s comefrom(%p) = %X\n",
370
                       smp_processor_id(),
371
                       table->name,
372
                       &((struct ip6t_entry *)table_base)->comefrom,
373
                       ((struct ip6t_entry *)table_base)->comefrom);
374
        }
375
        ((struct ip6t_entry *)table_base)->comefrom = 0x57acc001;
376
#endif
377
 
378
        /* For return from builtin chain */
379
        back = get_entry(table_base, table->private->underflow[hook]);
380
 
381
        do {
382
                IP_NF_ASSERT(e);
383
                IP_NF_ASSERT(back);
384
                (*pskb)->nfcache |= e->nfcache;
385
                if (ip6_packet_match(*pskb, ipv6, indev, outdev,
386
                        &e->ipv6, offset)) {
387
                        struct ip6t_entry_target *t;
388
 
389
                        if (IP6T_MATCH_ITERATE(e, do_match,
390
                                               *pskb, in, out,
391
                                               offset, protohdr,
392
                                               datalen, &hotdrop) != 0)
393
                                goto no_match;
394
 
395
                        ADD_COUNTER(e->counters, ntohs(ipv6->payload_len) + IPV6_HDR_LEN, 1);
396
 
397
                        t = ip6t_get_target(e);
398
                        IP_NF_ASSERT(t->u.kernel.target);
399
                        /* Standard target? */
400
                        if (!t->u.kernel.target->target) {
401
                                int v;
402
 
403
                                v = ((struct ip6t_standard_target *)t)->verdict;
404
                                if (v < 0) {
405
                                        /* Pop from stack? */
406
                                        if (v != IP6T_RETURN) {
407
                                                verdict = (unsigned)(-v) - 1;
408
                                                break;
409
                                        }
410
                                        e = back;
411
                                        back = get_entry(table_base,
412
                                                         back->comefrom);
413
                                        continue;
414
                                }
415
                                if (table_base + v
416
                                    != (void *)e + e->next_offset) {
417
                                        /* Save old back ptr in next entry */
418
                                        struct ip6t_entry *next
419
                                                = (void *)e + e->next_offset;
420
                                        next->comefrom
421
                                                = (void *)back - table_base;
422
                                        /* set back pointer to next entry */
423
                                        back = next;
424
                                }
425
 
426
                                e = get_entry(table_base, v);
427
                        } else {
428
                                /* Targets which reenter must return
429
                                   abs. verdicts */
430
#ifdef CONFIG_NETFILTER_DEBUG
431
                                ((struct ip6t_entry *)table_base)->comefrom
432
                                        = 0xeeeeeeec;
433
#endif
434
                                verdict = t->u.kernel.target->target(pskb,
435
                                                                     hook,
436
                                                                     in, out,
437
                                                                     t->data,
438
                                                                     userdata);
439
 
440
#ifdef CONFIG_NETFILTER_DEBUG
441
                                if (((struct ip6t_entry *)table_base)->comefrom
442
                                    != 0xeeeeeeec
443
                                    && verdict == IP6T_CONTINUE) {
444
                                        printk("Target %s reentered!\n",
445
                                               t->u.kernel.target->name);
446
                                        verdict = NF_DROP;
447
                                }
448
                                ((struct ip6t_entry *)table_base)->comefrom
449
                                        = 0x57acc001;
450
#endif
451
                                /* Target might have changed stuff. */
452
                                ipv6 = (*pskb)->nh.ipv6h;
453
                                protohdr = (u_int32_t *)((void *)ipv6 + IPV6_HDR_LEN);
454
                                datalen = (*pskb)->len - IPV6_HDR_LEN;
455
 
456
                                if (verdict == IP6T_CONTINUE)
457
                                        e = (void *)e + e->next_offset;
458
                                else
459
                                        /* Verdict */
460
                                        break;
461
                        }
462
                } else {
463
 
464
                no_match:
465
                        e = (void *)e + e->next_offset;
466
                }
467
        } while (!hotdrop);
468
 
469
#ifdef CONFIG_NETFILTER_DEBUG
470
        ((struct ip6t_entry *)table_base)->comefrom = 0xdead57ac;
471
#endif
472
        read_unlock_bh(&table->lock);
473
 
474
#ifdef DEBUG_ALLOW_ALL
475
        return NF_ACCEPT;
476
#else
477
        if (hotdrop)
478
                return NF_DROP;
479
        else return verdict;
480
#endif
481
}
482
 
483
/* If it succeeds, returns element and locks mutex */
484
static inline void *
485
find_inlist_lock_noload(struct list_head *head,
486
                        const char *name,
487
                        int *error,
488
                        struct semaphore *mutex)
489
{
490
        void *ret;
491
 
492
#if 1
493
        duprintf("find_inlist: searching for `%s' in %s.\n",
494
                 name, head == &ip6t_target ? "ip6t_target"
495
                 : head == &ip6t_match ? "ip6t_match"
496
                 : head == &ip6t_tables ? "ip6t_tables" : "UNKNOWN");
497
#endif
498
 
499
        *error = down_interruptible(mutex);
500
        if (*error != 0)
501
                return NULL;
502
 
503
        ret = list_named_find(head, name);
504
        if (!ret) {
505
                *error = -ENOENT;
506
                up(mutex);
507
        }
508
        return ret;
509
}
510
 
511
#ifndef CONFIG_KMOD
512
#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
513
#else
514
static void *
515
find_inlist_lock(struct list_head *head,
516
                 const char *name,
517
                 const char *prefix,
518
                 int *error,
519
                 struct semaphore *mutex)
520
{
521
        void *ret;
522
 
523
        ret = find_inlist_lock_noload(head, name, error, mutex);
524
        if (!ret) {
525
                char modulename[IP6T_FUNCTION_MAXNAMELEN + strlen(prefix) + 1];
526
                strcpy(modulename, prefix);
527
                strcat(modulename, name);
528
                duprintf("find_inlist: loading `%s'.\n", modulename);
529
                request_module(modulename);
530
                ret = find_inlist_lock_noload(head, name, error, mutex);
531
        }
532
 
533
        return ret;
534
}
535
#endif
536
 
537
static inline struct ip6t_table *
538
find_table_lock(const char *name, int *error, struct semaphore *mutex)
539
{
540
        return find_inlist_lock(&ip6t_tables, name, "ip6table_", error, mutex);
541
}
542
 
543
static inline struct ip6t_match *
544
find_match_lock(const char *name, int *error, struct semaphore *mutex)
545
{
546
        return find_inlist_lock(&ip6t_match, name, "ip6t_", error, mutex);
547
}
548
 
549
static inline struct ip6t_target *
550
find_target_lock(const char *name, int *error, struct semaphore *mutex)
551
{
552
        return find_inlist_lock(&ip6t_target, name, "ip6t_", error, mutex);
553
}
554
 
555
/* All zeroes == unconditional rule. */
556
static inline int
557
unconditional(const struct ip6t_ip6 *ipv6)
558
{
559
        unsigned int i;
560
 
561
        for (i = 0; i < sizeof(*ipv6); i++)
562
                if (((char *)ipv6)[i])
563
                        break;
564
 
565
        return (i == sizeof(*ipv6));
566
}
567
 
568
/* Figures out from what hook each rule can be called: returns 0 if
569
   there are loops.  Puts hook bitmask in comefrom. */
570
static int
571
mark_source_chains(struct ip6t_table_info *newinfo, unsigned int valid_hooks)
572
{
573
        unsigned int hook;
574
 
575
        /* No recursion; use packet counter to save back ptrs (reset
576
           to 0 as we leave), and comefrom to save source hook bitmask */
577
        for (hook = 0; hook < NF_IP6_NUMHOOKS; hook++) {
578
                unsigned int pos = newinfo->hook_entry[hook];
579
                struct ip6t_entry *e
580
                        = (struct ip6t_entry *)(newinfo->entries + pos);
581
 
582
                if (!(valid_hooks & (1 << hook)))
583
                        continue;
584
 
585
                /* Set initial back pointer. */
586
                e->counters.pcnt = pos;
587
 
588
                for (;;) {
589
                        struct ip6t_standard_target *t
590
                                = (void *)ip6t_get_target(e);
591
 
592
                        if (e->comefrom & (1 << NF_IP6_NUMHOOKS)) {
593
                                printk("iptables: loop hook %u pos %u %08X.\n",
594
                                       hook, pos, e->comefrom);
595
                                return 0;
596
                        }
597
                        e->comefrom
598
                                |= ((1 << hook) | (1 << NF_IP6_NUMHOOKS));
599
 
600
                        /* Unconditional return/END. */
601
                        if (e->target_offset == sizeof(struct ip6t_entry)
602
                            && (strcmp(t->target.u.user.name,
603
                                       IP6T_STANDARD_TARGET) == 0)
604
                            && t->verdict < 0
605
                            && unconditional(&e->ipv6)) {
606
                                unsigned int oldpos, size;
607
 
608
                                /* Return: backtrack through the last
609
                                   big jump. */
610
                                do {
611
                                        e->comefrom ^= (1<<NF_IP6_NUMHOOKS);
612
#ifdef DEBUG_IP_FIREWALL_USER
613
                                        if (e->comefrom
614
                                            & (1 << NF_IP6_NUMHOOKS)) {
615
                                                duprintf("Back unset "
616
                                                         "on hook %u "
617
                                                         "rule %u\n",
618
                                                         hook, pos);
619
                                        }
620
#endif
621
                                        oldpos = pos;
622
                                        pos = e->counters.pcnt;
623
                                        e->counters.pcnt = 0;
624
 
625
                                        /* We're at the start. */
626
                                        if (pos == oldpos)
627
                                                goto next;
628
 
629
                                        e = (struct ip6t_entry *)
630
                                                (newinfo->entries + pos);
631
                                } while (oldpos == pos + e->next_offset);
632
 
633
                                /* Move along one */
634
                                size = e->next_offset;
635
                                e = (struct ip6t_entry *)
636
                                        (newinfo->entries + pos + size);
637
                                e->counters.pcnt = pos;
638
                                pos += size;
639
                        } else {
640
                                int newpos = t->verdict;
641
 
642
                                if (strcmp(t->target.u.user.name,
643
                                           IP6T_STANDARD_TARGET) == 0
644
                                    && newpos >= 0) {
645
                                        /* This a jump; chase it. */
646
                                        duprintf("Jump rule %u -> %u\n",
647
                                                 pos, newpos);
648
                                } else {
649
                                        /* ... this is a fallthru */
650
                                        newpos = pos + e->next_offset;
651
                                }
652
                                e = (struct ip6t_entry *)
653
                                        (newinfo->entries + newpos);
654
                                e->counters.pcnt = pos;
655
                                pos = newpos;
656
                        }
657
                }
658
                next:
659
                duprintf("Finished chain %u\n", hook);
660
        }
661
        return 1;
662
}
663
 
664
static inline int
665
cleanup_match(struct ip6t_entry_match *m, unsigned int *i)
666
{
667
        if (i && (*i)-- == 0)
668
                return 1;
669
 
670
        if (m->u.kernel.match->destroy)
671
                m->u.kernel.match->destroy(m->data,
672
                                           m->u.match_size - sizeof(*m));
673
 
674
        if (m->u.kernel.match->me)
675
                __MOD_DEC_USE_COUNT(m->u.kernel.match->me);
676
 
677
        return 0;
678
}
679
 
680
static inline int
681
standard_check(const struct ip6t_entry_target *t,
682
               unsigned int max_offset)
683
{
684
        struct ip6t_standard_target *targ = (void *)t;
685
 
686
        /* Check standard info. */
687
        if (t->u.target_size
688
            != IP6T_ALIGN(sizeof(struct ip6t_standard_target))) {
689
                duprintf("standard_check: target size %u != %u\n",
690
                         t->u.target_size,
691
                         IP6T_ALIGN(sizeof(struct ip6t_standard_target)));
692
                return 0;
693
        }
694
 
695
        if (targ->verdict >= 0
696
            && targ->verdict > max_offset - sizeof(struct ip6t_entry)) {
697
                duprintf("ip6t_standard_check: bad verdict (%i)\n",
698
                         targ->verdict);
699
                return 0;
700
        }
701
 
702
        if (targ->verdict < -NF_MAX_VERDICT - 1) {
703
                duprintf("ip6t_standard_check: bad negative verdict (%i)\n",
704
                         targ->verdict);
705
                return 0;
706
        }
707
        return 1;
708
}
709
 
710
static inline int
711
check_match(struct ip6t_entry_match *m,
712
            const char *name,
713
            const struct ip6t_ip6 *ipv6,
714
            unsigned int hookmask,
715
            unsigned int *i)
716
{
717
        int ret;
718
        struct ip6t_match *match;
719
 
720
        match = find_match_lock(m->u.user.name, &ret, &ip6t_mutex);
721
        if (!match) {
722
          //            duprintf("check_match: `%s' not found\n", m->u.name);
723
                return ret;
724
        }
725
        if (match->me)
726
                __MOD_INC_USE_COUNT(match->me);
727
        m->u.kernel.match = match;
728
        up(&ip6t_mutex);
729
 
730
        if (m->u.kernel.match->checkentry
731
            && !m->u.kernel.match->checkentry(name, ipv6, m->data,
732
                                              m->u.match_size - sizeof(*m),
733
                                              hookmask)) {
734
                if (m->u.kernel.match->me)
735
                        __MOD_DEC_USE_COUNT(m->u.kernel.match->me);
736
                duprintf("ip_tables: check failed for `%s'.\n",
737
                         m->u.kernel.match->name);
738
                return -EINVAL;
739
        }
740
 
741
        (*i)++;
742
        return 0;
743
}
744
 
745
static struct ip6t_target ip6t_standard_target;
746
 
747
static inline int
748
check_entry(struct ip6t_entry *e, const char *name, unsigned int size,
749
            unsigned int *i)
750
{
751
        struct ip6t_entry_target *t;
752
        struct ip6t_target *target;
753
        int ret;
754
        unsigned int j;
755
 
756
        if (!ip6_checkentry(&e->ipv6)) {
757
                duprintf("ip_tables: ip check failed %p %s.\n", e, name);
758
                return -EINVAL;
759
        }
760
 
761
        j = 0;
762
        ret = IP6T_MATCH_ITERATE(e, check_match, name, &e->ipv6, e->comefrom, &j);
763
        if (ret != 0)
764
                goto cleanup_matches;
765
 
766
        t = ip6t_get_target(e);
767
        target = find_target_lock(t->u.user.name, &ret, &ip6t_mutex);
768
        if (!target) {
769
                duprintf("check_entry: `%s' not found\n", t->u.user.name);
770
                goto cleanup_matches;
771
        }
772
        if (target->me)
773
                __MOD_INC_USE_COUNT(target->me);
774
        t->u.kernel.target = target;
775
        up(&ip6t_mutex);
776
 
777
        if (t->u.kernel.target == &ip6t_standard_target) {
778
                if (!standard_check(t, size)) {
779
                        ret = -EINVAL;
780
                        goto cleanup_matches;
781
                }
782
        } else if (t->u.kernel.target->checkentry
783
                   && !t->u.kernel.target->checkentry(name, e, t->data,
784
                                                      t->u.target_size
785
                                                      - sizeof(*t),
786
                                                      e->comefrom)) {
787
                if (t->u.kernel.target->me)
788
                        __MOD_DEC_USE_COUNT(t->u.kernel.target->me);
789
                duprintf("ip_tables: check failed for `%s'.\n",
790
                         t->u.kernel.target->name);
791
                ret = -EINVAL;
792
                goto cleanup_matches;
793
        }
794
 
795
        (*i)++;
796
        return 0;
797
 
798
 cleanup_matches:
799
        IP6T_MATCH_ITERATE(e, cleanup_match, &j);
800
        return ret;
801
}
802
 
803
static inline int
804
check_entry_size_and_hooks(struct ip6t_entry *e,
805
                           struct ip6t_table_info *newinfo,
806
                           unsigned char *base,
807
                           unsigned char *limit,
808
                           const unsigned int *hook_entries,
809
                           const unsigned int *underflows,
810
                           unsigned int *i)
811
{
812
        unsigned int h;
813
 
814
        if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0
815
            || (unsigned char *)e + sizeof(struct ip6t_entry) >= limit) {
816
                duprintf("Bad offset %p\n", e);
817
                return -EINVAL;
818
        }
819
 
820
        if (e->next_offset
821
            < sizeof(struct ip6t_entry) + sizeof(struct ip6t_entry_target)) {
822
                duprintf("checking: element %p size %u\n",
823
                         e, e->next_offset);
824
                return -EINVAL;
825
        }
826
 
827
        /* Check hooks & underflows */
828
        for (h = 0; h < NF_IP6_NUMHOOKS; h++) {
829
                if ((unsigned char *)e - base == hook_entries[h])
830
                        newinfo->hook_entry[h] = hook_entries[h];
831
                if ((unsigned char *)e - base == underflows[h])
832
                        newinfo->underflow[h] = underflows[h];
833
        }
834
 
835
        /* FIXME: underflows must be unconditional, standard verdicts
836
           < 0 (not IP6T_RETURN). --RR */
837
 
838
        /* Clear counters and comefrom */
839
        e->counters = ((struct ip6t_counters) { 0, 0 });
840
        e->comefrom = 0;
841
 
842
        (*i)++;
843
        return 0;
844
}
845
 
846
static inline int
847
cleanup_entry(struct ip6t_entry *e, unsigned int *i)
848
{
849
        struct ip6t_entry_target *t;
850
 
851
        if (i && (*i)-- == 0)
852
                return 1;
853
 
854
        /* Cleanup all matches */
855
        IP6T_MATCH_ITERATE(e, cleanup_match, NULL);
856
        t = ip6t_get_target(e);
857
        if (t->u.kernel.target->destroy)
858
                t->u.kernel.target->destroy(t->data,
859
                                            t->u.target_size - sizeof(*t));
860
        if (t->u.kernel.target->me)
861
                __MOD_DEC_USE_COUNT(t->u.kernel.target->me);
862
 
863
        return 0;
864
}
865
 
866
/* Checks and translates the user-supplied table segment (held in
867
   newinfo) */
868
static int
869
translate_table(const char *name,
870
                unsigned int valid_hooks,
871
                struct ip6t_table_info *newinfo,
872
                unsigned int size,
873
                unsigned int number,
874
                const unsigned int *hook_entries,
875
                const unsigned int *underflows)
876
{
877
        unsigned int i;
878
        int ret;
879
 
880
        newinfo->size = size;
881
        newinfo->number = number;
882
 
883
        /* Init all hooks to impossible value. */
884
        for (i = 0; i < NF_IP6_NUMHOOKS; i++) {
885
                newinfo->hook_entry[i] = 0xFFFFFFFF;
886
                newinfo->underflow[i] = 0xFFFFFFFF;
887
        }
888
 
889
        duprintf("translate_table: size %u\n", newinfo->size);
890
        i = 0;
891
        /* Walk through entries, checking offsets. */
892
        ret = IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size,
893
                                check_entry_size_and_hooks,
894
                                newinfo,
895
                                newinfo->entries,
896
                                newinfo->entries + size,
897
                                hook_entries, underflows, &i);
898
        if (ret != 0)
899
                return ret;
900
 
901
        if (i != number) {
902
                duprintf("translate_table: %u not %u entries\n",
903
                         i, number);
904
                return -EINVAL;
905
        }
906
 
907
        /* Check hooks all assigned */
908
        for (i = 0; i < NF_IP6_NUMHOOKS; i++) {
909
                /* Only hooks which are valid */
910
                if (!(valid_hooks & (1 << i)))
911
                        continue;
912
                if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
913
                        duprintf("Invalid hook entry %u %u\n",
914
                                 i, hook_entries[i]);
915
                        return -EINVAL;
916
                }
917
                if (newinfo->underflow[i] == 0xFFFFFFFF) {
918
                        duprintf("Invalid underflow %u %u\n",
919
                                 i, underflows[i]);
920
                        return -EINVAL;
921
                }
922
        }
923
 
924
        if (!mark_source_chains(newinfo, valid_hooks))
925
                return -ELOOP;
926
 
927
        /* Finally, each sanity check must pass */
928
        i = 0;
929
        ret = IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size,
930
                                check_entry, name, size, &i);
931
 
932
        if (ret != 0) {
933
                IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size,
934
                                  cleanup_entry, &i);
935
                return ret;
936
        }
937
 
938
        /* And one copy for every other CPU */
939
        for (i = 1; i < smp_num_cpus; i++) {
940
                memcpy(newinfo->entries + SMP_ALIGN(newinfo->size)*i,
941
                       newinfo->entries,
942
                       SMP_ALIGN(newinfo->size));
943
        }
944
 
945
        return ret;
946
}
947
 
948
static struct ip6t_table_info *
949
replace_table(struct ip6t_table *table,
950
              unsigned int num_counters,
951
              struct ip6t_table_info *newinfo,
952
              int *error)
953
{
954
        struct ip6t_table_info *oldinfo;
955
 
956
#ifdef CONFIG_NETFILTER_DEBUG
957
        {
958
                struct ip6t_entry *table_base;
959
                unsigned int i;
960
 
961
                for (i = 0; i < smp_num_cpus; i++) {
962
                        table_base =
963
                                (void *)newinfo->entries
964
                                + TABLE_OFFSET(newinfo, i);
965
 
966
                        table_base->comefrom = 0xdead57ac;
967
                }
968
        }
969
#endif
970
 
971
        /* Do the substitution. */
972
        write_lock_bh(&table->lock);
973
        /* Check inside lock: is the old number correct? */
974
        if (num_counters != table->private->number) {
975
                duprintf("num_counters != table->private->number (%u/%u)\n",
976
                         num_counters, table->private->number);
977
                write_unlock_bh(&table->lock);
978
                *error = -EAGAIN;
979
                return NULL;
980
        }
981
        oldinfo = table->private;
982
        table->private = newinfo;
983
        newinfo->initial_entries = oldinfo->initial_entries;
984
        write_unlock_bh(&table->lock);
985
 
986
        return oldinfo;
987
}
988
 
989
/* Gets counters. */
990
static inline int
991
add_entry_to_counter(const struct ip6t_entry *e,
992
                     struct ip6t_counters total[],
993
                     unsigned int *i)
994
{
995
        ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
996
 
997
        (*i)++;
998
        return 0;
999
}
1000
 
1001
static void
1002
get_counters(const struct ip6t_table_info *t,
1003
             struct ip6t_counters counters[])
1004
{
1005
        unsigned int cpu;
1006
        unsigned int i;
1007
 
1008
        for (cpu = 0; cpu < smp_num_cpus; cpu++) {
1009
                i = 0;
1010
                IP6T_ENTRY_ITERATE(t->entries + TABLE_OFFSET(t, cpu),
1011
                                  t->size,
1012
                                  add_entry_to_counter,
1013
                                  counters,
1014
                                  &i);
1015
        }
1016
}
1017
 
1018
static int
1019
copy_entries_to_user(unsigned int total_size,
1020
                     struct ip6t_table *table,
1021
                     void *userptr)
1022
{
1023
        unsigned int off, num, countersize;
1024
        struct ip6t_entry *e;
1025
        struct ip6t_counters *counters;
1026
        int ret = 0;
1027
 
1028
        /* We need atomic snapshot of counters: rest doesn't change
1029
           (other than comefrom, which userspace doesn't care
1030
           about). */
1031
        countersize = sizeof(struct ip6t_counters) * table->private->number;
1032
        counters = vmalloc(countersize);
1033
 
1034
        if (counters == NULL)
1035
                return -ENOMEM;
1036
 
1037
        /* First, sum counters... */
1038
        memset(counters, 0, countersize);
1039
        write_lock_bh(&table->lock);
1040
        get_counters(table->private, counters);
1041
        write_unlock_bh(&table->lock);
1042
 
1043
        /* ... then copy entire thing from CPU 0... */
1044
        if (copy_to_user(userptr, table->private->entries, total_size) != 0) {
1045
                ret = -EFAULT;
1046
                goto free_counters;
1047
        }
1048
 
1049
        /* FIXME: use iterator macros --RR */
1050
        /* ... then go back and fix counters and names */
1051
        for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
1052
                unsigned int i;
1053
                struct ip6t_entry_match *m;
1054
                struct ip6t_entry_target *t;
1055
 
1056
                e = (struct ip6t_entry *)(table->private->entries + off);
1057
                if (copy_to_user(userptr + off
1058
                                 + offsetof(struct ip6t_entry, counters),
1059
                                 &counters[num],
1060
                                 sizeof(counters[num])) != 0) {
1061
                        ret = -EFAULT;
1062
                        goto free_counters;
1063
                }
1064
 
1065
                for (i = sizeof(struct ip6t_entry);
1066
                     i < e->target_offset;
1067
                     i += m->u.match_size) {
1068
                        m = (void *)e + i;
1069
 
1070
                        if (copy_to_user(userptr + off + i
1071
                                         + offsetof(struct ip6t_entry_match,
1072
                                                    u.user.name),
1073
                                         m->u.kernel.match->name,
1074
                                         strlen(m->u.kernel.match->name)+1)
1075
                            != 0) {
1076
                                ret = -EFAULT;
1077
                                goto free_counters;
1078
                        }
1079
                }
1080
 
1081
                t = ip6t_get_target(e);
1082
                if (copy_to_user(userptr + off + e->target_offset
1083
                                 + offsetof(struct ip6t_entry_target,
1084
                                            u.user.name),
1085
                                 t->u.kernel.target->name,
1086
                                 strlen(t->u.kernel.target->name)+1) != 0) {
1087
                        ret = -EFAULT;
1088
                        goto free_counters;
1089
                }
1090
        }
1091
 
1092
 free_counters:
1093
        vfree(counters);
1094
        return ret;
1095
}
1096
 
1097
static int
1098
get_entries(const struct ip6t_get_entries *entries,
1099
            struct ip6t_get_entries *uptr)
1100
{
1101
        int ret;
1102
        struct ip6t_table *t;
1103
 
1104
        t = find_table_lock(entries->name, &ret, &ip6t_mutex);
1105
        if (t) {
1106
                duprintf("t->private->number = %u\n",
1107
                         t->private->number);
1108
                if (entries->size == t->private->size)
1109
                        ret = copy_entries_to_user(t->private->size,
1110
                                                   t, uptr->entrytable);
1111
                else {
1112
                        duprintf("get_entries: I've got %u not %u!\n",
1113
                                 t->private->size,
1114
                                 entries->size);
1115
                        ret = -EINVAL;
1116
                }
1117
                up(&ip6t_mutex);
1118
        } else
1119
                duprintf("get_entries: Can't find %s!\n",
1120
                         entries->name);
1121
 
1122
        return ret;
1123
}
1124
 
1125
static int
1126
do_replace(void *user, unsigned int len)
1127
{
1128
        int ret;
1129
        struct ip6t_replace tmp;
1130
        struct ip6t_table *t;
1131
        struct ip6t_table_info *newinfo, *oldinfo;
1132
        struct ip6t_counters *counters;
1133
 
1134
        if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1135
                return -EFAULT;
1136
 
1137
        /* Pedantry: prevent them from hitting BUG() in vmalloc.c --RR */
1138
        if ((SMP_ALIGN(tmp.size) >> PAGE_SHIFT) + 2 > num_physpages)
1139
                return -ENOMEM;
1140
 
1141
        newinfo = vmalloc(sizeof(struct ip6t_table_info)
1142
                          + SMP_ALIGN(tmp.size) * smp_num_cpus);
1143
        if (!newinfo)
1144
                return -ENOMEM;
1145
 
1146
        if (copy_from_user(newinfo->entries, user + sizeof(tmp),
1147
                           tmp.size) != 0) {
1148
                ret = -EFAULT;
1149
                goto free_newinfo;
1150
        }
1151
 
1152
        counters = vmalloc(tmp.num_counters * sizeof(struct ip6t_counters));
1153
        if (!counters) {
1154
                ret = -ENOMEM;
1155
                goto free_newinfo;
1156
        }
1157
        memset(counters, 0, tmp.num_counters * sizeof(struct ip6t_counters));
1158
 
1159
        ret = translate_table(tmp.name, tmp.valid_hooks,
1160
                              newinfo, tmp.size, tmp.num_entries,
1161
                              tmp.hook_entry, tmp.underflow);
1162
        if (ret != 0)
1163
                goto free_newinfo_counters;
1164
 
1165
        duprintf("ip_tables: Translated table\n");
1166
 
1167
        t = find_table_lock(tmp.name, &ret, &ip6t_mutex);
1168
        if (!t)
1169
                goto free_newinfo_counters_untrans;
1170
 
1171
        /* You lied! */
1172
        if (tmp.valid_hooks != t->valid_hooks) {
1173
                duprintf("Valid hook crap: %08X vs %08X\n",
1174
                         tmp.valid_hooks, t->valid_hooks);
1175
                ret = -EINVAL;
1176
                goto free_newinfo_counters_untrans_unlock;
1177
        }
1178
 
1179
        oldinfo = replace_table(t, tmp.num_counters, newinfo, &ret);
1180
        if (!oldinfo)
1181
                goto free_newinfo_counters_untrans_unlock;
1182
 
1183
        /* Update module usage count based on number of rules */
1184
        duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
1185
                oldinfo->number, oldinfo->initial_entries, newinfo->number);
1186
        if (t->me && (oldinfo->number <= oldinfo->initial_entries) &&
1187
            (newinfo->number > oldinfo->initial_entries))
1188
                __MOD_INC_USE_COUNT(t->me);
1189
        else if (t->me && (oldinfo->number > oldinfo->initial_entries) &&
1190
                 (newinfo->number <= oldinfo->initial_entries))
1191
                __MOD_DEC_USE_COUNT(t->me);
1192
 
1193
        /* Get the old counters. */
1194
        get_counters(oldinfo, counters);
1195
        /* Decrease module usage counts and free resource */
1196
        IP6T_ENTRY_ITERATE(oldinfo->entries, oldinfo->size, cleanup_entry,NULL);
1197
        vfree(oldinfo);
1198
        /* Silent error: too late now. */
1199
        copy_to_user(tmp.counters, counters,
1200
                     sizeof(struct ip6t_counters) * tmp.num_counters);
1201
        vfree(counters);
1202
        up(&ip6t_mutex);
1203
        return 0;
1204
 
1205
 free_newinfo_counters_untrans_unlock:
1206
        up(&ip6t_mutex);
1207
 free_newinfo_counters_untrans:
1208
        IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size, cleanup_entry,NULL);
1209
 free_newinfo_counters:
1210
        vfree(counters);
1211
 free_newinfo:
1212
        vfree(newinfo);
1213
        return ret;
1214
}
1215
 
1216
/* We're lazy, and add to the first CPU; overflow works its fey magic
1217
 * and everything is OK. */
1218
static inline int
1219
add_counter_to_entry(struct ip6t_entry *e,
1220
                     const struct ip6t_counters addme[],
1221
                     unsigned int *i)
1222
{
1223
#if 0
1224
        duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
1225
                 *i,
1226
                 (long unsigned int)e->counters.pcnt,
1227
                 (long unsigned int)e->counters.bcnt,
1228
                 (long unsigned int)addme[*i].pcnt,
1229
                 (long unsigned int)addme[*i].bcnt);
1230
#endif
1231
 
1232
        ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
1233
 
1234
        (*i)++;
1235
        return 0;
1236
}
1237
 
1238
static int
1239
do_add_counters(void *user, unsigned int len)
1240
{
1241
        unsigned int i;
1242
        struct ip6t_counters_info tmp, *paddc;
1243
        struct ip6t_table *t;
1244
        int ret;
1245
 
1246
        if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1247
                return -EFAULT;
1248
 
1249
        if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct ip6t_counters))
1250
                return -EINVAL;
1251
 
1252
        paddc = vmalloc(len);
1253
        if (!paddc)
1254
                return -ENOMEM;
1255
 
1256
        if (copy_from_user(paddc, user, len) != 0) {
1257
                ret = -EFAULT;
1258
                goto free;
1259
        }
1260
 
1261
        t = find_table_lock(tmp.name, &ret, &ip6t_mutex);
1262
        if (!t)
1263
                goto free;
1264
 
1265
        write_lock_bh(&t->lock);
1266
        if (t->private->number != paddc->num_counters) {
1267
                ret = -EINVAL;
1268
                goto unlock_up_free;
1269
        }
1270
 
1271
        i = 0;
1272
        IP6T_ENTRY_ITERATE(t->private->entries,
1273
                          t->private->size,
1274
                          add_counter_to_entry,
1275
                          paddc->counters,
1276
                          &i);
1277
 unlock_up_free:
1278
        write_unlock_bh(&t->lock);
1279
        up(&ip6t_mutex);
1280
 free:
1281
        vfree(paddc);
1282
 
1283
        return ret;
1284
}
1285
 
1286
static int
1287
do_ip6t_set_ctl(struct sock *sk,        int cmd, void *user, unsigned int len)
1288
{
1289
        int ret;
1290
 
1291
        if (!capable(CAP_NET_ADMIN))
1292
                return -EPERM;
1293
 
1294
        switch (cmd) {
1295
        case IP6T_SO_SET_REPLACE:
1296
                ret = do_replace(user, len);
1297
                break;
1298
 
1299
        case IP6T_SO_SET_ADD_COUNTERS:
1300
                ret = do_add_counters(user, len);
1301
                break;
1302
 
1303
        default:
1304
                duprintf("do_ip6t_set_ctl:  unknown request %i\n", cmd);
1305
                ret = -EINVAL;
1306
        }
1307
 
1308
        return ret;
1309
}
1310
 
1311
static int
1312
do_ip6t_get_ctl(struct sock *sk, int cmd, void *user, int *len)
1313
{
1314
        int ret;
1315
 
1316
        if (!capable(CAP_NET_ADMIN))
1317
                return -EPERM;
1318
 
1319
        switch (cmd) {
1320
        case IP6T_SO_GET_INFO: {
1321
                char name[IP6T_TABLE_MAXNAMELEN];
1322
                struct ip6t_table *t;
1323
 
1324
                if (*len != sizeof(struct ip6t_getinfo)) {
1325
                        duprintf("length %u != %u\n", *len,
1326
                                 sizeof(struct ip6t_getinfo));
1327
                        ret = -EINVAL;
1328
                        break;
1329
                }
1330
 
1331
                if (copy_from_user(name, user, sizeof(name)) != 0) {
1332
                        ret = -EFAULT;
1333
                        break;
1334
                }
1335
                name[IP6T_TABLE_MAXNAMELEN-1] = '\0';
1336
                t = find_table_lock(name, &ret, &ip6t_mutex);
1337
                if (t) {
1338
                        struct ip6t_getinfo info;
1339
 
1340
                        info.valid_hooks = t->valid_hooks;
1341
                        memcpy(info.hook_entry, t->private->hook_entry,
1342
                               sizeof(info.hook_entry));
1343
                        memcpy(info.underflow, t->private->underflow,
1344
                               sizeof(info.underflow));
1345
                        info.num_entries = t->private->number;
1346
                        info.size = t->private->size;
1347
                        strcpy(info.name, name);
1348
 
1349
                        if (copy_to_user(user, &info, *len) != 0)
1350
                                ret = -EFAULT;
1351
                        else
1352
                                ret = 0;
1353
 
1354
                        up(&ip6t_mutex);
1355
                }
1356
        }
1357
        break;
1358
 
1359
        case IP6T_SO_GET_ENTRIES: {
1360
                struct ip6t_get_entries get;
1361
 
1362
                if (*len < sizeof(get)) {
1363
                        duprintf("get_entries: %u < %u\n", *len, sizeof(get));
1364
                        ret = -EINVAL;
1365
                } else if (copy_from_user(&get, user, sizeof(get)) != 0) {
1366
                        ret = -EFAULT;
1367
                } else if (*len != sizeof(struct ip6t_get_entries) + get.size) {
1368
                        duprintf("get_entries: %u != %u\n", *len,
1369
                                 sizeof(struct ip6t_get_entries) + get.size);
1370
                        ret = -EINVAL;
1371
                } else
1372
                        ret = get_entries(&get, user);
1373
                break;
1374
        }
1375
 
1376
        default:
1377
                duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd);
1378
                ret = -EINVAL;
1379
        }
1380
 
1381
        return ret;
1382
}
1383
 
1384
/* Registration hooks for targets. */
1385
int
1386
ip6t_register_target(struct ip6t_target *target)
1387
{
1388
        int ret;
1389
 
1390
        MOD_INC_USE_COUNT;
1391
        ret = down_interruptible(&ip6t_mutex);
1392
        if (ret != 0) {
1393
                MOD_DEC_USE_COUNT;
1394
                return ret;
1395
        }
1396
        if (!list_named_insert(&ip6t_target, target)) {
1397
                duprintf("ip6t_register_target: `%s' already in list!\n",
1398
                         target->name);
1399
                ret = -EINVAL;
1400
                MOD_DEC_USE_COUNT;
1401
        }
1402
        up(&ip6t_mutex);
1403
        return ret;
1404
}
1405
 
1406
void
1407
ip6t_unregister_target(struct ip6t_target *target)
1408
{
1409
        down(&ip6t_mutex);
1410
        LIST_DELETE(&ip6t_target, target);
1411
        up(&ip6t_mutex);
1412
        MOD_DEC_USE_COUNT;
1413
}
1414
 
1415
int
1416
ip6t_register_match(struct ip6t_match *match)
1417
{
1418
        int ret;
1419
 
1420
        MOD_INC_USE_COUNT;
1421
        ret = down_interruptible(&ip6t_mutex);
1422
        if (ret != 0) {
1423
                MOD_DEC_USE_COUNT;
1424
                return ret;
1425
        }
1426
        if (!list_named_insert(&ip6t_match, match)) {
1427
                duprintf("ip6t_register_match: `%s' already in list!\n",
1428
                         match->name);
1429
                MOD_DEC_USE_COUNT;
1430
                ret = -EINVAL;
1431
        }
1432
        up(&ip6t_mutex);
1433
 
1434
        return ret;
1435
}
1436
 
1437
void
1438
ip6t_unregister_match(struct ip6t_match *match)
1439
{
1440
        down(&ip6t_mutex);
1441
        LIST_DELETE(&ip6t_match, match);
1442
        up(&ip6t_mutex);
1443
        MOD_DEC_USE_COUNT;
1444
}
1445
 
1446
int ip6t_register_table(struct ip6t_table *table)
1447
{
1448
        int ret;
1449
        struct ip6t_table_info *newinfo;
1450
        static struct ip6t_table_info bootstrap
1451
                = { 0, 0, 0, { 0 }, { 0 }, { } };
1452
 
1453
        MOD_INC_USE_COUNT;
1454
        newinfo = vmalloc(sizeof(struct ip6t_table_info)
1455
                          + SMP_ALIGN(table->table->size) * smp_num_cpus);
1456
        if (!newinfo) {
1457
                ret = -ENOMEM;
1458
                MOD_DEC_USE_COUNT;
1459
                return ret;
1460
        }
1461
        memcpy(newinfo->entries, table->table->entries, table->table->size);
1462
 
1463
        ret = translate_table(table->name, table->valid_hooks,
1464
                              newinfo, table->table->size,
1465
                              table->table->num_entries,
1466
                              table->table->hook_entry,
1467
                              table->table->underflow);
1468
        if (ret != 0) {
1469
                vfree(newinfo);
1470
                MOD_DEC_USE_COUNT;
1471
                return ret;
1472
        }
1473
 
1474
        ret = down_interruptible(&ip6t_mutex);
1475
        if (ret != 0) {
1476
                vfree(newinfo);
1477
                MOD_DEC_USE_COUNT;
1478
                return ret;
1479
        }
1480
 
1481
        /* Don't autoload: we'd eat our tail... */
1482
        if (list_named_find(&ip6t_tables, table->name)) {
1483
                ret = -EEXIST;
1484
                goto free_unlock;
1485
        }
1486
 
1487
        /* Simplifies replace_table code. */
1488
        table->private = &bootstrap;
1489
        if (!replace_table(table, 0, newinfo, &ret))
1490
                goto free_unlock;
1491
 
1492
        duprintf("table->private->number = %u\n",
1493
                 table->private->number);
1494
 
1495
        /* save number of initial entries */
1496
        table->private->initial_entries = table->private->number;
1497
 
1498
        table->lock = RW_LOCK_UNLOCKED;
1499
        list_prepend(&ip6t_tables, table);
1500
 
1501
 unlock:
1502
        up(&ip6t_mutex);
1503
        return ret;
1504
 
1505
 free_unlock:
1506
        vfree(newinfo);
1507
        MOD_DEC_USE_COUNT;
1508
        goto unlock;
1509
}
1510
 
1511
void ip6t_unregister_table(struct ip6t_table *table)
1512
{
1513
        down(&ip6t_mutex);
1514
        LIST_DELETE(&ip6t_tables, table);
1515
        up(&ip6t_mutex);
1516
 
1517
        /* Decrease module usage counts and free resources */
1518
        IP6T_ENTRY_ITERATE(table->private->entries, table->private->size,
1519
                          cleanup_entry, NULL);
1520
        vfree(table->private);
1521
        MOD_DEC_USE_COUNT;
1522
}
1523
 
1524
/* Returns 1 if the port is matched by the range, 0 otherwise */
1525
static inline int
1526
port_match(u_int16_t min, u_int16_t max, u_int16_t port, int invert)
1527
{
1528
        int ret;
1529
 
1530
        ret = (port >= min && port <= max) ^ invert;
1531
        return ret;
1532
}
1533
 
1534
static int
1535
tcp_find_option(u_int8_t option,
1536
                const struct tcphdr *tcp,
1537
                u_int16_t datalen,
1538
                int invert,
1539
                int *hotdrop)
1540
{
1541
        unsigned int i = sizeof(struct tcphdr);
1542
        const u_int8_t *opt = (u_int8_t *)tcp;
1543
 
1544
        duprintf("tcp_match: finding option\n");
1545
        /* If we don't have the whole header, drop packet. */
1546
        if (tcp->doff * 4 < sizeof(struct tcphdr) ||
1547
            tcp->doff * 4 > datalen) {
1548
                *hotdrop = 1;
1549
                return 0;
1550
        }
1551
 
1552
        while (i < tcp->doff * 4) {
1553
                if (opt[i] == option) return !invert;
1554
                if (opt[i] < 2) i++;
1555
                else i += opt[i+1]?:1;
1556
        }
1557
 
1558
        return invert;
1559
}
1560
 
1561
static int
1562
tcp_match(const struct sk_buff *skb,
1563
          const struct net_device *in,
1564
          const struct net_device *out,
1565
          const void *matchinfo,
1566
          int offset,
1567
          const void *hdr,
1568
          u_int16_t datalen,
1569
          int *hotdrop)
1570
{
1571
        const struct tcphdr *tcp;
1572
        const struct ip6t_tcp *tcpinfo = matchinfo;
1573
        int tcpoff;
1574
        u8 nexthdr = skb->nh.ipv6h->nexthdr;
1575
 
1576
        /* To quote Alan:
1577
 
1578
           Don't allow a fragment of TCP 8 bytes in. Nobody normal
1579
           causes this. Its a cracker trying to break in by doing a
1580
           flag overwrite to pass the direction checks.
1581
        */
1582
 
1583
        if (offset == 1) {
1584
                duprintf("Dropping evil TCP offset=1 frag.\n");
1585
                *hotdrop = 1;
1586
                return 0;
1587
        } else if (offset == 0 && datalen < sizeof(struct tcphdr)) {
1588
                /* We've been asked to examine this packet, and we
1589
                   can't.  Hence, no choice but to drop. */
1590
                duprintf("Dropping evil TCP offset=0 tinygram.\n");
1591
                *hotdrop = 1;
1592
                return 0;
1593
        }
1594
 
1595
        tcpoff = (u8*)(skb->nh.ipv6h + 1) - skb->data;
1596
        tcpoff = ipv6_skip_exthdr(skb, tcpoff, &nexthdr, skb->len - tcpoff);
1597
        if (tcpoff < 0 || tcpoff > skb->len) {
1598
                duprintf("tcp_match: cannot skip exthdr. Dropping.\n");
1599
                *hotdrop = 1;
1600
                return 0;
1601
        } else if (nexthdr == IPPROTO_FRAGMENT)
1602
                return 0;
1603
        else if (nexthdr != IPPROTO_TCP ||
1604
                 skb->len - tcpoff < sizeof(struct tcphdr)) {
1605
                /* cannot be occured */
1606
                duprintf("tcp_match: cannot get TCP header. Dropping.\n");
1607
                *hotdrop = 1;
1608
                return 0;
1609
        }
1610
 
1611
        tcp = (struct tcphdr *)(skb->data + tcpoff);
1612
 
1613
        /* FIXME: Try tcp doff >> packet len against various stacks --RR */
1614
 
1615
#define FWINVTCP(bool,invflg) ((bool) ^ !!(tcpinfo->invflags & invflg))
1616
 
1617
        /* Must not be a fragment. */
1618
        return !offset
1619
                && port_match(tcpinfo->spts[0], tcpinfo->spts[1],
1620
                              ntohs(tcp->source),
1621
                              !!(tcpinfo->invflags & IP6T_TCP_INV_SRCPT))
1622
                && port_match(tcpinfo->dpts[0], tcpinfo->dpts[1],
1623
                              ntohs(tcp->dest),
1624
                              !!(tcpinfo->invflags & IP6T_TCP_INV_DSTPT))
1625
                && FWINVTCP((((unsigned char *)tcp)[13]
1626
                             & tcpinfo->flg_mask)
1627
                            == tcpinfo->flg_cmp,
1628
                            IP6T_TCP_INV_FLAGS)
1629
                && (!tcpinfo->option
1630
                    || tcp_find_option(tcpinfo->option, tcp, datalen,
1631
                                       tcpinfo->invflags
1632
                                       & IP6T_TCP_INV_OPTION,
1633
                                       hotdrop));
1634
}
1635
 
1636
/* Called when user tries to insert an entry of this type. */
1637
static int
1638
tcp_checkentry(const char *tablename,
1639
               const struct ip6t_ip6 *ipv6,
1640
               void *matchinfo,
1641
               unsigned int matchsize,
1642
               unsigned int hook_mask)
1643
{
1644
        const struct ip6t_tcp *tcpinfo = matchinfo;
1645
 
1646
        /* Must specify proto == TCP, and no unknown invflags */
1647
        return ipv6->proto == IPPROTO_TCP
1648
                && !(ipv6->invflags & IP6T_INV_PROTO)
1649
                && matchsize == IP6T_ALIGN(sizeof(struct ip6t_tcp))
1650
                && !(tcpinfo->invflags & ~IP6T_TCP_INV_MASK);
1651
}
1652
 
1653
static int
1654
udp_match(const struct sk_buff *skb,
1655
          const struct net_device *in,
1656
          const struct net_device *out,
1657
          const void *matchinfo,
1658
          int offset,
1659
          const void *hdr,
1660
          u_int16_t datalen,
1661
          int *hotdrop)
1662
{
1663
        const struct udphdr *udp;
1664
        const struct ip6t_udp *udpinfo = matchinfo;
1665
        int udpoff;
1666
        u8 nexthdr = skb->nh.ipv6h->nexthdr;
1667
 
1668
        if (offset == 0 && datalen < sizeof(struct udphdr)) {
1669
                /* We've been asked to examine this packet, and we
1670
                   can't.  Hence, no choice but to drop. */
1671
                duprintf("Dropping evil UDP tinygram.\n");
1672
                *hotdrop = 1;
1673
                return 0;
1674
        }
1675
 
1676
        udpoff = (u8*)(skb->nh.ipv6h + 1) - skb->data;
1677
        udpoff = ipv6_skip_exthdr(skb, udpoff, &nexthdr, skb->len - udpoff);
1678
        if (udpoff < 0 || udpoff > skb->len) {
1679
                duprintf("udp_match: cannot skip exthdr. Dropping.\n");
1680
                *hotdrop = 1;
1681
                return 0;
1682
        } else if (nexthdr == IPPROTO_FRAGMENT)
1683
                return 0;
1684
        else if (nexthdr != IPPROTO_UDP ||
1685
                 skb->len - udpoff < sizeof(struct udphdr)) {
1686
                duprintf("udp_match: cannot get UDP header. Dropping.\n");
1687
                *hotdrop = 1;
1688
                return 0;
1689
        }
1690
 
1691
        udp = (struct udphdr *)(skb->data + udpoff);
1692
 
1693
        /* Must not be a fragment. */
1694
        return !offset
1695
                && port_match(udpinfo->spts[0], udpinfo->spts[1],
1696
                              ntohs(udp->source),
1697
                              !!(udpinfo->invflags & IP6T_UDP_INV_SRCPT))
1698
                && port_match(udpinfo->dpts[0], udpinfo->dpts[1],
1699
                              ntohs(udp->dest),
1700
                              !!(udpinfo->invflags & IP6T_UDP_INV_DSTPT));
1701
}
1702
 
1703
/* Called when user tries to insert an entry of this type. */
1704
static int
1705
udp_checkentry(const char *tablename,
1706
               const struct ip6t_ip6 *ipv6,
1707
               void *matchinfo,
1708
               unsigned int matchinfosize,
1709
               unsigned int hook_mask)
1710
{
1711
        const struct ip6t_udp *udpinfo = matchinfo;
1712
 
1713
        /* Must specify proto == UDP, and no unknown invflags */
1714
        if (ipv6->proto != IPPROTO_UDP || (ipv6->invflags & IP6T_INV_PROTO)) {
1715
                duprintf("ip6t_udp: Protocol %u != %u\n", ipv6->proto,
1716
                         IPPROTO_UDP);
1717
                return 0;
1718
        }
1719
        if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_udp))) {
1720
                duprintf("ip6t_udp: matchsize %u != %u\n",
1721
                         matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_udp)));
1722
                return 0;
1723
        }
1724
        if (udpinfo->invflags & ~IP6T_UDP_INV_MASK) {
1725
                duprintf("ip6t_udp: unknown flags %X\n",
1726
                         udpinfo->invflags);
1727
                return 0;
1728
        }
1729
 
1730
        return 1;
1731
}
1732
 
1733
/* Returns 1 if the type and code is matched by the range, 0 otherwise */
1734
static inline int
1735
icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
1736
                     u_int8_t type, u_int8_t code,
1737
                     int invert)
1738
{
1739
        return (type == test_type && code >= min_code && code <= max_code)
1740
                ^ invert;
1741
}
1742
 
1743
static int
1744
icmp6_match(const struct sk_buff *skb,
1745
           const struct net_device *in,
1746
           const struct net_device *out,
1747
           const void *matchinfo,
1748
           int offset,
1749
           const void *hdr,
1750
           u_int16_t datalen,
1751
           int *hotdrop)
1752
{
1753
        const struct icmp6hdr *icmp = hdr;
1754
        const struct ip6t_icmp *icmpinfo = matchinfo;
1755
 
1756
        if (offset == 0 && datalen < 2) {
1757
                /* We've been asked to examine this packet, and we
1758
                   can't.  Hence, no choice but to drop. */
1759
                duprintf("Dropping evil ICMP tinygram.\n");
1760
                *hotdrop = 1;
1761
                return 0;
1762
        }
1763
 
1764
        /* Must not be a fragment. */
1765
        return !offset
1766
                && icmp6_type_code_match(icmpinfo->type,
1767
                                        icmpinfo->code[0],
1768
                                        icmpinfo->code[1],
1769
                                        icmp->icmp6_type, icmp->icmp6_code,
1770
                                        !!(icmpinfo->invflags&IP6T_ICMP_INV));
1771
}
1772
 
1773
/* Called when user tries to insert an entry of this type. */
1774
static int
1775
icmp6_checkentry(const char *tablename,
1776
           const struct ip6t_ip6 *ipv6,
1777
           void *matchinfo,
1778
           unsigned int matchsize,
1779
           unsigned int hook_mask)
1780
{
1781
        const struct ip6t_icmp *icmpinfo = matchinfo;
1782
 
1783
        /* Must specify proto == ICMP, and no unknown invflags */
1784
        return ipv6->proto == IPPROTO_ICMPV6
1785
                && !(ipv6->invflags & IP6T_INV_PROTO)
1786
                && matchsize == IP6T_ALIGN(sizeof(struct ip6t_icmp))
1787
                && !(icmpinfo->invflags & ~IP6T_ICMP_INV);
1788
}
1789
 
1790
/* The built-in targets: standard (NULL) and error. */
1791
static struct ip6t_target ip6t_standard_target
1792
= { { NULL, NULL }, IP6T_STANDARD_TARGET, NULL, NULL, NULL };
1793
static struct ip6t_target ip6t_error_target
1794
= { { NULL, NULL }, IP6T_ERROR_TARGET, ip6t_error, NULL, NULL };
1795
 
1796
static struct nf_sockopt_ops ip6t_sockopts
1797
= { { NULL, NULL }, PF_INET6, IP6T_BASE_CTL, IP6T_SO_SET_MAX+1, do_ip6t_set_ctl,
1798
    IP6T_BASE_CTL, IP6T_SO_GET_MAX+1, do_ip6t_get_ctl, 0, NULL  };
1799
 
1800
static struct ip6t_match tcp_matchstruct
1801
= { { NULL, NULL }, "tcp", &tcp_match, &tcp_checkentry, NULL };
1802
static struct ip6t_match udp_matchstruct
1803
= { { NULL, NULL }, "udp", &udp_match, &udp_checkentry, NULL };
1804
static struct ip6t_match icmp6_matchstruct
1805
= { { NULL, NULL }, "icmp6", &icmp6_match, &icmp6_checkentry, NULL };
1806
 
1807
#ifdef CONFIG_PROC_FS
1808
static inline int print_name(const char *i,
1809
                             off_t start_offset, char *buffer, int length,
1810
                             off_t *pos, unsigned int *count)
1811
{
1812
        if ((*count)++ >= start_offset) {
1813
                unsigned int namelen;
1814
 
1815
                namelen = sprintf(buffer + *pos, "%s\n",
1816
                                  i + sizeof(struct list_head));
1817
                if (*pos + namelen > length) {
1818
                        /* Stop iterating */
1819
                        return 1;
1820
                }
1821
                *pos += namelen;
1822
        }
1823
        return 0;
1824
}
1825
 
1826
static int ip6t_get_tables(char *buffer, char **start, off_t offset, int length)
1827
{
1828
        off_t pos = 0;
1829
        unsigned int count = 0;
1830
 
1831
        if (down_interruptible(&ip6t_mutex) != 0)
1832
                return 0;
1833
 
1834
        LIST_FIND(&ip6t_tables, print_name, char *,
1835
                  offset, buffer, length, &pos, &count);
1836
 
1837
        up(&ip6t_mutex);
1838
 
1839
        /* `start' hack - see fs/proc/generic.c line ~105 */
1840
        *start=(char *)((unsigned long)count-offset);
1841
        return pos;
1842
}
1843
 
1844
static int ip6t_get_targets(char *buffer, char **start, off_t offset, int length)
1845
{
1846
        off_t pos = 0;
1847
        unsigned int count = 0;
1848
 
1849
        if (down_interruptible(&ip6t_mutex) != 0)
1850
                return 0;
1851
 
1852
        LIST_FIND(&ip6t_target, print_name, char *,
1853
                  offset, buffer, length, &pos, &count);
1854
 
1855
        up(&ip6t_mutex);
1856
 
1857
        *start = (char *)((unsigned long)count - offset);
1858
        return pos;
1859
}
1860
 
1861
static int ip6t_get_matches(char *buffer, char **start, off_t offset, int length)
1862
{
1863
        off_t pos = 0;
1864
        unsigned int count = 0;
1865
 
1866
        if (down_interruptible(&ip6t_mutex) != 0)
1867
                return 0;
1868
 
1869
        LIST_FIND(&ip6t_match, print_name, char *,
1870
                  offset, buffer, length, &pos, &count);
1871
 
1872
        up(&ip6t_mutex);
1873
 
1874
        *start = (char *)((unsigned long)count - offset);
1875
        return pos;
1876
}
1877
 
1878
static struct { char *name; get_info_t *get_info; } ip6t_proc_entry[] =
1879
{ { "ip6_tables_names", ip6t_get_tables },
1880
  { "ip6_tables_targets", ip6t_get_targets },
1881
  { "ip6_tables_matches", ip6t_get_matches },
1882
  { NULL, NULL} };
1883
#endif /*CONFIG_PROC_FS*/
1884
 
1885
static int __init init(void)
1886
{
1887
        int ret;
1888
 
1889
        /* Noone else will be downing sem now, so we won't sleep */
1890
        down(&ip6t_mutex);
1891
        list_append(&ip6t_target, &ip6t_standard_target);
1892
        list_append(&ip6t_target, &ip6t_error_target);
1893
        list_append(&ip6t_match, &tcp_matchstruct);
1894
        list_append(&ip6t_match, &udp_matchstruct);
1895
        list_append(&ip6t_match, &icmp6_matchstruct);
1896
        up(&ip6t_mutex);
1897
 
1898
        /* Register setsockopt */
1899
        ret = nf_register_sockopt(&ip6t_sockopts);
1900
        if (ret < 0) {
1901
                duprintf("Unable to register sockopts.\n");
1902
                return ret;
1903
        }
1904
 
1905
#ifdef CONFIG_PROC_FS
1906
        {
1907
                struct proc_dir_entry *proc;
1908
                int i;
1909
 
1910
                for (i = 0; ip6t_proc_entry[i].name; i++) {
1911
                        proc = proc_net_create(ip6t_proc_entry[i].name, 0,
1912
                                               ip6t_proc_entry[i].get_info);
1913
                        if (!proc) {
1914
                                while (--i >= 0)
1915
                                       proc_net_remove(ip6t_proc_entry[i].name);
1916
                                nf_unregister_sockopt(&ip6t_sockopts);
1917
                                return -ENOMEM;
1918
                        }
1919
                        proc->owner = THIS_MODULE;
1920
                }
1921
        }
1922
#endif
1923
 
1924
        printk("ip6_tables: (C) 2000-2002 Netfilter core team\n");
1925
        return 0;
1926
}
1927
 
1928
static void __exit fini(void)
1929
{
1930
        nf_unregister_sockopt(&ip6t_sockopts);
1931
#ifdef CONFIG_PROC_FS
1932
        {
1933
                int i;
1934
                for (i = 0; ip6t_proc_entry[i].name; i++)
1935
                        proc_net_remove(ip6t_proc_entry[i].name);
1936
        }
1937
#endif
1938
}
1939
 
1940
EXPORT_SYMBOL(ip6t_register_table);
1941
EXPORT_SYMBOL(ip6t_unregister_table);
1942
EXPORT_SYMBOL(ip6t_do_table);
1943
EXPORT_SYMBOL(ip6t_register_match);
1944
EXPORT_SYMBOL(ip6t_unregister_match);
1945
EXPORT_SYMBOL(ip6t_register_target);
1946
EXPORT_SYMBOL(ip6t_unregister_target);
1947
EXPORT_SYMBOL(ip6t_ext_hdr);
1948
 
1949
module_init(init);
1950
module_exit(fini);
1951
MODULE_LICENSE("GPL");

powered by: WebSVN 2.1.0

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