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

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [net/] [ipv4/] [netfilter/] [arp_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 for ARP packets.
3
 *
4
 * Based heavily, if not almost entirely, upon ip_tables.c framework.
5
 *
6
 * Some ARP specific bits are:
7
 *
8
 * Copyright (C) 2002 David S. Miller (davem@redhat.com)
9
 *
10
 */
11
 
12
#include <linux/config.h>
13
#include <linux/kernel.h>
14
#include <linux/skbuff.h>
15
#include <linux/netdevice.h>
16
#include <linux/if_arp.h>
17
#include <linux/kmod.h>
18
#include <linux/vmalloc.h>
19
#include <linux/proc_fs.h>
20
#include <linux/module.h>
21
#include <linux/init.h>
22
 
23
#include <asm/uaccess.h>
24
#include <asm/semaphore.h>
25
 
26
#include <linux/netfilter_arp/arp_tables.h>
27
 
28
/*#define DEBUG_ARP_TABLES*/
29
/*#define DEBUG_ARP_TABLES_USER*/
30
 
31
#ifdef DEBUG_ARP_TABLES
32
#define dprintf(format, args...)  printk(format , ## args)
33
#else
34
#define dprintf(format, args...)
35
#endif
36
 
37
#ifdef DEBUG_ARP_TABLES_USER
38
#define duprintf(format, args...) printk(format , ## args)
39
#else
40
#define duprintf(format, args...)
41
#endif
42
 
43
#ifdef CONFIG_NETFILTER_DEBUG
44
#define ARP_NF_ASSERT(x)                                        \
45
do {                                                            \
46
        if (!(x))                                               \
47
                printk("ARP_NF_ASSERT: %s:%s:%u\n",             \
48
                       __FUNCTION__, __FILE__, __LINE__);       \
49
} while(0)
50
#else
51
#define ARP_NF_ASSERT(x)
52
#endif
53
#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
54
 
55
static DECLARE_MUTEX(arpt_mutex);
56
 
57
#define ASSERT_READ_LOCK(x) ARP_NF_ASSERT(down_trylock(&arpt_mutex) != 0)
58
#define ASSERT_WRITE_LOCK(x) ARP_NF_ASSERT(down_trylock(&arpt_mutex) != 0)
59
#include <linux/netfilter_ipv4/lockhelp.h>
60
#include <linux/netfilter_ipv4/listhelp.h>
61
 
62
struct arpt_table_info {
63
        unsigned int size;
64
        unsigned int number;
65
        unsigned int initial_entries;
66
        unsigned int hook_entry[NF_ARP_NUMHOOKS];
67
        unsigned int underflow[NF_ARP_NUMHOOKS];
68
        char entries[0] __attribute__((aligned(SMP_CACHE_BYTES)));
69
};
70
 
71
static LIST_HEAD(arpt_target);
72
static LIST_HEAD(arpt_tables);
73
#define ADD_COUNTER(c,b,p) do { (c).bcnt += (b); (c).pcnt += (p); } while(0)
74
 
75
#ifdef CONFIG_SMP
76
#define TABLE_OFFSET(t,p) (SMP_ALIGN((t)->size)*(p))
77
#else
78
#define TABLE_OFFSET(t,p) 0
79
#endif
80
 
81
static inline int arp_devaddr_compare(const struct arpt_devaddr_info *ap,
82
                                      char *hdr_addr, int len)
83
{
84
        int i, ret;
85
 
86
        if (len > ARPT_DEV_ADDR_LEN_MAX)
87
                len = ARPT_DEV_ADDR_LEN_MAX;
88
 
89
        ret = 0;
90
        for (i = 0; i < len; i++)
91
                ret |= (hdr_addr[i] ^ ap->addr[i]) & ap->mask[i];
92
 
93
        return (ret != 0);
94
}
95
 
96
/* Returns whether packet matches rule or not. */
97
static inline int arp_packet_match(const struct arphdr *arphdr,
98
                                   struct net_device *dev,
99
                                   const char *indev,
100
                                   const char *outdev,
101
                                   const struct arpt_arp *arpinfo)
102
{
103
        char *arpptr = (char *)(arphdr + 1);
104
        char *src_devaddr, *tgt_devaddr;
105
        u32 *src_ipaddr, *tgt_ipaddr;
106
        int i, ret;
107
 
108
#define FWINV(bool,invflg) ((bool) ^ !!(arpinfo->invflags & invflg))
109
 
110
        if (FWINV((arphdr->ar_op & arpinfo->arpop_mask) != arpinfo->arpop,
111
                  ARPT_INV_ARPOP)) {
112
                dprintf("ARP operation field mismatch.\n");
113
                dprintf("ar_op: %04x info->arpop: %04x info->arpop_mask: %04x\n",
114
                        arphdr->ar_op, arpinfo->arpop, arpinfo->arpop_mask);
115
                return 0;
116
        }
117
 
118
        if (FWINV((arphdr->ar_hrd & arpinfo->arhrd_mask) != arpinfo->arhrd,
119
                  ARPT_INV_ARPHRD)) {
120
                dprintf("ARP hardware address format mismatch.\n");
121
                dprintf("ar_hrd: %04x info->arhrd: %04x info->arhrd_mask: %04x\n",
122
                        arphdr->ar_hrd, arpinfo->arhrd, arpinfo->arhrd_mask);
123
                return 0;
124
        }
125
 
126
        if (FWINV((arphdr->ar_pro & arpinfo->arpro_mask) != arpinfo->arpro,
127
                  ARPT_INV_ARPPRO)) {
128
                dprintf("ARP protocol address format mismatch.\n");
129
                dprintf("ar_pro: %04x info->arpro: %04x info->arpro_mask: %04x\n",
130
                        arphdr->ar_pro, arpinfo->arpro, arpinfo->arpro_mask);
131
                return 0;
132
        }
133
 
134
        if (FWINV((arphdr->ar_hln & arpinfo->arhln_mask) != arpinfo->arhln,
135
                  ARPT_INV_ARPHLN)) {
136
                dprintf("ARP hardware address length mismatch.\n");
137
                dprintf("ar_hln: %02x info->arhln: %02x info->arhln_mask: %02x\n",
138
                        arphdr->ar_hln, arpinfo->arhln, arpinfo->arhln_mask);
139
                return 0;
140
        }
141
 
142
        src_devaddr = arpptr;
143
        arpptr += dev->addr_len;
144
        src_ipaddr = (u32 *) arpptr;
145
        arpptr += sizeof(u32);
146
        tgt_devaddr = arpptr;
147
        arpptr += dev->addr_len;
148
        tgt_ipaddr = (u32 *) arpptr;
149
 
150
        if (FWINV(arp_devaddr_compare(&arpinfo->src_devaddr, src_devaddr, dev->addr_len),
151
                  ARPT_INV_SRCDEVADDR) ||
152
            FWINV(arp_devaddr_compare(&arpinfo->tgt_devaddr, tgt_devaddr, dev->addr_len),
153
                  ARPT_INV_TGTDEVADDR)) {
154
                dprintf("Source or target device address mismatch.\n");
155
 
156
                return 0;
157
        }
158
 
159
        if (FWINV(((*src_ipaddr) & arpinfo->smsk.s_addr) != arpinfo->src.s_addr,
160
                  ARPT_INV_SRCIP) ||
161
            FWINV((((*tgt_ipaddr) & arpinfo->tmsk.s_addr) != arpinfo->tgt.s_addr),
162
                  ARPT_INV_TGTIP)) {
163
                dprintf("Source or target IP address mismatch.\n");
164
 
165
                dprintf("SRC: %u.%u.%u.%u. Mask: %u.%u.%u.%u. Target: %u.%u.%u.%u.%s\n",
166
                        NIPQUAD(*src_ipaddr),
167
                        NIPQUAD(arpinfo->smsk.s_addr),
168
                        NIPQUAD(arpinfo->src.s_addr),
169
                        arpinfo->invflags & ARPT_INV_SRCIP ? " (INV)" : "");
170
                dprintf("TGT: %u.%u.%u.%u Mask: %u.%u.%u.%u Target: %u.%u.%u.%u.%s\n",
171
                        NIPQUAD(*tgt_ipaddr),
172
                        NIPQUAD(arpinfo->tmsk.s_addr),
173
                        NIPQUAD(arpinfo->tgt.s_addr),
174
                        arpinfo->invflags & ARPT_INV_TGTIP ? " (INV)" : "");
175
                return 0;
176
        }
177
 
178
        /* Look for ifname matches; this should unroll nicely. */
179
        for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
180
                ret |= (((const unsigned long *)indev)[i]
181
                        ^ ((const unsigned long *)arpinfo->iniface)[i])
182
                        & ((const unsigned long *)arpinfo->iniface_mask)[i];
183
        }
184
 
185
        if (FWINV(ret != 0, ARPT_INV_VIA_IN)) {
186
                dprintf("VIA in mismatch (%s vs %s).%s\n",
187
                        indev, arpinfo->iniface,
188
                        arpinfo->invflags&ARPT_INV_VIA_IN ?" (INV)":"");
189
                return 0;
190
        }
191
 
192
        for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
193
                ret |= (((const unsigned long *)outdev)[i]
194
                        ^ ((const unsigned long *)arpinfo->outiface)[i])
195
                        & ((const unsigned long *)arpinfo->outiface_mask)[i];
196
        }
