linux/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-2005 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
  12#include <linux/capability.h>
  13#include <linux/in.h>
  14#include <linux/skbuff.h>
  15#include <linux/kmod.h>
  16#include <linux/vmalloc.h>
  17#include <linux/netdevice.h>
  18#include <linux/module.h>
  19#include <linux/poison.h>
  20#include <linux/icmpv6.h>
  21#include <net/ipv6.h>
  22#include <asm/uaccess.h>
  23#include <linux/mutex.h>
  24#include <linux/proc_fs.h>
  25#include <linux/cpumask.h>
  26
  27#include <linux/netfilter_ipv6/ip6_tables.h>
  28#include <linux/netfilter/x_tables.h>
  29
  30MODULE_LICENSE("GPL");
  31MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
  32MODULE_DESCRIPTION("IPv6 packet filter");
  33
  34#define IPV6_HDR_LEN    (sizeof(struct ipv6hdr))
  35#define IPV6_OPTHDR_LEN (sizeof(struct ipv6_opt_hdr))
  36
  37/*#define DEBUG_IP_FIREWALL*/
  38/*#define DEBUG_ALLOW_ALL*/ /* Useful for remote debugging */
  39/*#define DEBUG_IP_FIREWALL_USER*/
  40
  41#ifdef DEBUG_IP_FIREWALL
  42#define dprintf(format, args...)  printk(format , ## args)
  43#else
  44#define dprintf(format, args...)
  45#endif
  46
  47#ifdef DEBUG_IP_FIREWALL_USER
  48#define duprintf(format, args...) printk(format , ## args)
  49#else
  50#define duprintf(format, args...)
  51#endif
  52
  53#ifdef CONFIG_NETFILTER_DEBUG
  54#define IP_NF_ASSERT(x)                                         \
  55do {                                                            \
  56        if (!(x))                                               \
  57                printk("IP_NF_ASSERT: %s:%s:%u\n",              \
  58                       __FUNCTION__, __FILE__, __LINE__);       \
  59} while(0)
  60#else
  61#define IP_NF_ASSERT(x)
  62#endif
  63
  64#if 0
  65/* All the better to debug you with... */
  66#define static
  67#define inline
  68#endif
  69
  70/*
  71   We keep a set of rules for each CPU, so we can avoid write-locking
  72   them in the softirq when updating the counters and therefore
  73   only need to read-lock in the softirq; doing a write_lock_bh() in user
  74   context stops packets coming through and allows user context to read
  75   the counters or update the rules.
  76
  77   Hence the start of any table is given by get_table() below.  */
  78
  79#if 0
  80#define down(x) do { printk("DOWN:%u:" #x "\n", __LINE__); down(x); } while(0)
  81#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; })
  82#define up(x) do { printk("UP:%u:" #x "\n", __LINE__); up(x); } while(0)
  83#endif
  84
  85/* Check for an extension */
  86int
  87ip6t_ext_hdr(u8 nexthdr)
  88{
  89        return ( (nexthdr == IPPROTO_HOPOPTS)   ||
  90                 (nexthdr == IPPROTO_ROUTING)   ||
  91                 (nexthdr == IPPROTO_FRAGMENT)  ||
  92                 (nexthdr == IPPROTO_ESP)       ||
  93                 (nexthdr == IPPROTO_AH)        ||
  94                 (nexthdr == IPPROTO_NONE)      ||
  95                 (nexthdr == IPPROTO_DSTOPTS) );
  96}
  97
  98/* Returns whether matches rule or not. */
  99static inline bool
 100ip6_packet_match(const struct sk_buff *skb,
 101                 const char *indev,
 102                 const char *outdev,
 103                 const struct ip6t_ip6 *ip6info,
 104                 unsigned int *protoff,
 105                 int *fragoff, bool *hotdrop)
 106{
 107        size_t i;
 108        unsigned long ret;
 109        const struct ipv6hdr *ipv6 = ipv6_hdr(skb);
 110
 111#define FWINV(bool,invflg) ((bool) ^ !!(ip6info->invflags & invflg))
 112
 113        if (FWINV(ipv6_masked_addr_cmp(&ipv6->saddr, &ip6info->smsk,
 114                                       &ip6info->src), IP6T_INV_SRCIP)
 115            || FWINV(ipv6_masked_addr_cmp(&ipv6->daddr, &ip6info->dmsk,
 116                                          &ip6info->dst), IP6T_INV_DSTIP)) {
 117                dprintf("Source or dest mismatch.\n");
 118/*
 119                dprintf("SRC: %u. Mask: %u. Target: %u.%s\n", ip->saddr,
 120                        ipinfo->smsk.s_addr, ipinfo->src.s_addr,
 121                        ipinfo->invflags & IP6T_INV_SRCIP ? " (INV)" : "");
 122                dprintf("DST: %u. Mask: %u. Target: %u.%s\n", ip->daddr,
 123                        ipinfo->dmsk.s_addr, ipinfo->dst.s_addr,
 124                        ipinfo->invflags & IP6T_INV_DSTIP ? " (INV)" : "");*/
 125                return false;
 126        }
 127
 128        /* Look for ifname matches; this should unroll nicely. */
 129        for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
 130                ret |= (((const unsigned long *)indev)[i]
 131                        ^ ((const unsigned long *)ip6info->iniface)[i])
 132                        & ((const unsigned long *)ip6info->iniface_mask)[i];
 133        }
 134
 135        if (FWINV(ret != 0, IP6T_INV_VIA_IN)) {
 136                dprintf("VIA in mismatch (%s vs %s).%s\n",
 137                        indev, ip6info->iniface,
 138                        ip6info->invflags&IP6T_INV_VIA_IN ?" (INV)":"");
 139                return false;
 140        }
 141
 142        for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
 143                ret |= (((const unsigned long *)outdev)[i]
 144                        ^ ((const unsigned long *)ip6info->outiface)[i])
 145                        & ((const unsigned long *)ip6info->outiface_mask)[i];
 146        }
 147
 148        if (FWINV(ret != 0, IP6T_INV_VIA_OUT)) {
 149                dprintf("VIA out mismatch (%s vs %s).%s\n",
 150                        outdev, ip6info->outiface,
 151                        ip6info->invflags&IP6T_INV_VIA_OUT ?" (INV)":"");
 152                return false;
 153        }
 154
 155/* ... might want to do something with class and flowlabel here ... */
 156
 157        /* look for the desired protocol header */
 158        if((ip6info->flags & IP6T_F_PROTO)) {
 159                int protohdr;
 160                unsigned short _frag_off;
 161
 162                protohdr = ipv6_find_hdr(skb, protoff, -1, &_frag_off);
 163                if (protohdr < 0) {
 164                        if (_frag_off == 0)
 165                                *hotdrop = true;
 166                        return false;
 167                }
 168                *fragoff = _frag_off;
 169
 170                dprintf("Packet protocol %hi ?= %s%hi.\n",
 171                                protohdr,
 172                                ip6info->invflags & IP6T_INV_PROTO ? "!":"",
 173                                ip6info->proto);
 174
 175                if (ip6info->proto == protohdr) {
 176                        if(ip6info->invflags & IP6T_INV_PROTO) {
 177                                return false;
 178                        }
 179                        return true;
 180                }
 181
 182                /* We need match for the '-p all', too! */
 183                if ((ip6info->proto != 0) &&
 184                        !(ip6info->invflags & IP6T_INV_PROTO))
 185                        return false;
 186        }
 187        return true;
 188}
 189
 190/* should be ip6 safe */
 191static inline bool
 192ip6_checkentry(const struct ip6t_ip6 *ipv6)
 193{
 194        if (ipv6->flags & ~IP6T_F_MASK) {
 195                duprintf("Unknown flag bits set: %08X\n",
 196                         ipv6->flags & ~IP6T_F_MASK);
 197                return false;
 198        }
 199        if (ipv6->invflags & ~IP6T_INV_MASK) {
 200                duprintf("Unknown invflag bits set: %08X\n",
 201                         ipv6->invflags & ~IP6T_INV_MASK);
 202                return false;
 203        }
 204        return true;
 205}
 206
 207static unsigned int
 208ip6t_error(struct sk_buff *skb,
 209          const struct net_device *in,
 210          const struct net_device *out,
 211          unsigned int hooknum,
 212          const struct xt_target *target,
 213          const void *targinfo)
 214{
 215        if (net_ratelimit())
 216                printk("ip6_tables: error: `%s'\n", (char *)targinfo);
 217
 218        return NF_DROP;
 219}
 220
 221static inline
 222bool do_match(struct ip6t_entry_match *m,
 223              const struct sk_buff *skb,
 224              const struct net_device *in,
 225              const struct net_device *out,
 226              int offset,
 227              unsigned int protoff,
 228              bool *hotdrop)
 229{
 230        /* Stop iteration if it doesn't match */
 231        if (!m->u.kernel.match->match(skb, in, out, m->u.kernel.match, m->data,
 232                                      offset, protoff, hotdrop))
 233                return true;
 234        else
 235                return false;
 236}
 237
 238static inline struct ip6t_entry *
 239get_entry(void *base, unsigned int offset)
 240{
 241        return (struct ip6t_entry *)(base + offset);
 242}
 243
 244/* All zeroes == unconditional rule. */
 245static inline int
 246unconditional(const struct ip6t_ip6 *ipv6)
 247{
 248        unsigned int i;
 249
 250        for (i = 0; i < sizeof(*ipv6); i++)
 251                if (((char *)ipv6)[i])
 252                        break;
 253
 254        return (i == sizeof(*ipv6));
 255}
 256
 257#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
 258    defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
 259/* This cries for unification! */
 260static const char *hooknames[] = {
 261        [NF_IP6_PRE_ROUTING]            = "PREROUTING",
 262        [NF_IP6_LOCAL_IN]               = "INPUT",
 263        [NF_IP6_FORWARD]                = "FORWARD",
 264        [NF_IP6_LOCAL_OUT]              = "OUTPUT",
 265        [NF_IP6_POST_ROUTING]           = "POSTROUTING",
 266};
 267
 268enum nf_ip_trace_comments {
 269        NF_IP6_TRACE_COMMENT_RULE,
 270        NF_IP6_TRACE_COMMENT_RETURN,
 271        NF_IP6_TRACE_COMMENT_POLICY,
 272};
 273
 274static const char *comments[] = {
 275        [NF_IP6_TRACE_COMMENT_RULE]     = "rule",
 276        [NF_IP6_TRACE_COMMENT_RETURN]   = "return",
 277        [NF_IP6_TRACE_COMMENT_POLICY]   = "policy",
 278};
 279
 280static struct nf_loginfo trace_loginfo = {
 281        .type = NF_LOG_TYPE_LOG,
 282        .u = {
 283                .log = {
 284                        .level = 4,
 285                        .logflags = NF_LOG_MASK,
 286                },
 287        },
 288};
 289
 290static inline int
 291get_chainname_rulenum(struct ip6t_entry *s, struct ip6t_entry *e,
 292                      char *hookname, char **chainname,
 293                      char **comment, unsigned int *rulenum)
 294{
 295        struct ip6t_standard_target *t = (void *)ip6t_get_target(s);
 296
 297        if (strcmp(t->target.u.kernel.target->name, IP6T_ERROR_TARGET) == 0) {
 298                /* Head of user chain: ERROR target with chainname */
 299                *chainname = t->target.data;
 300                (*rulenum) = 0;
 301        } else if (s == e) {
 302                (*rulenum)++;
 303
 304                if (s->target_offset == sizeof(struct ip6t_entry)
 305                   && strcmp(t->target.u.kernel.target->name,
 306                             IP6T_STANDARD_TARGET) == 0
 307                   && t->verdict < 0
 308                   && unconditional(&s->ipv6)) {
 309                        /* Tail of chains: STANDARD target (return/policy) */
 310                        *comment = *chainname == hookname
 311                                ? (char *)comments[NF_IP6_TRACE_COMMENT_POLICY]
 312                                : (char *)comments[NF_IP6_TRACE_COMMENT_RETURN];
 313                }
 314                return 1;
 315        } else
 316                (*rulenum)++;
 317
 318        return 0;
 319}
 320
 321static void trace_packet(struct sk_buff *skb,
 322                         unsigned int hook,
 323                         const struct net_device *in,
 324                         const struct net_device *out,
 325                         char *tablename,
 326                         struct xt_table_info *private,
 327                         struct ip6t_entry *e)
 328{
 329        void *table_base;
 330        struct ip6t_entry *root;
 331        char *hookname, *chainname, *comment;
 332        unsigned int rulenum = 0;
 333
 334        table_base = (void *)private->entries[smp_processor_id()];
 335        root = get_entry(table_base, private->hook_entry[hook]);
 336
 337        hookname = chainname = (char *)hooknames[hook];
 338        comment = (char *)comments[NF_IP6_TRACE_COMMENT_RULE];
 339
 340        IP6T_ENTRY_ITERATE(root,
 341                           private->size - private->hook_entry[hook],
 342                           get_chainname_rulenum,
 343                           e, hookname, &chainname, &comment, &rulenum);
 344
 345        nf_log_packet(AF_INET6, hook, skb, in, out, &trace_loginfo,
 346                      "TRACE: %s:%s:%s:%u ",
 347                      tablename, chainname, comment, rulenum);
 348}
 349#endif
 350
 351/* Returns one of the generic firewall policies, like NF_ACCEPT. */
 352unsigned int
 353ip6t_do_table(struct sk_buff *skb,
 354              unsigned int hook,
 355              const struct net_device *in,
 356              const struct net_device *out,
 357              struct xt_table *table)
 358{
 359        static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
 360        int offset = 0;
 361        unsigned int protoff = 0;
 362        bool hotdrop = false;
 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        struct xt_table_info *private;
 369
 370        /* Initialization */
 371        indev = in ? in->name : nulldevname;
 372        outdev = out ? out->name : nulldevname;
 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        private = table->private;
 382        IP_NF_ASSERT(table->valid_hooks & (1 << hook));
 383        table_base = (void *)private->entries[smp_processor_id()];
 384        e = get_entry(table_base, private->hook_entry[hook]);
 385
 386        /* For return from builtin chain */
 387        back = get_entry(table_base, private->underflow[hook]);
 388
 389        do {
 390                IP_NF_ASSERT(e);
 391                IP_NF_ASSERT(back);
 392                if (ip6_packet_match(skb, indev, outdev, &e->ipv6,
 393                        &protoff, &offset, &hotdrop)) {
 394                        struct ip6t_entry_target *t;
 395
 396                        if (IP6T_MATCH_ITERATE(e, do_match,
 397                                               skb, in, out,
 398                                               offset, protoff, &hotdrop) != 0)
 399                                goto no_match;
 400
 401                        ADD_COUNTER(e->counters,
 402                                    ntohs(ipv6_hdr(skb)->payload_len)
 403                                    + IPV6_HDR_LEN,
 404                                    1);
 405
 406                        t = ip6t_get_target(e);
 407                        IP_NF_ASSERT(t->u.kernel.target);
 408
 409#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
 410    defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
 411                        /* The packet is traced: log it */
 412                        if (unlikely(skb->nf_trace))
 413                                trace_packet(skb, hook, in, out,
 414                                             table->name, private, e);
 415#endif
 416                        /* Standard target? */
 417                        if (!t->u.kernel.target->target) {
 418                                int v;
 419
 420                                v = ((struct ip6t_standard_target *)t)->verdict;
 421                                if (v < 0) {
 422                                        /* Pop from stack? */
 423                                        if (v != IP6T_RETURN) {
 424                                                verdict = (unsigned)(-v) - 1;
 425                                                break;
 426                                        }
 427                                        e = back;
 428                                        back = get_entry(table_base,
 429                                                         back->comefrom);
 430                                        continue;
 431                                }
 432                                if (table_base + v != (void *)e + e->next_offset
 433                                    && !(e->ipv6.flags & IP6T_F_GOTO)) {
 434                                        /* Save old back ptr in next entry */
 435                                        struct ip6t_entry *next
 436                                                = (void *)e + e->next_offset;
 437                                        next->comefrom
 438                                                = (void *)back - table_base;
 439                                        /* set back pointer to next entry */
 440                                        back = next;
 441                                }
 442
 443                                e = get_entry(table_base, v);
 444                        } else {
 445                                /* Targets which reenter must return
 446                                   abs. verdicts */
 447#ifdef CONFIG_NETFILTER_DEBUG
 448                                ((struct ip6t_entry *)table_base)->comefrom
 449                                        = 0xeeeeeeec;
 450#endif
 451                                verdict = t->u.kernel.target->target(skb,
 452                                                                     in, out,
 453                                                                     hook,
 454                                                                     t->u.kernel.target,
 455                                                                     t->data);
 456
 457#ifdef CONFIG_NETFILTER_DEBUG
 458                                if (((struct ip6t_entry *)table_base)->comefrom
 459                                    != 0xeeeeeeec
 460                                    && verdict == IP6T_CONTINUE) {
 461                                        printk("Target %s reentered!\n",
 462                                               t->u.kernel.target->name);
 463                                        verdict = NF_DROP;
 464                                }
 465                                ((struct ip6t_entry *)table_base)->comefrom
 466                                        = 0x57acc001;
 467#endif
 468                                if (verdict == IP6T_CONTINUE)
 469                                        e = (void *)e + e->next_offset;
 470                                else
 471                                        /* Verdict */
 472                                        break;
 473                        }
 474                } else {
 475
 476                no_match:
 477                        e = (void *)e + e->next_offset;
 478                }
 479        } while (!hotdrop);
 480
 481#ifdef CONFIG_NETFILTER_DEBUG
 482        ((struct ip6t_entry *)table_base)->comefrom = NETFILTER_LINK_POISON;
 483#endif
 484        read_unlock_bh(&table->lock);
 485
 486#ifdef DEBUG_ALLOW_ALL
 487        return NF_ACCEPT;
 488#else
 489        if (hotdrop)
 490                return NF_DROP;
 491        else return verdict;
 492#endif
 493}
 494
 495/* Figures out from what hook each rule can be called: returns 0 if
 496   there are loops.  Puts hook bitmask in comefrom. */
 497static int
 498mark_source_chains(struct xt_table_info *newinfo,
 499                   unsigned int valid_hooks, void *entry0)
 500{
 501        unsigned int hook;
 502
 503        /* No recursion; use packet counter to save back ptrs (reset
 504           to 0 as we leave), and comefrom to save source hook bitmask */
 505        for (hook = 0; hook < NF_IP6_NUMHOOKS; hook++) {
 506                unsigned int pos = newinfo->hook_entry[hook];
 507                struct ip6t_entry *e
 508                        = (struct ip6t_entry *)(entry0 + pos);
 509                int visited = e->comefrom & (1 << hook);
 510
 511                if (!(valid_hooks & (1 << hook)))
 512                        continue;
 513
 514                /* Set initial back pointer. */
 515                e->counters.pcnt = pos;
 516
 517                for (;;) {
 518                        struct ip6t_standard_target *t
 519                                = (void *)ip6t_get_target(e);
 520
 521                        if (e->comefrom & (1 << NF_IP6_NUMHOOKS)) {
 522                                printk("iptables: loop hook %u pos %u %08X.\n",
 523                                       hook, pos, e->comefrom);
 524                                return 0;
 525                        }
 526                        e->comefrom
 527                                |= ((1 << hook) | (1 << NF_IP6_NUMHOOKS));
 528
 529                        /* Unconditional return/END. */
 530                        if ((e->target_offset == sizeof(struct ip6t_entry)
 531                            && (strcmp(t->target.u.user.name,
 532                                       IP6T_STANDARD_TARGET) == 0)
 533                            && t->verdict < 0
 534                            && unconditional(&e->ipv6)) || visited) {
 535                                unsigned int oldpos, size;
 536
 537                                if (t->verdict < -NF_MAX_VERDICT - 1) {
 538                                        duprintf("mark_source_chains: bad "
 539                                                "negative verdict (%i)\n",
 540                                                                t->verdict);
 541                                        return 0;
 542                                }
 543
 544                                /* Return: backtrack through the last
 545                                   big jump. */
 546                                do {
 547                                        e->comefrom ^= (1<<NF_IP6_NUMHOOKS);
 548#ifdef DEBUG_IP_FIREWALL_USER
 549                                        if (e->comefrom
 550                                            & (1 << NF_IP6_NUMHOOKS)) {
 551                                                duprintf("Back unset "
 552                                                         "on hook %u "
 553                                                         "rule %u\n",
 554                                                         hook, pos);
 555                                        }
 556#endif
 557                                        oldpos = pos;
 558                                        pos = e->counters.pcnt;
 559                                        e->counters.pcnt = 0;
 560
 561                                        /* We're at the start. */
 562                                        if (pos == oldpos)
 563                                                goto next;
 564
 565                                        e = (struct ip6t_entry *)
 566                                                (entry0 + pos);
 567                                } while (oldpos == pos + e->next_offset);
 568
 569                                /* Move along one */
 570                                size = e->next_offset;
 571                                e = (struct ip6t_entry *)
 572                                        (entry0 + pos + size);
 573                                e->counters.pcnt = pos;
 574                                pos += size;
 575                        } else {
 576                                int newpos = t->verdict;
 577
 578                                if (strcmp(t->target.u.user.name,
 579                                           IP6T_STANDARD_TARGET) == 0
 580                                    && newpos >= 0) {
 581                                        if (newpos > newinfo->size -
 582                                                sizeof(struct ip6t_entry)) {
 583                                                duprintf("mark_source_chains: "
 584                                                        "bad verdict (%i)\n",
 585                                                                newpos);
 586                                                return 0;
 587                                        }
 588                                        /* This a jump; chase it. */
 589                                        duprintf("Jump rule %u -> %u\n",
 590                                                 pos, newpos);
 591                                } else {
 592                                        /* ... this is a fallthru */
 593                                        newpos = pos + e->next_offset;
 594                                }
 595                                e = (struct ip6t_entry *)
 596                                        (entry0 + newpos);
 597                                e->counters.pcnt = pos;
 598                                pos = newpos;
 599                        }
 600                }
 601                next:
 602                duprintf("Finished chain %u\n", hook);
 603        }
 604        return 1;
 605}
 606
 607static inline int
 608cleanup_match(struct ip6t_entry_match *m, unsigned int *i)
 609{
 610        if (i && (*i)-- == 0)
 611                return 1;
 612
 613        if (m->u.kernel.match->destroy)
 614                m->u.kernel.match->destroy(m->u.kernel.match, m->data);
 615        module_put(m->u.kernel.match->me);
 616        return 0;
 617}
 618
 619static inline int
 620check_match(struct ip6t_entry_match *m,
 621            const char *name,
 622            const struct ip6t_ip6 *ipv6,
 623            unsigned int hookmask,
 624            unsigned int *i)
 625{
 626        struct xt_match *match;
 627        int ret;
 628
 629        match = try_then_request_module(xt_find_match(AF_INET6, m->u.user.name,
 630                                        m->u.user.revision),
 631                                        "ip6t_%s", m->u.user.name);
 632        if (IS_ERR(match) || !match) {
 633                duprintf("check_match: `%s' not found\n", m->u.user.name);
 634                return match ? PTR_ERR(match) : -ENOENT;
 635        }
 636        m->u.kernel.match = match;
 637
 638        ret = xt_check_match(match, AF_INET6, m->u.match_size - sizeof(*m),
 639                             name, hookmask, ipv6->proto,
 640                             ipv6->invflags & IP6T_INV_PROTO);
 641        if (ret)
 642                goto err;
 643
 644        if (m->u.kernel.match->checkentry
 645            && !m->u.kernel.match->checkentry(name, ipv6, match,  m->data,
 646                                              hookmask)) {
 647                duprintf("ip_tables: check failed for `%s'.\n",
 648                         m->u.kernel.match->name);
 649                ret = -EINVAL;
 650                goto err;
 651        }
 652
 653        (*i)++;
 654        return 0;
 655err:
 656        module_put(m->u.kernel.match->me);
 657        return ret;
 658}
 659
 660static struct xt_target ip6t_standard_target;
 661
 662static inline int
 663check_entry(struct ip6t_entry *e, const char *name, unsigned int size,
 664            unsigned int *i)
 665{
 666        struct ip6t_entry_target *t;
 667        struct xt_target *target;
 668        int ret;
 669        unsigned int j;
 670
 671        if (!ip6_checkentry(&e->ipv6)) {
 672                duprintf("ip_tables: ip check failed %p %s.\n", e, name);
 673                return -EINVAL;
 674        }
 675
 676        if (e->target_offset + sizeof(struct ip6t_entry_target) >
 677                                                                e->next_offset)
 678                return -EINVAL;
 679
 680        j = 0;
 681        ret = IP6T_MATCH_ITERATE(e, check_match, name, &e->ipv6, e->comefrom, &j);
 682        if (ret != 0)
 683                goto cleanup_matches;
 684
 685        t = ip6t_get_target(e);
 686        ret = -EINVAL;
 687        if (e->target_offset + t->u.target_size > e->next_offset)
 688                        goto cleanup_matches;
 689        target = try_then_request_module(xt_find_target(AF_INET6,
 690                                                        t->u.user.name,
 691                                                        t->u.user.revision),
 692                                         "ip6t_%s", t->u.user.name);
 693        if (IS_ERR(target) || !target) {
 694                duprintf("check_entry: `%s' not found\n", t->u.user.name);
 695                ret = target ? PTR_ERR(target) : -ENOENT;
 696                goto cleanup_matches;
 697        }
 698        t->u.kernel.target = target;
 699
 700        ret = xt_check_target(target, AF_INET6, t->u.target_size - sizeof(*t),
 701                              name, e->comefrom, e->ipv6.proto,
 702                              e->ipv6.invflags & IP6T_INV_PROTO);
 703        if (ret)
 704                goto err;
 705
 706        if (t->u.kernel.target->checkentry
 707                   && !t->u.kernel.target->checkentry(name, e, target, t->data,
 708                                                      e->comefrom)) {
 709                duprintf("ip_tables: check failed for `%s'.\n",
 710                         t->u.kernel.target->name);
 711                ret = -EINVAL;
 712                goto err;
 713        }
 714
 715        (*i)++;
 716        return 0;
 717 err:
 718        module_put(t->u.kernel.target->me);
 719 cleanup_matches:
 720        IP6T_MATCH_ITERATE(e, cleanup_match, &j);
 721        return ret;
 722}
 723
 724static inline int
 725check_entry_size_and_hooks(struct ip6t_entry *e,
 726                           struct xt_table_info *newinfo,
 727                           unsigned char *base,
 728                           unsigned char *limit,
 729                           const unsigned int *hook_entries,
 730                           const unsigned int *underflows,
 731                           unsigned int *i)
 732{
 733        unsigned int h;
 734
 735        if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0
 736            || (unsigned char *)e + sizeof(struct ip6t_entry) >= limit) {
 737                duprintf("Bad offset %p\n", e);
 738                return -EINVAL;
 739        }
 740
 741        if (e->next_offset
 742            < sizeof(struct ip6t_entry) + sizeof(struct ip6t_entry_target)) {
 743                duprintf("checking: element %p size %u\n",
 744                         e, e->next_offset);
 745                return -EINVAL;
 746        }
 747
 748        /* Check hooks & underflows */
 749        for (h = 0; h < NF_IP6_NUMHOOKS; h++) {
 750                if ((unsigned char *)e - base == hook_entries[h])
 751                        newinfo->hook_entry[h] = hook_entries[h];
 752                if ((unsigned char *)e - base == underflows[h])
 753                        newinfo->underflow[h] = underflows[h];
 754        }
 755
 756        /* FIXME: underflows must be unconditional, standard verdicts
 757           < 0 (not IP6T_RETURN). --RR */
 758
 759        /* Clear counters and comefrom */
 760        e->counters = ((struct xt_counters) { 0, 0 });
 761        e->comefrom = 0;
 762
 763        (*i)++;
 764        return 0;
 765}
 766
 767static inline int
 768cleanup_entry(struct ip6t_entry *e, unsigned int *i)
 769{
 770        struct ip6t_entry_target *t;
 771
 772        if (i && (*i)-- == 0)
 773                return 1;
 774
 775        /* Cleanup all matches */
 776        IP6T_MATCH_ITERATE(e, cleanup_match, NULL);
 777        t = ip6t_get_target(e);
 778        if (t->u.kernel.target->destroy)
 779                t->u.kernel.target->destroy(t->u.kernel.target, t->data);
 780        module_put(t->u.kernel.target->me);
 781        return 0;
 782}
 783
 784/* Checks and translates the user-supplied table segment (held in
 785   newinfo) */
 786static int
 787translate_table(const char *name,
 788                unsigned int valid_hooks,
 789                struct xt_table_info *newinfo,
 790                void *entry0,
 791                unsigned int size,
 792                unsigned int number,
 793                const unsigned int *hook_entries,
 794                const unsigned int *underflows)
 795{
 796        unsigned int i;
 797        int ret;
 798
 799        newinfo->size = size;
 800        newinfo->number = number;
 801
 802        /* Init all hooks to impossible value. */
 803        for (i = 0; i < NF_IP6_NUMHOOKS; i++) {
 804                newinfo->hook_entry[i] = 0xFFFFFFFF;
 805                newinfo->underflow[i] = 0xFFFFFFFF;
 806        }
 807
 808        duprintf("translate_table: size %u\n", newinfo->size);
 809        i = 0;
 810        /* Walk through entries, checking offsets. */
 811        ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
 812                                check_entry_size_and_hooks,
 813                                newinfo,
 814                                entry0,
 815                                entry0 + size,
 816                                hook_entries, underflows, &i);
 817        if (ret != 0)
 818                return ret;
 819
 820        if (i != number) {
 821                duprintf("translate_table: %u not %u entries\n",
 822                         i, number);
 823                return -EINVAL;
 824        }
 825
 826        /* Check hooks all assigned */
 827        for (i = 0; i < NF_IP6_NUMHOOKS; i++) {
 828                /* Only hooks which are valid */
 829                if (!(valid_hooks & (1 << i)))
 830                        continue;
 831                if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
 832                        duprintf("Invalid hook entry %u %u\n",
 833                                 i, hook_entries[i]);
 834                        return -EINVAL;
 835                }
 836                if (newinfo->underflow[i] == 0xFFFFFFFF) {
 837                        duprintf("Invalid underflow %u %u\n",
 838                                 i, underflows[i]);
 839                        return -EINVAL;
 840                }
 841        }
 842
 843        if (!mark_source_chains(newinfo, valid_hooks, entry0))
 844                return -ELOOP;
 845
 846        /* Finally, each sanity check must pass */
 847        i = 0;
 848        ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
 849                                check_entry, name, size, &i);
 850
 851        if (ret != 0) {
 852                IP6T_ENTRY_ITERATE(entry0, newinfo->size,
 853                                   cleanup_entry, &i);
 854                return ret;
 855        }
 856
 857        /* And one copy for every other CPU */
 858        for_each_possible_cpu(i) {
 859                if (newinfo->entries[i] && newinfo->entries[i] != entry0)
 860                        memcpy(newinfo->entries[i], entry0, newinfo->size);
 861        }
 862
 863        return 0;
 864}
 865
 866/* Gets counters. */
 867static inline int
 868add_entry_to_counter(const struct ip6t_entry *e,
 869                     struct xt_counters total[],
 870                     unsigned int *i)
 871{
 872        ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
 873
 874        (*i)++;
 875        return 0;
 876}
 877
 878static inline int
 879set_entry_to_counter(const struct ip6t_entry *e,
 880                     struct ip6t_counters total[],
 881                     unsigned int *i)
 882{
 883        SET_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
 884
 885        (*i)++;
 886        return 0;
 887}
 888
 889static void
 890get_counters(const struct xt_table_info *t,
 891             struct xt_counters counters[])
 892{
 893        unsigned int cpu;
 894        unsigned int i;
 895        unsigned int curcpu;
 896
 897        /* Instead of clearing (by a previous call to memset())
 898         * the counters and using adds, we set the counters
 899         * with data used by 'current' CPU
 900         * We dont care about preemption here.
 901         */
 902        curcpu = raw_smp_processor_id();
 903
 904        i = 0;
 905        IP6T_ENTRY_ITERATE(t->entries[curcpu],
 906                           t->size,
 907                           set_entry_to_counter,
 908                           counters,
 909                           &i);
 910
 911        for_each_possible_cpu(cpu) {
 912                if (cpu == curcpu)
 913                        continue;
 914                i = 0;
 915                IP6T_ENTRY_ITERATE(t->entries[cpu],
 916                                  t->size,
 917                                  add_entry_to_counter,
 918                                  counters,
 919                                  &i);
 920        }
 921}
 922
 923static int
 924copy_entries_to_user(unsigned int total_size,
 925                     struct xt_table *table,
 926                     void __user *userptr)
 927{
 928        unsigned int off, num, countersize;
 929        struct ip6t_entry *e;
 930        struct xt_counters *counters;
 931        struct xt_table_info *private = table->private;
 932        int ret = 0;
 933        void *loc_cpu_entry;
 934
 935        /* We need atomic snapshot of counters: rest doesn't change
 936           (other than comefrom, which userspace doesn't care
 937           about). */
 938        countersize = sizeof(struct xt_counters) * private->number;
 939        counters = vmalloc(countersize);
 940
 941        if (counters == NULL)
 942                return -ENOMEM;
 943
 944        /* First, sum counters... */
 945        write_lock_bh(&table->lock);
 946        get_counters(private, counters);
 947        write_unlock_bh(&table->lock);
 948
 949        /* choose the copy that is on ourc node/cpu */
 950        loc_cpu_entry = private->entries[raw_smp_processor_id()];
 951        if (copy_to_user(userptr, loc_cpu_entry, total_size) != 0) {
 952                ret = -EFAULT;
 953                goto free_counters;
 954        }
 955
 956        /* FIXME: use iterator macros --RR */
 957        /* ... then go back and fix counters and names */
 958        for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
 959                unsigned int i;
 960                struct ip6t_entry_match *m;
 961                struct ip6t_entry_target *t;
 962
 963                e = (struct ip6t_entry *)(loc_cpu_entry + off);
 964                if (copy_to_user(userptr + off
 965                                 + offsetof(struct ip6t_entry, counters),
 966                                 &counters[num],
 967                                 sizeof(counters[num])) != 0) {
 968                        ret = -EFAULT;
 969                        goto free_counters;
 970                }
 971
 972                for (i = sizeof(struct ip6t_entry);
 973                     i < e->target_offset;
 974                     i += m->u.match_size) {
 975                        m = (void *)e + i;
 976
 977                        if (copy_to_user(userptr + off + i
 978                                         + offsetof(struct ip6t_entry_match,
 979                                                    u.user.name),
 980                                         m->u.kernel.match->name,
 981                                         strlen(m->u.kernel.match->name)+1)
 982                            != 0) {
 983                                ret = -EFAULT;
 984                                goto free_counters;
 985                        }
 986                }
 987
 988                t = ip6t_get_target(e);
 989                if (copy_to_user(userptr + off + e->target_offset
 990                                 + offsetof(struct ip6t_entry_target,
 991                                            u.user.name),
 992                                 t->u.kernel.target->name,
 993                                 strlen(t->u.kernel.target->name)+1) != 0) {
 994                        ret = -EFAULT;
 995                        goto free_counters;
 996                }
 997        }
 998
 999 free_counters:
