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