197
 
198
        if (FWINV(ret != 0, ARPT_INV_VIA_OUT)) {
199
                dprintf("VIA out mismatch (%s vs %s).%s\n",
200
                        outdev, arpinfo->outiface,
201
                        arpinfo->invflags&ARPT_INV_VIA_OUT ?" (INV)":"");
202
                return 0;
203
        }
204
 
205
        return 1;
206
}
207
 
208
static inline int arp_checkentry(const struct arpt_arp *arp)
209
{
210
        if (arp->flags & ~ARPT_F_MASK) {
211
                duprintf("Unknown flag bits set: %08X\n",
212
                         arp->flags & ~ARPT_F_MASK);
213
                return 0;
214
        }
215
        if (arp->invflags & ~ARPT_INV_MASK) {
216
                duprintf("Unknown invflag bits set: %08X\n",
217
                         arp->invflags & ~ARPT_INV_MASK);
218
                return 0;
219
        }
220
 
221
        return 1;
222
}
223
 
224
static unsigned int arpt_error(struct sk_buff **pskb,
225
                               unsigned int hooknum,
226
                               const struct net_device *in,
227
                               const struct net_device *out,
228
                               const void *targinfo,
229
                               void *userinfo)
230
{
231
        if (net_ratelimit())
232
                printk("arp_tables: error: '%s'\n", (char *)targinfo);
233
 
234
        return NF_DROP;
235
}
236
 
237
static inline struct arpt_entry *get_entry(void *base, unsigned int offset)
238
{
239
        return (struct arpt_entry *)(base + offset);
240
}
241
 
242
unsigned int arpt_do_table(struct sk_buff **pskb,
243
                           unsigned int hook,
244
                           const struct net_device *in,
245
                           const struct net_device *out,
246
                           struct arpt_table *table,
247
                           void *userdata)