1000        vfree(counters);
1001        return ret;
1002}
1003
1004static int
1005get_entries(const struct ip6t_get_entries *entries,
1006            struct ip6t_get_entries __user *uptr)
1007{
1008        int ret;
1009        struct xt_table *t;
1010
1011        t = xt_find_table_lock(AF_INET6, entries->name);
1012        if (t && !IS_ERR(t)) {
1013                struct xt_table_info *private = t->private;
1014                duprintf("t->private->number = %u\n", private->number);
1015                if (entries->size == private->size)
1016                        ret = copy_entries_to_user(private->size,
1017                                                   t, uptr->entrytable);
1018                else {
1019                        duprintf("get_entries: I've got %u not %u!\n",
1020                                 private->size, entries->size);
1021                        ret = -EINVAL;
1022                }
1023                module_put(t->me);
1024                xt_table_unlock(t);
1025        } else
1026                ret = t ? PTR_ERR(t) : -ENOENT;
1027
1028        return ret;
1029}
1030
1031static int
1032do_replace(void __user *user, unsigned int len)
1033{
1034        int ret;
1035        struct ip6t_replace tmp;
1036        struct xt_table *t;
1037        struct xt_table_info *newinfo, *oldinfo;
1038        struct xt_counters *counters;
1039        void *loc_cpu_entry, *loc_cpu_old_entry;
1040
1041        if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1042                return -EFAULT;
1043
1044        /* overflow check */
1045        if (tmp.size >= (INT_MAX - sizeof(struct xt_table_info)) / NR_CPUS -
1046                        SMP_CACHE_BYTES)
1047                return -ENOMEM;
1048        if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
1049                return -ENOMEM;
1050
1051        newinfo = xt_alloc_table_info(tmp.size);
1052        if (!newinfo)
1053                return -ENOMEM;
1054
1055        /* choose the copy that is on our node/cpu */
1056        loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1057        if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
1058                           tmp.size) != 0) {
1059                ret = -EFAULT;
1060                goto free_newinfo;
1061        }
1062
1063        counters = vmalloc(tmp.num_counters * sizeof(struct xt_counters));
1064        if (!counters) {
1065                ret = -ENOMEM;
1066                goto free_newinfo;
1067        }
1068
1069        ret = translate_table(tmp.name, tmp.valid_hooks,
1070                              newinfo, loc_cpu_entry, tmp.size, tmp.num_entries,
1071                              tmp.hook_entry, tmp.underflow);
1072        if (ret != 0)
1073                goto free_newinfo_counters;
1074
1075        duprintf("ip_tables: Translated table\n");
1076
1077        t = try_then_request_module(xt_find_table_lock(AF_INET6, tmp.name),
1078                                    "ip6table_%s", tmp.name);
1079        if (!t || IS_ERR(t)) {
1080                ret = t ? PTR_ERR(t) : -ENOENT;
1081                goto free_newinfo_counters_untrans;
1082        }
1083
1084        /* You lied! */
1085        if (tmp.valid_hooks != t->valid_hooks) {
1086                duprintf("Valid hook crap: %08X vs %08X\n",
1087                         tmp.valid_hooks, t->valid_hooks);
1088                ret = -EINVAL;
1089                goto put_module;
1090        }
1091
1092        oldinfo = xt_replace_table(t, tmp.num_counters, newinfo, &ret);
1093        if (!oldinfo)
1094                goto put_module;
1095
1096        /* Update module usage count based on number of rules */
1097        duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
1098                oldinfo->number, oldinfo->initial_entries, newinfo->number);
1099        if ((oldinfo->number > oldinfo->initial_entries) ||
1100            (newinfo->number <= oldinfo->initial_entries))
1101                module_put(t->me);
1102        if ((oldinfo->number > oldinfo->initial_entries) &&
1103            (newinfo->number <= oldinfo->initial_entries))
1104                module_put(t->me);
1105
1106        /* Get the old counters. */
1107        get_counters(oldinfo, counters);
1108        /* Decrease module usage counts and free resource */
1109        loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
1110        IP6T_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,NULL);
1111        xt_free_table_info(oldinfo);
1112        if (copy_to_user(tmp.counters, counters,
1113                         sizeof(struct xt_counters) * tmp.num_counters) != 0)
1114                ret = -EFAULT;
1115        vfree(counters);
1116        xt_table_unlock(t);
1117        return ret;
1118
1119 put_module:
1120        module_put(t->me);
1121        xt_table_unlock(t);
1122 free_newinfo_counters_untrans:
1123        IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry,NULL);
1124 free_newinfo_counters:
1125        vfree(counters);
1126 free_newinfo:
1127        xt_free_table_info(newinfo);
1128        return ret;
1129}
1130
1131/* We're lazy, and add to the first CPU; overflow works its fey magic
1132 * and everything is OK. */
1133static inline int
1134add_counter_to_entry(struct ip6t_entry *e,
1135                     const struct xt_counters addme[],
1136                     unsigned int *i)
1137{
1138#if 0
1139        duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
1140                 *i,
1141                 (long unsigned int)e->counters.pcnt,
1142                 (long unsigned int)e->counters.bcnt,
1143                 (long unsigned int)addme[*i].pcnt,
1144                 (long unsigned int)addme[*i].bcnt);
1145#endif
1146
1147        ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
1148
1149        (*i)++;
1150        return 0;
1151}
1152
1153static int
1154do_add_counters(void __user *user, unsigned int len)
1155{
1156        unsigned int i;
1157        struct xt_counters_info tmp, *paddc;
1158        struct xt_table_info *private;
1159        struct xt_table *t;
1160        int ret = 0;
1161        void *loc_cpu_entry;
1162
1163        if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1164                return -EFAULT;
1165
1166        if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct xt_counters))
1167                return -EINVAL;
1168
1169        paddc = vmalloc(len);
1170        if (!paddc)
1171                return -ENOMEM;
1172
1173        if (copy_from_user(paddc, user, len) != 0) {
1174                ret = -EFAULT;
1175                goto free;
1176        }
1177
1178        t = xt_find_table_lock(AF_INET6, tmp.name);
1179        if (!t || IS_ERR(t)) {
1180                ret = t ? PTR_ERR(t) : -ENOENT;
1181                goto free;
1182        }
1183
1184        write_lock_bh(&t->lock);
1185        private = t->private;
1186        if (private->number != tmp.num_counters) {
1187                ret = -EINVAL;
1188                goto unlock_up_free;
1189        }
1190
1191        i = 0;
1192        /* Choose the copy that is on our node */
1193        loc_cpu_entry = private->entries[smp_processor_id()];
1194        IP6T_ENTRY_ITERATE(loc_cpu_entry,
1195                          private->size,
1196                          add_counter_to_entry,
1197                          paddc->counters,
1198                          &i);
1199 unlock_up_free:
1200        write_unlock_bh(&t->lock);
1201        xt_table_unlock(t);
1202        module_put(t->me);
1203 free:
1204        vfree(paddc);
1205
1206        return ret;
1207}
1208
1209static int
1210do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
1211{
1212        int ret;
1213
1214        if (!capable(CAP_NET_ADMIN))
1215                return -EPERM;
1216
1217        switch (cmd) {
1218        case IP6T_SO_SET_REPLACE:
1219                ret = do_replace(user, len);
1220                break;
1221
1222        case IP6T_SO_SET_ADD_COUNTERS:
1223                ret = do_add_counters(user, len);
1224                break;
1225
1226        default:
1227                duprintf("do_ip6t_set_ctl:  unknown request %i\n", cmd);
1228                ret = -EINVAL;
1229        }
1230
1231        return ret;
1232}
1233
1234static int
1235do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
1236{
1237        int ret;
1238
1239        if (!capable(CAP_NET_ADMIN))
1240                return -EPERM;
1241
1242        switch (cmd) {
1243        case IP6T_SO_GET_INFO: {
1244                char name[IP6T_TABLE_MAXNAMELEN];
1245                struct xt_table *t;
1246
1247                if (*len != sizeof(struct ip6t_getinfo)) {
1248                        duprintf("length %u != %u\n", *len,
1249                                 sizeof(struct ip6t_getinfo));
1250                        ret = -EINVAL;
1251                        break;
1252                }
1253
1254                if (copy_from_user(name, user, sizeof(name)) != 0) {
1255                        ret = -EFAULT;
1256                        break;
1257                }
1258                name[IP6T_TABLE_MAXNAMELEN-1] = '\0';
1259
1260                t = try_then_request_module(xt_find_table_lock(AF_INET6, name),
1261                                            "ip6table_%s", name);
1262                if (t && !IS_ERR(t)) {
1263                        struct ip6t_getinfo info;
1264                        struct xt_table_info *private = t->private;
1265
1266                        info.valid_hooks = t->valid_hooks;
1267                        memcpy(info.hook_entry, private->hook_entry,
1268                               sizeof(info.hook_entry));
1269                        memcpy(info.underflow, private->underflow,
1270                               sizeof(info.underflow));
1271                        info.num_entries = private->number;
1272                        info.size = private->size;
1273                        memcpy(info.name, name, sizeof(info.name));
1274
1275                        if (copy_to_user(user, &info, *len) != 0)
1276                                ret = -EFAULT;
1277                        else
1278                                ret = 0;
1279                        xt_table_unlock(t);
1280                        module_put(t->me);
1281                } else
1282                        ret = t ? PTR_ERR(t) : -ENOENT;
1283        }
1284        break;
1285
1286        case IP6T_SO_GET_ENTRIES: {
1287                struct ip6t_get_entries get;
1288
1289                if (*len < sizeof(get)) {
1290                        duprintf("get_entries: %u < %u\n", *len, sizeof(get));
1291                        ret = -EINVAL;
1292                } else if (copy_from_user(&get, user, sizeof(get)) != 0) {
1293                        ret = -EFAULT;
1294                } else if (*len != sizeof(struct ip6t_get_entries) + get.size) {
1295                        duprintf("get_entries: %u != %u\n", *len,
1296                                 sizeof(struct ip6t_get_entries) + get.size);
1297                        ret = -EINVAL;
1298                } else
1299                        ret = get_entries(&get, user);
1300                break;
1301        }
1302
1303        case IP6T_SO_GET_REVISION_MATCH:
1304        case IP6T_SO_GET_REVISION_TARGET: {
1305                struct ip6t_get_revision rev;
1306                int target;
1307
1308                if (*len != sizeof(rev)) {
1309                        ret = -EINVAL;
1310                        break;
1311                }
1312                if (copy_from_user(&rev, user, sizeof(rev)) != 0) {
1313                        ret = -EFAULT;
1314                        break;
1315                }
1316
1317                if (cmd == IP6T_SO_GET_REVISION_TARGET)
1318                        target = 1;
1319                else
1320                        target = 0;
1321
1322                try_then_request_module(xt_find_revision(AF_INET6, rev.name,
1323                                                         rev.revision,
1324                                                         target, &ret),
1325                                        "ip6t_%s", rev.name);
1326                break;
1327        }
1328
1329        default:
1330                duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd);
1331                ret = -EINVAL;
1332        }
1333
1334        return ret;
1335}
1336
1337int ip6t_register_table(struct xt_table *table,
1338                        const struct ip6t_replace *repl)
1339{
1340        int ret;
1341        struct xt_table_info *newinfo;
1342        static struct xt_table_info bootstrap
1343                = { 0, 0, 0, { 0 }, { 0 }, { } };
1344        void *loc_cpu_entry;
1345
1346        newinfo = xt_alloc_table_info(repl->size);
1347        if (!newinfo)
1348                return -ENOMEM;
1349
1350        /* choose the copy on our node/cpu */
1351        loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1352        memcpy(loc_cpu_entry, repl->entries, repl->size);
1353
1354        ret = translate_table(table->name, table->valid_hooks,
1355                              newinfo, loc_cpu_entry, repl->size,
1356                              repl->num_entries,
1357                              repl->hook_entry,
1358                              repl->underflow);
1359        if (ret != 0) {
1360                xt_free_table_info(newinfo);
1361                return ret;
1362        }
1363
1364        ret = xt_register_table(table, &bootstrap, newinfo);
1365        if (ret != 0) {
1366                xt_free_table_info(newinfo);
1367                return ret;
1368        }
1369
1370        return 0;
1371}
1372
1373void ip6t_unregister_table(struct xt_table *table)
1374{
1375        struct xt_table_info *private;
1376        void *loc_cpu_entry;
1377
1378        private = xt_unregister_table(table);
1379
1380        /* Decrease module usage counts and free resources */
1381        loc_cpu_entry = private->entries[raw_smp_processor_id()];
1382        IP6T_ENTRY_ITERATE(loc_cpu_entry, private->size, cleanup_entry, NULL);
1383        xt_free_table_info(private);
1384}
1385
1386/* Returns 1 if the type and code is matched by the range, 0 otherwise */
1387static inline bool
1388icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
1389                     u_int8_t type, u_int8_t code,
1390                     bool invert)
1391{
1392        return (type == test_type && code >= min_code && code <= max_code)
1393                ^ invert;
1394}
1395
1396static bool
1397icmp6_match(const struct sk_buff *skb,
1398           const struct net_device *in,
1399           const struct net_device *out,
1400           const struct xt_match *match,
1401           const void *matchinfo,
1402           int offset,
1403           unsigned int protoff,
1404           bool *hotdrop)
1405{
1406        struct icmp6hdr _icmp, *ic;
1407        const struct ip6t_icmp *icmpinfo = matchinfo;
1408
1409        /* Must not be a fragment. */
1410        if (offset)
1411                return false;
1412
1413        ic = skb_header_pointer(skb, protoff, sizeof(_icmp), &_icmp);
1414        if (ic == NULL) {
1415                /* We've been asked to examine this packet, and we
1416                   can't.  Hence, no choice but to drop. */
1417                duprintf("Dropping evil ICMP tinygram.\n");
1418                *hotdrop = true;
1419                return false;
1420        }
1421
1422        return icmp6_type_code_match(icmpinfo->type,
1423                                     icmpinfo->code[0],
1424                                     icmpinfo->code[1],
1425                                     ic->icmp6_type, ic->icmp6_code,
1426                                     !!(icmpinfo->invflags&IP6T_ICMP_INV));
1427}
1428
1429/* Called when user tries to insert an entry of this type. */
1430static bool
1431icmp6_checkentry(const char *tablename,
1432           const void *entry,
1433           const struct xt_match *match,
1434           void *matchinfo,
1435           unsigned int hook_mask)
1436{
1437        const struct ip6t_icmp *icmpinfo = matchinfo;
1438
1439        /* Must specify no unknown invflags */
1440        return !(icmpinfo->invflags & ~IP6T_ICMP_INV);
1441}
1442
1443/* The built-in targets: standard (NULL) and error. */
1444static struct xt_target ip6t_standard_target __read_mostly = {
1445        .name           = IP6T_STANDARD_TARGET,
1446        .targetsize     = sizeof(int),
1447        .family         = AF_INET6,
1448};
1449
1450static struct xt_target ip6t_error_target __read_mostly = {
1451        .name           = IP6T_ERROR_TARGET,
1452        .target         = ip6t_error,
1453        .targetsize     = IP6T_FUNCTION_MAXNAMELEN,
1454        .family         = AF_INET6,
1455};
1456
1457static struct nf_sockopt_ops ip6t_sockopts = {
1458        .pf             = PF_INET6,
1459        .set_optmin     = IP6T_BASE_CTL,
1460        .set_optmax     = IP6T_SO_SET_MAX+1,
1461        .set            = do_ip6t_set_ctl,
1462        .get_optmin     = IP6T_BASE_CTL,
1463        .get_optmax     = IP6T_SO_GET_MAX+1,
1464        .get            = do_ip6t_get_ctl,
1465        .owner          = THIS_MODULE,
1466};
1467
1468static struct xt_match icmp6_matchstruct __read_mostly = {
1469        .name           = "icmp6",
1470        .match          = &icmp6_match,
1471        .matchsize      = sizeof(struct ip6t_icmp),
1472        .checkentry     = icmp6_checkentry,
1473        .proto          = IPPROTO_ICMPV6,
1474        .family         = AF_INET6,
1475};
1476
1477static int __init ip6_tables_init(void)
1478{
1479        int ret;
1480
1481        ret = xt_proto_init(AF_INET6);
1482        if (ret < 0)
1483                goto err1;
1484
1485        /* Noone else will be downing sem now, so we won't sleep */
1486        ret = xt_register_target(&ip6t_standard_target);
1487        if (ret < 0)
1488                goto err2;
1489        ret = xt_register_target(&ip6t_error_target);
1490        if (ret < 0)
1491                goto err3;
1492        ret = xt_register_match(&icmp6_matchstruct);
1493        if (ret < 0)
1494                goto err4;
1495
1496        /* Register setsockopt */
1497        ret = nf_register_sockopt(&ip6t_sockopts);
1498        if (ret < 0)
1499                goto err5;
1500
1501        printk(KERN_INFO "ip6_tables: (C) 2000-2006 Netfilter Core Team\n");
1502        return 0;
1503
1504err5:
1505        xt_unregister_match(&icmp6_matchstruct);
1506err4:
1507        xt_unregister_target(&ip6t_error_target);
1508err3:
1509        xt_unregister_target(&ip6t_standard_target);
1510err2:
1511        xt_proto_fini(AF_INET6);
1512err1:
1513        return ret;
1514}
1515
1516static void __exit ip6_tables_fini(void)
1517{
1518        nf_unregister_sockopt(&ip6t_sockopts);
1519        xt_unregister_match(&icmp6_matchstruct);
1520        xt_unregister_target(&ip6t_error_target);
1521        xt_unregister_target(&ip6t_standard_target);
1522        xt_proto_fini(AF_INET6);
1523}
1524
1525/*
1526 * find the offset to specified header or the protocol number of last header
1527 * if target < 0. "last header" is transport protocol header, ESP, or
1528 * "No next header".
1529 *
1530 * If target header is found, its offset is set in *offset and return protocol
1531 * number. Otherwise, return -1.
1532 *
1533 * If the first fragment doesn't contain the final protocol header or
1534 * NEXTHDR_NONE it is considered invalid.
1535 *
1536 * Note that non-1st fragment is special case that "the protocol number
1537 * of last header" is "next header" field in Fragment header. In this case,
1538 * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
1539 * isn't NULL.
1540 *
1541 */
1542int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
1543                  int target, unsigned short *fragoff)
1544{
1545        unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr);
1546        u8 nexthdr = ipv6_hdr(skb)->nexthdr;
1547        unsigned int len = skb->len - start;
1548
1549        if (fragoff)
1550                *fragoff = 0;
1551
1552        while (nexthdr != target) {
1553                struct ipv6_opt_hdr _hdr, *hp;
1554                unsigned int hdrlen;
1555
1556                if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
1557                        if (target < 0)
1558                                break;
1559                        return -ENOENT;
1560                }
1561
1562                hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
1563                if (hp == NULL)
1564                        return -EBADMSG;
1565                if (nexthdr == NEXTHDR_FRAGMENT) {
1566                        unsigned short _frag_off;
1567                        __be16 *fp;
1568                        fp = skb_header_pointer(skb,
1569                                                start+offsetof(struct frag_hdr,
1570                                                               frag_off),
1571                                                sizeof(_frag_off),
1572                                                &_frag_off);
1573                        if (fp == NULL)
1574                                return -EBADMSG;
1575
1576                        _frag_off = ntohs(*fp) & ~0x7;
1577                        if (_frag_off) {
1578                                if (target < 0 &&
1579                                    ((!ipv6_ext_hdr(hp->nexthdr)) ||
1580                                     hp->nexthdr == NEXTHDR_NONE)) {
1581                                        if (fragoff)
1582                                                *fragoff = _frag_off;
1583                                        return hp->nexthdr;
1584                                }
1585                                return -ENOENT;
1586                        }
1587                        hdrlen = 8;
1588                } else if (nexthdr == NEXTHDR_AUTH)
1589                        hdrlen = (hp->hdrlen + 2) << 2;
1590                else
1591                        hdrlen = ipv6_optlen(hp);
1592
1593                nexthdr = hp->nexthdr;
1594                len -= hdrlen;
1595                start += hdrlen;
1596        }
1597
1598        *offset = start;
1599        return nexthdr;
1600}
1601
1602EXPORT_SYMBOL(ip6t_register_table);
1603EXPORT_SYMBOL(ip6t_unregister_table);
1604EXPORT_SYMBOL(ip6t_do_table);
1605EXPORT_SYMBOL(ip6t_ext_hdr);
1606EXPORT_SYMBOL(ipv6_find_hdr);
1607
1608module_init(ip6_tables_init);
1609module_exit(ip6_tables_fini);
1610