248
{
249
        static const char nulldevname[IFNAMSIZ] = { 0 };
250
        unsigned int verdict = NF_DROP;
251
        struct arphdr *arp = (*pskb)->nh.arph;
252
        int hotdrop = 0;
253
        struct arpt_entry *e, *back;
254
        const char *indev, *outdev;
255
        void *table_base;
256
 
257
        indev = in ? in->name : nulldevname;
258
        outdev = out ? out->name : nulldevname;
259
 
260
        read_lock_bh(&table->lock);
261
        table_base = (void *)table->private->entries
262
                + TABLE_OFFSET(table->private,
263
                               cpu_number_map(smp_processor_id()));
264
        e = get_entry(table_base, table->private->hook_entry[hook]);
265
        back = get_entry(table_base, table->private->underflow[hook]);
266
 
267
        do {
268
                if (arp_packet_match(arp, (*pskb)->dev, indev, outdev, &e->arp)) {
269
                        struct arpt_entry_target *t;
270
                        int hdr_len;
271
 
272
                        hdr_len = sizeof(*arp) + (2 * sizeof(struct in_addr)) +
273
                                (2 * (*pskb)->dev->addr_len);
274
                        ADD_COUNTER(e->counters, hdr_len, 1);
275
 
276
                        t = arpt_get_target(e);
277
 
278
                        /* Standard target? */
279
                        if (!t->u.kernel.target->target) {
280
                                int v;
281
 
282
                                v = ((struct arpt_standard_target *)t)->verdict;
283
                                if (v < 0) {
284
                                        /* Pop from stack? */
285
                                        if (v != ARPT_RETURN) {
286
                                                verdict = (unsigned)(-v) - 1;
287
                                                break;
288
                                        }
289
                                        e = back;
290
                                        back = get_entry(table_base,
291
                                                         back->comefrom);
292
                                        continue;
293
                                }
294
                                if (table_base + v
295
                                    != (void *)e + e->next_offset) {
296
                                        /* Save old back ptr in next entry */
297
                                        struct arpt_entry *next
298
                                                = (void *)e + e->next_offset;
299
                                        next->comefrom =
300
                                                (void *)back - table_base;
301
 
302
                                        /* set back pointer to next entry */
303
                                        back = next;
304
                                }
305
 
306
                                e = get_entry(table_base, v);
307
                        } else {
308
                                /* Targets which reenter must return
309
                                 * abs. verdicts
310
                                 */
311
                                verdict = t->u.kernel.target->target(pskb,
312
                                                                     hook,
313
                                                                     in, out,
314
                                                                     t->data,
315
                                                                     userdata);
316
 
317
                                /* Target might have changed stuff. */
318
                                arp = (*pskb)->nh.arph;
319
 
320
                                if (verdict == ARPT_CONTINUE)
321
                                        e = (void *)e + e->next_offset;
322
                                else
323
                                        /* Verdict */
324
                                        break;
325
                        }
326
                } else {
327
                        e = (void *)e + e->next_offset;
328
                }
329
        } while (!hotdrop);
330
        read_unlock_bh(&table->lock);
331
 
332
        if (hotdrop)
333
                return NF_DROP;
334
        else
335
                return verdict;
336
}
337
 
338
static inline void *find_inlist_lock_noload(struct list_head *head,
339
                                            const char *name,
340
                                            int *error,
341
                                            struct semaphore *mutex)
342
{
343
        void *ret;
344
 
345
        *error = down_interruptible(mutex);
346
        if (*error != 0)
347
                return NULL;
348
 
349
        ret = list_named_find(head, name);
350
        if (!ret) {
351
                *error = -ENOENT;
352
                up(mutex);
353
        }
354
        return ret;
355
}
356
 
357
#ifndef CONFIG_KMOD
358
#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
359
#else
360
static void *
361
find_inlist_lock(struct list_head *head,
362
                 const char *name,
363
                 const char *prefix,
364
                 int *error,
365
                 struct semaphore *mutex)
366
{
367
        void *ret;
368
 
369
        ret = find_inlist_lock_noload(head, name, error, mutex);
370
        if (!ret) {
371
                char modulename[ARPT_FUNCTION_MAXNAMELEN + strlen(prefix) + 1];
372
                strcpy(modulename, prefix);
373
                strcat(modulename, name);
374
                duprintf("find_inlist: loading `%s'.\n", modulename);
375
                request_module(modulename);
376
                ret = find_inlist_lock_noload(head, name, error, mutex);
377
        }
378
 
379
        return ret;
380
}
381
#endif
382
 
383
static inline struct arpt_table *find_table_lock(const char *name, int *error, struct semaphore *mutex)
384
{
385
        return find_inlist_lock(&arpt_tables, name, "arptable_", error, mutex);
386
}
387
 
388
static inline struct arpt_target *find_target_lock(const char *name, int *error, struct semaphore *mutex)
389
{
390
        return find_inlist_lock(&arpt_target, name, "arpt_", error, mutex);
391
}
392
 
393
/* All zeroes == unconditional rule. */
394
static inline int unconditional(const struct arpt_arp *arp)
395
{
396
        unsigned int i;
397
 
398
        for (i = 0; i < sizeof(*arp)/sizeof(__u32); i++)
399
                if (((__u32 *)arp)[i])
400
                        return 0;
401
 
402
        return 1;
403
}
404
 
405
/* Figures out from what hook each rule can be called: returns 0 if
406
 * there are loops.  Puts hook bitmask in comefrom.
407
 */
408
static int mark_source_chains(struct arpt_table_info *newinfo, unsigned int valid_hooks)
409
{
410
        unsigned int hook;
411
 
412
        /* No recursion; use packet counter to save back ptrs (reset
413
         * to 0 as we leave), and comefrom to save source hook bitmask.
414
         */
415
        for (hook = 0; hook < NF_ARP_NUMHOOKS; hook++) {
416
                unsigned int pos = newinfo->hook_entry[hook];
417
                struct arpt_entry *e
418
                        = (struct arpt_entry *)(newinfo->entries + pos);
419
 
420
                if (!(valid_hooks & (1 << hook)))
421
                        continue;
422
 
423
                /* Set initial back pointer. */
424
                e->counters.pcnt = pos;
425
 
426
                for (;;) {
427
                        struct arpt_standard_target *t
428
                                = (void *)arpt_get_target(e);
429
 
430
                        if (e->comefrom & (1 << NF_ARP_NUMHOOKS)) {
431
                                printk("arptables: loop hook %u pos %u %08X.\n",
432
                                       hook, pos, e->comefrom);
433
                                return 0;
434
                        }
435
                        e->comefrom
436
                                |= ((1 << hook) | (1 << NF_ARP_NUMHOOKS));
437
 
438
                        /* Unconditional return/END. */
439
                        if (e->target_offset == sizeof(struct arpt_entry)
440
                            && (strcmp(t->target.u.user.name,
441
                                       ARPT_STANDARD_TARGET) == 0)
442
                            && t->verdict < 0
443
                            && unconditional(&e->arp)) {
444
                                unsigned int oldpos, size;
445
 
446
                                /* Return: backtrack through the last
447
                                 * big jump.
448
                                 */
449
                                do {
450
                                        e->comefrom ^= (1<<NF_ARP_NUMHOOKS);
451
                                        oldpos = pos;
452
                                        pos = e->counters.pcnt;
453
                                        e->counters.pcnt = 0;
454
 
455
                                        /* We're at the start. */
456
                                        if (pos == oldpos)
457
                                                goto next;
458
 
459
                                        e = (struct arpt_entry *)
460
                                                (newinfo->entries + pos);
461
                                } while (oldpos == pos + e->next_offset);
462
 
463
                                /* Move along one */
464
                                size = e->next_offset;
465
                                e = (struct arpt_entry *)
466
                                        (newinfo->entries + pos + size);
467
                                e->counters.pcnt = pos;
468
                                pos += size;
469
                        } else {
470
                                int newpos = t->verdict;
471
 
472
                                if (strcmp(t->target.u.user.name,
473
                                           ARPT_STANDARD_TARGET) == 0
474
                                    && newpos >= 0) {
475
                                        /* This a jump; chase it. */
476
                                        duprintf("Jump rule %u -> %u\n",
477
                                                 pos, newpos);
478
                                } else {
479
                                        /* ... this is a fallthru */
480
                                        newpos = pos + e->next_offset;
481
                                }
482
                                e = (struct arpt_entry *)
483
                                        (newinfo->entries + newpos);
484
                                e->counters.pcnt = pos;
485
                                pos = newpos;
486
                        }
487
                }
488
                next:
489
                duprintf("Finished chain %u\n", hook);
490
        }
491
        return 1;
492
}
493
 
494
static inline int standard_check(const struct arpt_entry_target *t,
495
                                 unsigned int max_offset)
496
{
497
        struct arpt_standard_target *targ = (void *)t;
498
 
499
        /* Check standard info. */
500
        if (t->u.target_size
501
            != ARPT_ALIGN(sizeof(struct arpt_standard_target))) {
502
                duprintf("arpt_standard_check: target size %u != %Zu\n",
503
                         t->u.target_size,
504
                         ARPT_ALIGN(sizeof(struct arpt_standard_target)));
505
                return 0;
506
        }
507
 
508
        if (targ->verdict >= 0
509
            && targ->verdict > max_offset - sizeof(struct arpt_entry)) {
510
                duprintf("arpt_standard_check: bad verdict (%i)\n",
511
                         targ->verdict);
512
                return 0;
513
        }
514
 
515
        if (targ->verdict < -NF_MAX_VERDICT - 1) {
516
                duprintf("arpt_standard_check: bad negative verdict (%i)\n",
517
                         targ->verdict);
518
                return 0;
519
        }
520
        return 1;
521
}
522
 
523
static struct arpt_target arpt_standard_target;
524
 
525
static inline int check_entry(struct arpt_entry *e, const char *name, unsigned int size,
526
                              unsigned int *i)
527
{
528
        struct arpt_entry_target *t;
529
        struct arpt_target *target;
530
        int ret;
531
 
532
        if (!arp_checkentry(&e->arp)) {
533
                duprintf("arp_tables: arp check failed %p %s.\n", e, name);
534
                return -EINVAL;
535
        }
536
 
537
        t = arpt_get_target(e);
538
        target = find_target_lock(t->u.user.name, &ret, &arpt_mutex);
539
        if (!target) {
540
                duprintf("check_entry: `%s' not found\n", t->u.user.name);
541
                goto out;
542
        }
543
        if (target->me)
544
                __MOD_INC_USE_COUNT(target->me);
545
        t->u.kernel.target = target;
546
        up(&arpt_mutex);
547
 
548
        if (t->u.kernel.target == &arpt_standard_target) {
549
                if (!standard_check(t, size)) {
550
                        ret = -EINVAL;
551
                        goto out;
552
                }
553
        } else if (t->u.kernel.target->checkentry
554
                   && !t->u.kernel.target->checkentry(name, e, t->data,
555
                                                      t->u.target_size
556
                                                      - sizeof(*t),
557
                                                      e->comefrom)) {
558
                if (t->u.kernel.target->me)
559
                        __MOD_DEC_USE_COUNT(t->u.kernel.target->me);
560
                duprintf("arp_tables: check failed for `%s'.\n",
561
                         t->u.kernel.target->name);
562
                ret = -EINVAL;
563
                goto out;
564
        }
565
 
566
        (*i)++;
567
        return 0;
568
 
569
out:
570
        return ret;
571
}
572
 
573
static inline int check_entry_size_and_hooks(struct arpt_entry *e,
574
                                             struct arpt_table_info *newinfo,
575
                                             unsigned char *base,
576
                                             unsigned char *limit,
577
                                             const unsigned int *hook_entries,
578
                                             const unsigned int *underflows,
579
                                             unsigned int *i)
580
{
581
        unsigned int h;
582
 
583
        if ((unsigned long)e % __alignof__(struct arpt_entry) != 0
584
            || (unsigned char *)e + sizeof(struct arpt_entry) >= limit) {
585
                duprintf("Bad offset %p\n", e);
586
                return -EINVAL;
587
        }
588
 
589
        if (e->next_offset
590
            < sizeof(struct arpt_entry) + sizeof(struct arpt_entry_target)) {
591
                duprintf("checking: element %p size %u\n",
592
                         e, e->next_offset);
593
                return -EINVAL;
594
        }
595
 
596
        /* Check hooks & underflows */
597
        for (h = 0; h < NF_ARP_NUMHOOKS; h++) {
598
                if ((unsigned char *)e - base == hook_entries[h])
599
                        newinfo->hook_entry[h] = hook_entries[h];
600
                if ((unsigned char *)e - base == underflows[h])
601
                        newinfo->underflow[h] = underflows[h];
602
        }
603
 
604
        /* FIXME: underflows must be unconditional, standard verdicts
605
           < 0 (not ARPT_RETURN). --RR */
606
 
607
        /* Clear counters and comefrom */
608
        e->counters = ((struct arpt_counters) { 0, 0 });
609
        e->comefrom = 0;
610
 
611
        (*i)++;
612
        return 0;
613
}
614
 
615
static inline int cleanup_entry(struct arpt_entry *e, unsigned int *i)
616
{
617
        struct arpt_entry_target *t;
618
 
619
        if (i && (*i)-- == 0)
620
                return 1;
621
 
622
        t = arpt_get_target(e);
623
        if (t->u.kernel.target->destroy)
624
                t->u.kernel.target->destroy(t->data,
625
                                            t->u.target_size - sizeof(*t));
626
        if (t->u.kernel.target->me)
627
                __MOD_DEC_USE_COUNT(t->u.kernel.target->me);
628
 
629
        return 0;
630
}
631
 
632
/* Checks and translates the user-supplied table segment (held in
633
 * newinfo).
634
 */
635
static int translate_table(const char *name,
636
                           unsigned int valid_hooks,
637
                           struct arpt_table_info *newinfo,
638
                           unsigned int size,
639
                           unsigned int number,
640
                           const unsigned int *hook_entries,
641
                           const unsigned int *underflows)
642
{
643
        unsigned int i;
644
        int ret;
645
 
646
        newinfo->size = size;
647
        newinfo->number = number;
648
 
649
        /* Init all hooks to impossible value. */
650
        for (i = 0; i < NF_ARP_NUMHOOKS; i++) {
651
                newinfo->hook_entry[i] = 0xFFFFFFFF;
652
                newinfo->underflow[i] = 0xFFFFFFFF;
653
        }
654
 
655
        duprintf("translate_table: size %u\n", newinfo->size);
656
        i = 0;
657
 
658
        /* Walk through entries, checking offsets. */
659
        ret = ARPT_ENTRY_ITERATE(newinfo->entries, newinfo->size,
660
                                 check_entry_size_and_hooks,
661
                                 newinfo,
662
                                 newinfo->entries,
663
                                 newinfo->entries + size,
664
                                 hook_entries, underflows, &i);
665
        duprintf("translate_table: ARPT_ENTRY_ITERATE gives %d\n", ret);
666
        if (ret != 0)
667
                return ret;
668
 
669
        if (i != number) {
670
                duprintf("translate_table: %u not %u entries\n",
671
                         i, number);
672
                return -EINVAL;
673
        }
674
 
675
        /* Check hooks all assigned */
676
        for (i = 0; i < NF_ARP_NUMHOOKS; i++) {
677
                /* Only hooks which are valid */
678
                if (!(valid_hooks & (1 << i)))
679
                        continue;
680
                if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
681
                        duprintf("Invalid hook entry %u %u\n",
682
                                 i, hook_entries[i]);
683
                        return -EINVAL;
684
                }
685
                if (newinfo->underflow[i] == 0xFFFFFFFF) {
686
                        duprintf("Invalid underflow %u %u\n",
687
                                 i, underflows[i]);
688
                        return -EINVAL;
689
                }
690
        }
691
 
692
        if (!mark_source_chains(newinfo, valid_hooks)) {
693
                duprintf("Looping hook\n");
694
                return -ELOOP;
695
        }
696
 
697
        /* Finally, each sanity check must pass */
698
        i = 0;
699
        ret = ARPT_ENTRY_ITERATE(newinfo->entries, newinfo->size,
700
                                 check_entry, name, size, &i);
701
 
702
        if (ret != 0) {
703
                ARPT_ENTRY_ITERATE(newinfo->entries, newinfo->size,
704
                                   cleanup_entry, &i);
705
                return ret;
706
        }
707
 
708
        /* And one copy for every other CPU */
709
        for (i = 1; i < smp_num_cpus; i++) {
710
                memcpy(newinfo->entries + SMP_ALIGN(newinfo->size)*i,
711
                       newinfo->entries,
712
                       SMP_ALIGN(newinfo->size));
713
        }
714
 
715
        return ret;
716
}
717
 
718
static struct arpt_table_info *replace_table(struct arpt_table *table,
719
                                             unsigned int num_counters,
720
                                             struct arpt_table_info *newinfo,
721
                                             int *error)
722
{
723
        struct arpt_table_info *oldinfo;
724
 
725
        /* Do the substitution. */
726
        write_lock_bh(&table->lock);
727
        /* Check inside lock: is the old number correct? */
728
        if (num_counters != table->private->number) {
729
                duprintf("num_counters != table->private->number (%u/%u)\n",
730
                         num_counters, table->private->number);
731
                write_unlock_bh(&table->lock);
732
                *error = -EAGAIN;
733
                return NULL;
734
        }
735
        oldinfo = table->private;
736
        table->private = newinfo;
737
        newinfo->initial_entries = oldinfo->initial_entries;
738
        write_unlock_bh(&table->lock);
739
 
740
        return oldinfo;
741
}
742
 
743
/* Gets counters. */
744
static inline int add_entry_to_counter(const struct arpt_entry *e,
745
                                       struct arpt_counters total[],
746
                                       unsigned int *i)
747
{
748
        ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
749
 
750
        (*i)++;
751
        return 0;
752
}
753
 
754
static void get_counters(const struct arpt_table_info *t,
755
                         struct arpt_counters counters[])
756
{
757
        unsigned int cpu;
758
        unsigned int i;
759
 
760
        for (cpu = 0; cpu < smp_num_cpus; cpu++) {
761
                i = 0;
762
                ARPT_ENTRY_ITERATE(t->entries + TABLE_OFFSET(t, cpu),
763
                                   t->size,
764
                                   add_entry_to_counter,
765
                                   counters,
766
                                   &i);
767
        }
768
}
769
 
770
static int copy_entries_to_user(unsigned int total_size,
771
                                struct arpt_table *table,
772
                                void *userptr)
773
{
774
        unsigned int off, num, countersize;
775
        struct arpt_entry *e;
776
        struct arpt_counters *counters;
777
        int ret = 0;
778
 
779
        /* We need atomic snapshot of counters: rest doesn't change
780
         * (other than comefrom, which userspace doesn't care
781
         * about).
782
         */
783
        countersize = sizeof(struct arpt_counters) * table->private->number;
784
        counters = vmalloc(countersize);
785
 
786
        if (counters == NULL)
787
                return -ENOMEM;
788
 
789
        /* First, sum counters... */
790
        memset(counters, 0, countersize);
791
        write_lock_bh(&table->lock);
792
        get_counters(table->private, counters);
793
        write_unlock_bh(&table->lock);
794
 
795
        /* ... then copy entire thing from CPU 0... */
796
        if (copy_to_user(userptr, table->private->entries, total_size) != 0) {
797
                ret = -EFAULT;
798
                goto free_counters;
799
        }
800
 
801
        /* FIXME: use iterator macros --RR */
802
        /* ... then go back and fix counters and names */
803
        for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
804
                struct arpt_entry_target *t;
805
 
806
                e = (struct arpt_entry *)(table->private->entries + off);
807
                if (copy_to_user(userptr + off
808
                                 + offsetof(struct arpt_entry, counters),
809
                                 &counters[num],
810
                                 sizeof(counters[num])) != 0) {
811
                        ret = -EFAULT;
812
                        goto free_counters;
813
                }
814
 
815
                t = arpt_get_target(e);
816
                if (copy_to_user(userptr + off + e->target_offset
817
                                 + offsetof(struct arpt_entry_target,
818
                                            u.user.name),
819
                                 t->u.kernel.target->name,
820
                                 strlen(t->u.kernel.target->name)+1) != 0) {
821
                        ret = -EFAULT;
822
                        goto free_counters;
823
                }
824
        }
825
 
826
 free_counters:
827
        vfree(counters);
828
        return ret;
829
}
830
 
831
static int get_entries(const struct arpt_get_entries *entries,
832
                       struct arpt_get_entries *uptr)
833
{
834
        int ret;
835
        struct arpt_table *t;
836
 
837
        t = find_table_lock(entries->name, &ret, &arpt_mutex);
838
        if (t) {
839
                duprintf("t->private->number = %u\n",
840
                         t->private->number);
841
                if (entries->size == t->private->size)
842
                        ret = copy_entries_to_user(t->private->size,
843
                                                   t, uptr->entrytable);
844
                else {
845
                        duprintf("get_entries: I've got %u not %u!\n",
846
                                 t->private->size,
847
                                 entries->size);
848
                        ret = -EINVAL;
849
                }
850
                up(&arpt_mutex);
851
        } else
852
                duprintf("get_entries: Can't find %s!\n",
853
                         entries->name);
854
 
855
        return ret;
856
}
857
 
858
static int do_replace(void *user, unsigned int len)
859
{
860
        int ret;
861
        struct arpt_replace tmp;
862
        struct arpt_table *t;
863
        struct arpt_table_info *newinfo, *oldinfo;
864
        struct arpt_counters *counters;
865
 
866
        if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
867
                return -EFAULT;
868
 
869
        /* Hack: Causes ipchains to give correct error msg --RR */
870
        if (len != sizeof(tmp) + tmp.size)
871
                return -ENOPROTOOPT;
872
 
873
        /* Pedantry: prevent them from hitting BUG() in vmalloc.c --RR */
874
        if ((SMP_ALIGN(tmp.size) >> PAGE_SHIFT) + 2 > num_physpages)
875
                return -ENOMEM;
876
 
877
        newinfo = vmalloc(sizeof(struct arpt_table_info)
878
                          + SMP_ALIGN(tmp.size) * smp_num_cpus);
879
        if (!newinfo)
880
                return -ENOMEM;
881
 
882
        if (copy_from_user(newinfo->entries, user + sizeof(tmp),
883
                           tmp.size) != 0) {
884
                ret = -EFAULT;
885
                goto free_newinfo;
886
        }
887
 
888
        counters = vmalloc(tmp.num_counters * sizeof(struct arpt_counters));
889
        if (!counters) {
890
                ret = -ENOMEM;
891
                goto free_newinfo;
892
        }
893
        memset(counters, 0, tmp.num_counters * sizeof(struct arpt_counters));
894
 
895
        ret = translate_table(tmp.name, tmp.valid_hooks,
896
                              newinfo, tmp.size, tmp.num_entries,
897
                              tmp.hook_entry, tmp.underflow);
898
        if (ret != 0)
899
                goto free_newinfo_counters;
900
 
901
        duprintf("arp_tables: Translated table\n");
902
 
903
        t = find_table_lock(tmp.name, &ret, &arpt_mutex);
904
        if (!t)
905
                goto free_newinfo_counters_untrans;
906
 
907
        /* You lied! */
908
        if (tmp.valid_hooks != t->valid_hooks) {
909
                duprintf("Valid hook crap: %08X vs %08X\n",
910
                         tmp.valid_hooks, t->valid_hooks);
911
                ret = -EINVAL;
912
                goto free_newinfo_counters_untrans_unlock;
913
        }
914
 
915
        oldinfo = replace_table(t, tmp.num_counters, newinfo, &ret);
916
        if (!oldinfo)
917
                goto free_newinfo_counters_untrans_unlock;
918
 
919
        /* Update module usage count based on number of rules */
920
        duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
921
                oldinfo->number, oldinfo->initial_entries, newinfo->number);
922
        if (t->me && (oldinfo->number <= oldinfo->initial_entries) &&
923
            (newinfo->number > oldinfo->initial_entries))
924
                __MOD_INC_USE_COUNT(t->me);
925
        else if (t->me && (oldinfo->number > oldinfo->initial_entries) &&
926
                 (newinfo->number <= oldinfo->initial_entries))
927
                __MOD_DEC_USE_COUNT(t->me);
928
 
929
        /* Get the old counters. */
930
        get_counters(oldinfo, counters);
931
        /* Decrease module usage counts and free resource */
932
        ARPT_ENTRY_ITERATE(oldinfo->entries, oldinfo->size, cleanup_entry,NULL);
933
        vfree(oldinfo);
934
        /* Silent error: too late now. */
935
        copy_to_user(tmp.counters, counters,
936
                     sizeof(struct arpt_counters) * tmp.num_counters);
937
        vfree(counters);
938
        up(&arpt_mutex);
939
        return 0;
940
 
941
 free_newinfo_counters_untrans_unlock:
942
        up(&arpt_mutex);
943
 free_newinfo_counters_untrans:
944
        ARPT_ENTRY_ITERATE(newinfo->entries, newinfo->size, cleanup_entry, NULL);
945
 free_newinfo_counters:
946
        vfree(counters);
947
 free_newinfo:
948
        vfree(newinfo);
949
        return ret;
950
}
951
 
952
/* We're lazy, and add to the first CPU; overflow works its fey magic
953
 * and everything is OK.
954
 */
955
static inline int add_counter_to_entry(struct arpt_entry *e,
956
                                       const struct arpt_counters addme[],
957
                                       unsigned int *i)
958
{
959
 
960
        ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
961
 
962
        (*i)++;
963
        return 0;
964
}
965
 
966
static int do_add_counters(void *user, unsigned int len)
967
{
968
        unsigned int i;
969
        struct arpt_counters_info tmp, *paddc;
970
        struct arpt_table *t;
971
        int ret;
972
 
973
        if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
974
                return -EFAULT;
975
 
976
        if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct arpt_counters))
977
                return -EINVAL;
978
 
979
        paddc = vmalloc(len);
980
        if (!paddc)
981
                return -ENOMEM;
982
 
983
        if (copy_from_user(paddc, user, len) != 0) {
984
                ret = -EFAULT;
985
                goto free;
986
        }
987
 
988
        t = find_table_lock(tmp.name, &ret, &arpt_mutex);
989
        if (!t)
990
                goto free;
991
 
992
        write_lock_bh(&t->lock);
993
        if (t->private->number != paddc->num_counters) {
994
                ret = -EINVAL;
995
                goto unlock_up_free;
996
        }
997
 
998
        i = 0;
999
        ARPT_ENTRY_ITERATE(t->private->entries,
1000
                           t->private->size,
1001
                           add_counter_to_entry,
1002
                           paddc->counters,
1003
                           &i);
1004
 unlock_up_free:
1005
        write_unlock_bh(&t->lock);
1006
        up(&arpt_mutex);
1007
 free:
1008
        vfree(paddc);
1009
 
1010
        return ret;
1011
}
1012
 
1013
static int do_arpt_set_ctl(struct sock *sk, int cmd, void *user, unsigned int len)
1014
{
1015
        int ret;
1016
 
1017
        if (!capable(CAP_NET_ADMIN))
1018
                return -EPERM;
1019
 
1020
        switch (cmd) {
1021
        case ARPT_SO_SET_REPLACE:
1022
                ret = do_replace(user, len);
1023
                break;
1024
 
1025
        case ARPT_SO_SET_ADD_COUNTERS:
1026
                ret = do_add_counters(user, len);
1027
                break;
1028
 
1029
        default:
1030
                duprintf("do_arpt_set_ctl:  unknown request %i\n", cmd);
1031
                ret = -EINVAL;
1032
        }
1033
 
1034
        return ret;
1035
}
1036
 
1037
static int do_arpt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
1038
{
1039
        int ret;
1040
 
1041
        if (!capable(CAP_NET_ADMIN))
1042
                return -EPERM;
1043
 
1044
        switch (cmd) {
1045
        case ARPT_SO_GET_INFO: {
1046
                char name[ARPT_TABLE_MAXNAMELEN];
1047
                struct arpt_table *t;
1048
 
1049
                if (*len != sizeof(struct arpt_getinfo)) {
1050
                        duprintf("length %u != %Zu\n", *len,
1051
                                 sizeof(struct arpt_getinfo));
1052
                        ret = -EINVAL;
1053
                        break;
1054
                }
1055
 
1056
                if (copy_from_user(name, user, sizeof(name)) != 0) {
1057
                        ret = -EFAULT;
1058
                        break;
1059
                }
1060
                name[ARPT_TABLE_MAXNAMELEN-1] = '\0';
1061
                t = find_table_lock(name, &ret, &arpt_mutex);
1062
                if (t) {
1063
                        struct arpt_getinfo info;
1064
 
1065
                        info.valid_hooks = t->valid_hooks;
1066
                        memcpy(info.hook_entry, t->private->hook_entry,
1067
                               sizeof(info.hook_entry));
1068
                        memcpy(info.underflow, t->private->underflow,
1069
                               sizeof(info.underflow));
1070
                        info.num_entries = t->private->number;
1071
                        info.size = t->private->size;
1072
                        strcpy(info.name, name);
1073
 
1074
                        if (copy_to_user(user, &info, *len) != 0)
1075
                                ret = -EFAULT;
1076
                        else
1077
                                ret = 0;
1078
 
1079
                        up(&arpt_mutex);
1080
                }
1081
        }
1082
        break;
1083
 
1084
        case ARPT_SO_GET_ENTRIES: {
1085
                struct arpt_get_entries get;
1086
 
1087
                if (*len < sizeof(get)) {
1088
                        duprintf("get_entries: %u < %Zu\n", *len, sizeof(get));
1089
                        ret = -EINVAL;
1090
                } else if (copy_from_user(&get, user, sizeof(get)) != 0) {
1091
                        ret = -EFAULT;
1092
                } else if (*len != sizeof(struct arpt_get_entries) + get.size) {
1093
                        duprintf("get_entries: %u != %Zu\n", *len,
1094
                                 sizeof(struct arpt_get_entries) + get.size);
1095
                        ret = -EINVAL;
1096
                } else
1097
                        ret = get_entries(&get, user);
1098
                break;
1099
        }
1100
 
1101
        default:
1102
                duprintf("do_arpt_get_ctl: unknown request %i\n", cmd);
1103
                ret = -EINVAL;
1104
        }
1105
 
1106
        return ret;
1107
}
1108
 
1109
/* Registration hooks for targets. */
1110
int arpt_register_target(struct arpt_target *target)
1111
{
1112
        int ret;
1113
 
1114
        MOD_INC_USE_COUNT;
1115
        ret = down_interruptible(&arpt_mutex);
1116
        if (ret != 0) {
1117
                MOD_DEC_USE_COUNT;
1118
                return ret;
1119
        }
1120
        if (!list_named_insert(&arpt_target, target)) {
1121
                duprintf("arpt_register_target: `%s' already in list!\n",
1122
                         target->name);
1123
                ret = -EINVAL;
1124
                MOD_DEC_USE_COUNT;
1125
        }
1126
        up(&arpt_mutex);
1127
        return ret;
1128
}
1129
 
1130
void arpt_unregister_target(struct arpt_target *target)
1131
{
1132
        down(&arpt_mutex);
1133
        LIST_DELETE(&arpt_target, target);
1134
        up(&arpt_mutex);
1135
        MOD_DEC_USE_COUNT;
1136
}
1137
 
1138
int arpt_register_table(struct arpt_table *table)
1139
{
1140
        int ret;
1141
        struct arpt_table_info *newinfo;
1142
        static struct arpt_table_info bootstrap
1143
                = { 0, 0, 0, { 0 }, { 0 }, { } };
1144
 
1145
        MOD_INC_USE_COUNT;
1146
        newinfo = vmalloc(sizeof(struct arpt_table_info)
1147
                          + SMP_ALIGN(table->table->size) * smp_num_cpus);
1148
        if (!newinfo) {
1149
                ret = -ENOMEM;
1150
                MOD_DEC_USE_COUNT;
1151
                return ret;
1152
        }
1153
        memcpy(newinfo->entries, table->table->entries, table->table->size);
1154
 
1155
        ret = translate_table(table->name, table->valid_hooks,
1156
                              newinfo, table->table->size,
1157
                              table->table->num_entries,
1158
                              table->table->hook_entry,
1159
                              table->table->underflow);
1160
        duprintf("arpt_register_table: translate table gives %d\n", ret);
1161
        if (ret != 0) {
1162
                vfree(newinfo);
1163
                MOD_DEC_USE_COUNT;
1164
                return ret;
1165
        }
1166
 
1167
        ret = down_interruptible(&arpt_mutex);
1168
        if (ret != 0) {
1169
                vfree(newinfo);
1170
                MOD_DEC_USE_COUNT;
1171
                return ret;
1172
        }
1173
 
1174
        /* Don't autoload: we'd eat our tail... */
1175
        if (list_named_find(&arpt_tables, table->name)) {
1176
                ret = -EEXIST;
1177
                goto free_unlock;
1178
        }
1179
 
1180
        /* Simplifies replace_table code. */
1181
        table->private = &bootstrap;
1182
        if (!replace_table(table, 0, newinfo, &ret))
1183
                goto free_unlock;
1184
 
1185
        duprintf("table->private->number = %u\n",
1186
                 table->private->number);
1187
 
1188
        /* save number of initial entries */
1189
        table->private->initial_entries = table->private->number;
1190
 
1191
        table->lock = RW_LOCK_UNLOCKED;
1192
        list_prepend(&arpt_tables, table);
1193
 
1194
 unlock:
1195
        up(&arpt_mutex);
1196
        return ret;
1197
 
1198
 free_unlock:
1199
        vfree(newinfo);
1200
        MOD_DEC_USE_COUNT;
1201
        goto unlock;
1202
}
1203
 
1204
void arpt_unregister_table(struct arpt_table *table)
1205
{
1206
        down(&arpt_mutex);
1207
        LIST_DELETE(&arpt_tables, table);
1208
        up(&arpt_mutex);
1209
 
1210
        /* Decrease module usage counts and free resources */
1211
        ARPT_ENTRY_ITERATE(table->private->entries, table->private->size,
1212
                           cleanup_entry, NULL);
1213
        vfree(table->private);
1214
        MOD_DEC_USE_COUNT;
1215
}
1216
 
1217
/* The built-in targets: standard (NULL) and error. */
1218
static struct arpt_target arpt_standard_target
1219
= { { NULL, NULL }, ARPT_STANDARD_TARGET, NULL, NULL, NULL };
1220
static struct arpt_target arpt_error_target
1221
= { { NULL, NULL }, ARPT_ERROR_TARGET, arpt_error, NULL, NULL };
1222
 
1223
static struct nf_sockopt_ops arpt_sockopts
1224
= { { NULL, NULL }, PF_INET, ARPT_BASE_CTL, ARPT_SO_SET_MAX+1, do_arpt_set_ctl,
1225
    ARPT_BASE_CTL, ARPT_SO_GET_MAX+1, do_arpt_get_ctl, 0, NULL  };
1226
 
1227
#ifdef CONFIG_PROC_FS
1228
static inline int print_name(const struct arpt_table *t,
1229
                             off_t start_offset, char *buffer, int length,
1230
                             off_t *pos, unsigned int *count)
1231
{
1232
        if ((*count)++ >= start_offset) {
1233
                unsigned int namelen;
1234
 
1235
                namelen = sprintf(buffer + *pos, "%s\n", t->name);
1236
                if (*pos + namelen > length) {
1237
                        /* Stop iterating */
1238
                        return 1;
1239
                }
1240
                *pos += namelen;
1241
        }
1242
        return 0;
1243
}
1244
 
1245
static int arpt_get_tables(char *buffer, char **start, off_t offset, int length)
1246
{
1247
        off_t pos = 0;
1248
        unsigned int count = 0;
1249
 
1250
        if (down_interruptible(&arpt_mutex) != 0)
1251
                return 0;
1252
 
1253
        LIST_FIND(&arpt_tables, print_name, struct arpt_table *,
1254
                  offset, buffer, length, &pos, &count);
1255
 
1256
        up(&arpt_mutex);
1257
 
1258
        /* `start' hack - see fs/proc/generic.c line ~105 */
1259
        *start=(char *)((unsigned long)count-offset);
1260
        return pos;
1261
}
1262
#endif /*CONFIG_PROC_FS*/
1263
 
1264
static int __init init(void)
1265
{
1266
        int ret;
1267
 
1268
        /* Noone else will be downing sem now, so we won't sleep */
1269
        down(&arpt_mutex);
1270
        list_append(&arpt_target, &arpt_standard_target);
1271
        list_append(&arpt_target, &arpt_error_target);
1272
        up(&arpt_mutex);
1273
 
1274
        /* Register setsockopt */
1275
        ret = nf_register_sockopt(&arpt_sockopts);
1276
        if (ret < 0) {
1277
                duprintf("Unable to register sockopts.\n");
1278
                return ret;
1279
        }
1280
 
1281
#ifdef CONFIG_PROC_FS
1282
        {
1283
                struct proc_dir_entry *proc;
1284
 
1285
                proc = proc_net_create("arp_tables_names", 0, arpt_get_tables);
1286
                if (!proc) {
1287
                        nf_unregister_sockopt(&arpt_sockopts);
1288
                        return -ENOMEM;
1289
                }
1290
                proc->owner = THIS_MODULE;
1291
        }
1292
#endif
1293
 
1294
        printk("arp_tables: (C) 2002 David S. Miller\n");
1295
        return 0;
1296
}
1297
 
1298
static void __exit fini(void)
1299
{
1300
        nf_unregister_sockopt(&arpt_sockopts);
1301
#ifdef CONFIG_PROC_FS
1302
        proc_net_remove("arp_tables_names");
1303
#endif
1304
}
1305
 
1306
EXPORT_SYMBOL(arpt_register_table);
1307
EXPORT_SYMBOL(arpt_unregister_table);
1308
EXPORT_SYMBOL(arpt_do_table);
1309
EXPORT_SYMBOL(arpt_register_target);
1310
EXPORT_SYMBOL(arpt_unregister_target);
1311
 
1312
module_init(init);
1313
module_exit(fini);
1314
MODULE_LICENSE("GPL");

powered by: WebSVN 2.1.0

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