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#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  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 <net/compat.h>
  23#include <asm/uaccess.h>
  24#include <linux/mutex.h>
  25#include <linux/proc_fs.h>
  26#include <linux/err.h>
  27#include <linux/cpumask.h>
  28
  29#include <linux/netfilter_ipv6/ip6_tables.h>
  30#include <linux/netfilter/x_tables.h>
  31#include <net/netfilter/nf_log.h>
  32#include "../../netfilter/xt_repldata.h"
  33
  34MODULE_LICENSE("GPL");
  35MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
  36MODULE_DESCRIPTION("IPv6 packet filter");
  37
  38/*#define DEBUG_IP_FIREWALL*/
  39/*#define DEBUG_ALLOW_ALL*/ /* Useful for remote debugging */
  40/*#define DEBUG_IP_FIREWALL_USER*/
  41
  42#ifdef DEBUG_IP_FIREWALL
  43#define dprintf(format, args...) pr_info(format , ## args)
  44#else
  45#define dprintf(format, args...)
  46#endif
  47
  48#ifdef DEBUG_IP_FIREWALL_USER
  49#define duprintf(format, args...) pr_info(format , ## args)
  50#else
  51#define duprintf(format, args...)
  52#endif
  53
  54#ifdef CONFIG_NETFILTER_DEBUG
  55#define IP_NF_ASSERT(x) WARN_ON(!(x))
  56#else
  57#define IP_NF_ASSERT(x)
  58#endif
  59
  60#if 0
  61/* All the better to debug you with... */
  62#define static
  63#define inline
  64#endif
  65
  66void *ip6t_alloc_initial_table(const struct xt_table *info)
  67{
  68        return xt_alloc_initial_table(ip6t, IP6T);
  69}
  70EXPORT_SYMBOL_GPL(ip6t_alloc_initial_table);
  71
  72/*
  73   We keep a set of rules for each CPU, so we can avoid write-locking
  74   them in the softirq when updating the counters and therefore
  75   only need to read-lock in the softirq; doing a write_lock_bh() in user
  76   context stops packets coming through and allows user context to read
  77   the counters or update the rules.
  78
  79   Hence the start of any table is given by get_table() below.  */
  80
  81/* Check for an extension */
  82int
  83ip6t_ext_hdr(u8 nexthdr)
  84{
  85        return  (nexthdr == IPPROTO_HOPOPTS)   ||
  86                (nexthdr == IPPROTO_ROUTING)   ||
  87                (nexthdr == IPPROTO_FRAGMENT)  ||
  88                (nexthdr == IPPROTO_ESP)       ||
  89                (nexthdr == IPPROTO_AH)        ||
  90                (nexthdr == IPPROTO_NONE)      ||
  91                (nexthdr == IPPROTO_DSTOPTS);
  92}
  93
  94/* Returns whether matches rule or not. */
  95/* Performance critical - called for every packet */
  96static inline bool
  97ip6_packet_match(const struct sk_buff *skb,
  98                 const char *indev,
  99                 const char *outdev,
 100                 const struct ip6t_ip6 *ip6info,
 101                 unsigned int *protoff,
 102                 int *fragoff, bool *hotdrop)
 103{
 104        unsigned long ret;
 105        const struct ipv6hdr *ipv6 = ipv6_hdr(skb);
 106
 107#define FWINV(bool, invflg) ((bool) ^ !!(ip6info->invflags & (invflg)))
 108
 109        if (FWINV(ipv6_masked_addr_cmp(&ipv6->saddr, &ip6info->smsk,
 110                                       &ip6info->src), IP6T_INV_SRCIP) ||
 111            FWINV(ipv6_masked_addr_cmp(&ipv6->daddr, &ip6info->dmsk,
 112                                       &ip6info->dst), IP6T_INV_DSTIP)) {
 113                dprintf("Source or dest mismatch.\n");
 114/*
 115                dprintf("SRC: %u. Mask: %u. Target: %u.%s\n", ip->saddr,
 116                        ipinfo->smsk.s_addr, ipinfo->src.s_addr,
 117                        ipinfo->invflags & IP6T_INV_SRCIP ? " (INV)" : "");
 118                dprintf("DST: %u. Mask: %u. Target: %u.%s\n", ip->daddr,
 119                        ipinfo->dmsk.s_addr, ipinfo->dst.s_addr,
 120                        ipinfo->invflags & IP6T_INV_DSTIP ? " (INV)" : "");*/
 121                return false;
 122        }
 123
 124        ret = ifname_compare_aligned(indev, ip6info->iniface, ip6info->iniface_mask);
 125
 126        if (FWINV(ret != 0, IP6T_INV_VIA_IN)) {
 127                dprintf("VIA in mismatch (%s vs %s).%s\n",
 128                        indev, ip6info->iniface,
 129                        ip6info->invflags&IP6T_INV_VIA_IN ?" (INV)":"");
 130                return false;
 131        }
 132
 133        ret = ifname_compare_aligned(outdev, ip6info->outiface, ip6info->outiface_mask);
 134
 135        if (FWINV(ret != 0, IP6T_INV_VIA_OUT)) {
 136                dprintf("VIA out mismatch (%s vs %s).%s\n",
 137                        outdev, ip6info->outiface,
 138                        ip6info->invflags&IP6T_INV_VIA_OUT ?" (INV)":"");
 139                return false;
 140        }
 141
 142/* ... might want to do something with class and flowlabel here ... */
 143
 144        /* look for the desired protocol header */
 145        if((ip6info->flags & IP6T_F_PROTO)) {
 146                int protohdr;
 147                unsigned short _frag_off;
 148
 149                protohdr = ipv6_find_hdr(skb, protoff, -1, &_frag_off);
 150                if (protohdr < 0) {
 151                        if (_frag_off == 0)
 152                                *hotdrop = true;
 153                        return false;
 154                }
 155                *fragoff = _frag_off;
 156
 157                dprintf("Packet protocol %hi ?= %s%hi.\n",
 158                                protohdr,
 159                                ip6info->invflags & IP6T_INV_PROTO ? "!":"",
 160                                ip6info->proto);
 161
 162                if (ip6info->proto == protohdr) {
 163                        if(ip6info->invflags & IP6T_INV_PROTO) {
 164                                return false;
 165                        }
 166                        return true;
 167                }
 168
 169                /* We need match for the '-p all', too! */
 170                if ((ip6info->proto != 0) &&
 171                        !(ip6info->invflags & IP6T_INV_PROTO))
 172                        return false;
 173        }
 174        return true;
 175}
 176
 177/* should be ip6 safe */
 178static bool
 179ip6_checkentry(const struct ip6t_ip6 *ipv6)
 180{
 181        if (ipv6->flags & ~IP6T_F_MASK) {
 182                duprintf("Unknown flag bits set: %08X\n",
 183                         ipv6->flags & ~IP6T_F_MASK);
 184                return false;
 185        }
 186        if (ipv6->invflags & ~IP6T_INV_MASK) {
 187                duprintf("Unknown invflag bits set: %08X\n",
 188                         ipv6->invflags & ~IP6T_INV_MASK);
 189                return false;
 190        }
 191        return true;
 192}
 193
 194static unsigned int
 195ip6t_error(struct sk_buff *skb, const struct xt_action_param *par)
 196{
 197        if (net_ratelimit())
 198                pr_info("error: `%s'\n", (const char *)par->targinfo);
 199
 200        return NF_DROP;
 201}
 202
 203static inline struct ip6t_entry *
 204get_entry(const void *base, unsigned int offset)
 205{
 206        return (struct ip6t_entry *)(base + offset);
 207}
 208
 209/* All zeroes == unconditional rule. */
 210/* Mildly perf critical (only if packet tracing is on) */
 211static inline bool unconditional(const struct ip6t_ip6 *ipv6)
 212{
 213        static const struct ip6t_ip6 uncond;
 214
 215        return memcmp(ipv6, &uncond, sizeof(uncond)) == 0;
 216}
 217
 218static inline const struct xt_entry_target *
 219ip6t_get_target_c(const struct ip6t_entry *e)
 220{
 221        return ip6t_get_target((struct ip6t_entry *)e);
 222}
 223
 224#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
 225    defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
 226/* This cries for unification! */
 227static const char *const hooknames[] = {
 228        [NF_INET_PRE_ROUTING]           = "PREROUTING",
 229        [NF_INET_LOCAL_IN]              = "INPUT",
 230        [NF_INET_FORWARD]               = "FORWARD",
 231        [NF_INET_LOCAL_OUT]             = "OUTPUT",
 232        [NF_INET_POST_ROUTING]          = "POSTROUTING",
 233};
 234
 235enum nf_ip_trace_comments {
 236        NF_IP6_TRACE_COMMENT_RULE,
 237        NF_IP6_TRACE_COMMENT_RETURN,
 238        NF_IP6_TRACE_COMMENT_POLICY,
 239};
 240
 241static const char *const comments[] = {
 242        [NF_IP6_TRACE_COMMENT_RULE]     = "rule",
 243        [NF_IP6_TRACE_COMMENT_RETURN]   = "return",
 244        [NF_IP6_TRACE_COMMENT_POLICY]   = "policy",
 245};
 246
 247static struct nf_loginfo trace_loginfo = {
 248        .type = NF_LOG_TYPE_LOG,
 249        .u = {
 250                .log = {
 251                        .level = 4,
 252                        .logflags = NF_LOG_MASK,
 253                },
 254        },
 255};
 256
 257/* Mildly perf critical (only if packet tracing is on) */
 258static inline int
 259get_chainname_rulenum(const struct ip6t_entry *s, const struct ip6t_entry *e,
 260                      const char *hookname, const char **chainname,
 261                      const char **comment, unsigned int *rulenum)
 262{
 263        const struct xt_standard_target *t = (void *)ip6t_get_target_c(s);
 264
 265        if (strcmp(t->target.u.kernel.target->name, XT_ERROR_TARGET) == 0) {
 266                /* Head of user chain: ERROR target with chainname */
 267                *chainname = t->target.data;
 268                (*rulenum) = 0;
 269        } else if (s == e) {
 270                (*rulenum)++;
 271
 272                if (s->target_offset == sizeof(struct ip6t_entry) &&
 273                    strcmp(t->target.u.kernel.target->name,
 274                           XT_STANDARD_TARGET) == 0 &&
 275                    t->verdict < 0 &&
 276                    unconditional(&s->ipv6)) {
 277                        /* Tail of chains: STANDARD target (return/policy) */
 278                        *comment = *chainname == hookname
 279                                ? comments[NF_IP6_TRACE_COMMENT_POLICY]
 280                                : comments[NF_IP6_TRACE_COMMENT_RETURN];
 281                }
 282                return 1;
 283        } else
 284                (*rulenum)++;
 285
 286        return 0;
 287}
 288
 289static void trace_packet(const struct sk_buff *skb,
 290                         unsigned int hook,
 291                         const struct net_device *in,
 292                         const struct net_device *out,
 293                         const char *tablename,
 294                         const struct xt_table_info *private,
 295                         const struct ip6t_entry *e)
 296{
 297        const void *table_base;
 298        const struct ip6t_entry *root;
 299        const char *hookname, *chainname, *comment;
 300        const struct ip6t_entry *iter;
 301        unsigned int rulenum = 0;
 302
 303        table_base = private->entries[smp_processor_id()];
 304        root = get_entry(table_base, private->hook_entry[hook]);
 305
 306        hookname = chainname = hooknames[hook];
 307        comment = comments[NF_IP6_TRACE_COMMENT_RULE];
 308
 309        xt_entry_foreach(iter, root, private->size - private->hook_entry[hook])
 310                if (get_chainname_rulenum(iter, e, hookname,
 311                    &chainname, &comment, &rulenum) != 0)
 312                        break;
 313
 314        nf_log_packet(AF_INET6, hook, skb, in, out, &trace_loginfo,
 315                      "TRACE: %s:%s:%s:%u ",
 316                      tablename, chainname, comment, rulenum);
 317}
 318#endif
 319
 320static inline __pure struct ip6t_entry *
 321ip6t_next_entry(const struct ip6t_entry *entry)
 322{
 323        return (void *)entry + entry->next_offset;
 324}
 325
 326/* Returns one of the generic firewall policies, like NF_ACCEPT. */
 327unsigned int
 328ip6t_do_table(struct sk_buff *skb,
 329              unsigned int hook,
 330              const struct net_device *in,
 331              const struct net_device *out,
 332              struct xt_table *table)
 333{
 334        static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
 335        /* Initializing verdict to NF_DROP keeps gcc happy. */
 336        unsigned int verdict = NF_DROP;
 337        const char *indev, *outdev;
 338        const void *table_base;
 339        struct ip6t_entry *e, **jumpstack;
 340        unsigned int *stackptr, origptr, cpu;
 341        const struct xt_table_info *private;
 342        struct xt_action_param acpar;
 343
 344        /* Initialization */
 345        indev = in ? in->name : nulldevname;
 346        outdev = out ? out->name : nulldevname;
 347        /* We handle fragments by dealing with the first fragment as
 348         * if it was a normal packet.  All other fragments are treated
 349         * normally, except that they will NEVER match rules that ask
 350         * things we don't know, ie. tcp syn flag or ports).  If the
 351         * rule is also a fragment-specific rule, non-fragments won't
 352         * match it. */
 353        acpar.hotdrop = false;
 354        acpar.in      = in;
 355        acpar.out     = out;
 356        acpar.family  = NFPROTO_IPV6;
 357        acpar.hooknum = hook;
 358
 359        IP_NF_ASSERT(table->valid_hooks & (1 << hook));
 360
 361        xt_info_rdlock_bh();
 362        private = table->private;
 363        cpu        = smp_processor_id();
 364        table_base = private->entries[cpu];
 365        jumpstack  = (struct ip6t_entry **)private->jumpstack[cpu];
 366        stackptr   = per_cpu_ptr(private->stackptr, cpu);
 367        origptr    = *stackptr;
 368
 369        e = get_entry(table_base, private->hook_entry[hook]);
 370
 371        do {
 372                const struct xt_entry_target *t;
 373                const struct xt_entry_match *ematch;
 374
 375                IP_NF_ASSERT(e);
 376                if (!ip6_packet_match(skb, indev, outdev, &e->ipv6,
 377                    &acpar.thoff, &acpar.fragoff, &acpar.hotdrop)) {
 378 no_match:
 379                        e = ip6t_next_entry(e);
 380                        continue;
 381                }
 382
 383                xt_ematch_foreach(ematch, e) {
 384                        acpar.match     = ematch->u.kernel.match;
 385                        acpar.matchinfo = ematch->data;
 386                        if (!acpar.match->match(skb, &acpar))
 387                                goto no_match;
 388                }
 389
 390                ADD_COUNTER(e->counters, skb->len, 1);
 391
 392                t = ip6t_get_target_c(e);
 393                IP_NF_ASSERT(t->u.kernel.target);
 394
 395#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
 396    defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
 397                /* The packet is traced: log it */
 398                if (unlikely(skb->nf_trace))
 399                        trace_packet(skb, hook, in, out,
 400                                     table->name, private, e);
 401#endif
 402                /* Standard target? */
 403                if (!t->u.kernel.target->target) {
 404                        int v;
 405
 406                        v = ((struct xt_standard_target *)t)->verdict;
 407                        if (v < 0) {
 408                                /* Pop from stack? */
 409                                if (v != XT_RETURN) {
 410                                        verdict = (unsigned)(-v) - 1;
 411                                        break;
 412                                }
 413                                if (*stackptr == 0)
 414                                        e = get_entry(table_base,
 415                                            private->underflow[hook]);
 416                                else
 417                                        e = ip6t_next_entry(jumpstack[--*stackptr]);
 418                                continue;
 419                        }
 420                        if (table_base + v != ip6t_next_entry(e) &&
 421                            !(e->ipv6.flags & IP6T_F_GOTO)) {
 422                                if (*stackptr >= private->stacksize) {
 423                                        verdict = NF_DROP;
 424                                        break;
 425                                }
 426                                jumpstack[(*stackptr)++] = e;
 427                        }
 428
 429                        e = get_entry(table_base, v);
 430                        continue;
 431                }
 432
 433                acpar.target   = t->u.kernel.target;
 434                acpar.targinfo = t->data;
 435
 436                verdict = t->u.kernel.target->target(skb, &acpar);
 437                if (verdict == XT_CONTINUE)
 438                        e = ip6t_next_entry(e);
 439                else
 440                        /* Verdict */
 441                        break;
 442        } while (!acpar.hotdrop);
 443
 444        xt_info_rdunlock_bh();
 445        *stackptr = origptr;
 446
 447#ifdef DEBUG_ALLOW_ALL
 448        return NF_ACCEPT;
 449#else
 450        if (acpar.hotdrop)
 451                return NF_DROP;
 452        else return verdict;
 453#endif
 454}
 455
 456/* Figures out from what hook each rule can be called: returns 0 if
 457   there are loops.  Puts hook bitmask in comefrom. */
 458static int
 459mark_source_chains(const struct xt_table_info *newinfo,
 460                   unsigned int valid_hooks, void *entry0)
 461{
 462        unsigned int hook;
 463
 464        /* No recursion; use packet counter to save back ptrs (reset
 465           to 0 as we leave), and comefrom to save source hook bitmask */
 466        for (hook = 0; hook < NF_INET_NUMHOOKS; hook++) {
 467                unsigned int pos = newinfo->hook_entry[hook];
 468                struct ip6t_entry *e = (struct ip6t_entry *)(entry0 + pos);
 469
 470                if (!(valid_hooks & (1 << hook)))
 471                        continue;
 472
 473                /* Set initial back pointer. */
 474                e->counters.pcnt = pos;
 475
 476                for (;;) {
 477                        const struct xt_standard_target *t
 478                                = (void *)ip6t_get_target_c(e);
 479                        int visited = e->comefrom & (1 << hook);
 480
 481                        if (e->comefrom & (1 << NF_INET_NUMHOOKS)) {
 482                                pr_err("iptables: loop hook %u pos %u %08X.\n",
 483                                       hook, pos, e->comefrom);
 484                                return 0;
 485                        }
 486                        e->comefrom |= ((1 << hook) | (1 << NF_INET_NUMHOOKS));
 487
 488                        /* Unconditional return/END. */
 489                        if ((e->target_offset == sizeof(struct ip6t_entry) &&
 490                             (strcmp(t->target.u.user.name,
 491                                     XT_STANDARD_TARGET) == 0) &&
 492                             t->verdict < 0 &&
 493                             unconditional(&e->ipv6)) || visited) {
 494                                unsigned int oldpos, size;
 495
 496                                if ((strcmp(t->target.u.user.name,
 497                                            XT_STANDARD_TARGET) == 0) &&
 498                                    t->verdict < -NF_MAX_VERDICT - 1) {
 499                                        duprintf("mark_source_chains: bad "
 500                                                "negative verdict (%i)\n",
 501                                                                t->verdict);
 502                                        return 0;
 503                                }
 504
 505                                /* Return: backtrack through the last
 506                                   big jump. */
 507                                do {
 508                                        e->comefrom ^= (1<<NF_INET_NUMHOOKS);
 509#ifdef DEBUG_IP_FIREWALL_USER
 510                                        if (e->comefrom
 511                                            & (1 << NF_INET_NUMHOOKS)) {
 512                                                duprintf("Back unset "
 513                                                         "on hook %u "
 514                                                         "rule %u\n",
 515                                                         hook, pos);
 516                                        }
 517#endif
 518                                        oldpos = pos;
 519                                        pos = e->counters.pcnt;
 520                                        e->counters.pcnt = 0;
 521
 522                                        /* We're at the start. */
 523                                        if (pos == oldpos)
 524                                                goto next;
 525
 526                                        e = (struct ip6t_entry *)
 527                                                (entry0 + pos);
 528                                } while (oldpos == pos + e->next_offset);
 529
 530                                /* Move along one */
 531                                size = e->next_offset;
 532                                e = (struct ip6t_entry *)
 533                                        (entry0 + pos + size);
 534                                e->counters.pcnt = pos;
 535                                pos += size;
 536                        } else {
 537                                int newpos = t->verdict;
 538
 539                                if (strcmp(t->target.u.user.name,
 540                                           XT_STANDARD_TARGET) == 0 &&
 541                                    newpos >= 0) {
 542                                        if (newpos > newinfo->size -
 543                                                sizeof(struct ip6t_entry)) {
 544                                                duprintf("mark_source_chains: "
 545                                                        "bad verdict (%i)\n",
 546                                                                newpos);
 547                                                return 0;
 548                                        }
 549                                        /* This a jump; chase it. */
 550                                        duprintf("Jump rule %u -> %u\n",
 551                                                 pos, newpos);
 552                                } else {
 553                                        /* ... this is a fallthru */
 554                                        newpos = pos + e->next_offset;
 555                                }
 556                                e = (struct ip6t_entry *)
 557                                        (entry0 + newpos);
 558                                e->counters.pcnt = pos;
 559                                pos = newpos;
 560                        }
 561                }
 562                next:
 563                duprintf("Finished chain %u\n", hook);
 564        }
 565        return 1;
 566}
 567
 568static void cleanup_match(struct xt_entry_match *m, struct net *net)
 569{
 570        struct xt_mtdtor_param par;
 571
 572        par.net       = net;
 573        par.match     = m->u.kernel.match;
 574        par.matchinfo = m->data;
 575        par.family    = NFPROTO_IPV6;
 576        if (par.match->destroy != NULL)
 577                par.match->destroy(&par);
 578        module_put(par.match->me);
 579}
 580
 581static int
 582check_entry(const struct ip6t_entry *e, const char *name)
 583{
 584        const struct xt_entry_target *t;
 585
 586        if (!ip6_checkentry(&e->ipv6)) {
 587                duprintf("ip_tables: ip check failed %p %s.\n", e, name);
 588                return -EINVAL;
 589        }
 590
 591        if (e->target_offset + sizeof(struct xt_entry_target) >
 592            e->next_offset)
 593                return -EINVAL;
 594
 595        t = ip6t_get_target_c(e);
 596        if (e->target_offset + t->u.target_size > e->next_offset)
 597                return -EINVAL;
 598
 599        return 0;
 600}
 601
 602static int check_match(struct xt_entry_match *m, struct xt_mtchk_param *par)
 603{
 604        const struct ip6t_ip6 *ipv6 = par->entryinfo;
 605        int ret;
 606
 607        par->match     = m->u.kernel.match;
 608        par->matchinfo = m->data;
 609
 610        ret = xt_check_match(par, m->u.match_size - sizeof(*m),
 611                             ipv6->proto, ipv6->invflags & IP6T_INV_PROTO);
 612        if (ret < 0) {
 613                duprintf("ip_tables: check failed for `%s'.\n",
 614                         par.match->name);
 615                return ret;
 616        }
 617        return 0;
 618}
 619
 620static int
 621find_check_match(struct xt_entry_match *m, struct xt_mtchk_param *par)
 622{
 623        struct xt_match *match;
 624        int ret;
 625
 626        match = xt_request_find_match(NFPROTO_IPV6, m->u.user.name,
 627                                      m->u.user.revision);
 628        if (IS_ERR(match)) {
 629                duprintf("find_check_match: `%s' not found\n", m->u.user.name);
 630                return PTR_ERR(match);
 631        }
 632        m->u.kernel.match = match;
 633
 634        ret = check_match(m, par);
 635        if (ret)
 636                goto err;
 637
 638        return 0;
 639err:
 640        module_put(m->u.kernel.match->me);
 641        return ret;
 642}
 643
 644static int check_target(struct ip6t_entry *e, struct net *net, const char *name)
 645{
 646        struct xt_entry_target *t = ip6t_get_target(e);
 647        struct xt_tgchk_param par = {
 648                .net       = net,
 649                .table     = name,
 650                .entryinfo = e,
 651                .target    = t->u.kernel.target,
 652                .targinfo  = t->data,
 653                .hook_mask = e->comefrom,
 654                .family    = NFPROTO_IPV6,
 655        };
 656        int ret;
 657
 658        t = ip6t_get_target(e);
 659        ret = xt_check_target(&par, t->u.target_size - sizeof(*t),
 660              e->ipv6.proto, e->ipv6.invflags & IP6T_INV_PROTO);
 661        if (ret < 0) {
 662                duprintf("ip_tables: check failed for `%s'.\n",
 663                         t->u.kernel.target->name);
 664                return ret;
 665        }
 666        return 0;
 667}
 668
 669static int
 670find_check_entry(struct ip6t_entry *e, struct net *net, const char *name,
 671                 unsigned int size)
 672{
 673        struct xt_entry_target *t;
 674        struct xt_target *target;
 675        int ret;
 676        unsigned int j;
 677        struct xt_mtchk_param mtpar;
 678        struct xt_entry_match *ematch;
 679
 680        ret = check_entry(e, name);
 681        if (ret)
 682                return ret;
 683
 684        j = 0;
 685        mtpar.net       = net;
 686        mtpar.table     = name;
 687        mtpar.entryinfo = &e->ipv6;
 688        mtpar.hook_mask = e->comefrom;
 689        mtpar.family    = NFPROTO_IPV6;
 690        xt_ematch_foreach(ematch, e) {
 691                ret = find_check_match(ematch, &mtpar);
 692                if (ret != 0)
 693                        goto cleanup_matches;
 694                ++j;
 695        }
 696
 697        t = ip6t_get_target(e);
 698        target = xt_request_find_target(NFPROTO_IPV6, t->u.user.name,
 699                                        t->u.user.revision);
 700        if (IS_ERR(target)) {
 701                duprintf("find_check_entry: `%s' not found\n", t->u.user.name);
 702                ret = PTR_ERR(target);
 703                goto cleanup_matches;
 704        }
 705        t->u.kernel.target = target;
 706
 707        ret = check_target(e, net, name);
 708        if (ret)
 709                goto err;
 710        return 0;
 711 err:
 712        module_put(t->u.kernel.target->me);
 713 cleanup_matches:
 714        xt_ematch_foreach(ematch, e) {
 715                if (j-- == 0)
 716                        break;
 717                cleanup_match(ematch, net);
 718        }
 719        return ret;
 720}
 721
 722static bool check_underflow(const struct ip6t_entry *e)
 723{
 724        const struct xt_entry_target *t;
 725        unsigned int verdict;
 726
 727        if (!unconditional(&e->ipv6))
 728                return false;
 729        t = ip6t_get_target_c(e);
 730        if (strcmp(t->u.user.name, XT_STANDARD_TARGET) != 0)
 731                return false;
 732        verdict = ((struct xt_standard_target *)t)->verdict;
 733        verdict = -verdict - 1;
 734        return verdict == NF_DROP || verdict == NF_ACCEPT;
 735}
 736
 737static int
 738check_entry_size_and_hooks(struct ip6t_entry *e,
 739                           struct xt_table_info *newinfo,
 740                           const unsigned char *base,
 741                           const unsigned char *limit,
 742                           const unsigned int *hook_entries,
 743                           const unsigned int *underflows,
 744                           unsigned int valid_hooks)
 745{
 746        unsigned int h;
 747
 748        if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0 ||
 749            (unsigned char *)e + sizeof(struct ip6t_entry) >= limit) {
 750                duprintf("Bad offset %p\n", e);
 751                return -EINVAL;
 752        }
 753
 754        if (e->next_offset
 755            < sizeof(struct ip6t_entry) + sizeof(struct xt_entry_target)) {
 756                duprintf("checking: element %p size %u\n",
 757                         e, e->next_offset);
 758                return -EINVAL;
 759        }
 760
 761        /* Check hooks & underflows */
 762        for (h = 0; h < NF_INET_NUMHOOKS; h++) {
 763                if (!(valid_hooks & (1 << h)))
 764                        continue;
 765                if ((unsigned char *)e - base == hook_entries[h])
 766                        newinfo->hook_entry[h] = hook_entries[h];
 767                if ((unsigned char *)e - base == underflows[h]) {
 768                        if (!check_underflow(e)) {
 769                                pr_err("Underflows must be unconditional and "
 770                                       "use the STANDARD target with "
 771                                       "ACCEPT/DROP\n");
 772                                return -EINVAL;
 773                        }
 774                        newinfo->underflow[h] = underflows[h];
 775                }
 776        }
 777
 778        /* Clear counters and comefrom */
 779        e->counters = ((struct xt_counters) { 0, 0 });
 780        e->comefrom = 0;
 781        return 0;
 782}
 783
 784static void cleanup_entry(struct ip6t_entry *e, struct net *net)
 785{
 786        struct xt_tgdtor_param par;
 787        struct xt_entry_target *t;
 788        struct xt_entry_match *ematch;
 789
 790        /* Cleanup all matches */
 791        xt_ematch_foreach(ematch, e)
 792                cleanup_match(ematch, net);
 793        t = ip6t_get_target(e);
 794
 795        par.net      = net;
 796        par.target   = t->u.kernel.target;
 797        par.targinfo = t->data;
 798        par.family   = NFPROTO_IPV6;
 799        if (par.target->destroy != NULL)
 800                par.target->destroy(&par);
 801        module_put(par.target->me);
 802}
 803
 804/* Checks and translates the user-supplied table segment (held in
 805   newinfo) */
 806static int
 807translate_table(struct net *net, struct xt_table_info *newinfo, void *entry0,
 808                const struct ip6t_replace *repl)
 809{
 810        struct ip6t_entry *iter;
 811        unsigned int i;
 812        int ret = 0;
 813
 814        newinfo->size = repl->size;
 815        newinfo->number = repl->num_entries;
 816
 817        /* Init all hooks to impossible value. */
 818        for (i = 0; i < NF_INET_NUMHOOKS; i++) {
 819                newinfo->hook_entry[i] = 0xFFFFFFFF;
 820                newinfo->underflow[i] = 0xFFFFFFFF;
 821        }
 822
 823        duprintf("translate_table: size %u\n", newinfo->size);
 824        i = 0;
 825        /* Walk through entries, checking offsets. */
 826        xt_entry_foreach(iter, entry0, newinfo->size) {
 827                ret = check_entry_size_and_hooks(iter, newinfo, entry0,
 828                                                 entry0 + repl->size,
 829                                                 repl->hook_entry,
 830                                                 repl->underflow,
 831                                                 repl->valid_hooks);
 832                if (ret != 0)
 833                        return ret;
 834                ++i;
 835                if (strcmp(ip6t_get_target(iter)->u.user.name,
 836                    XT_ERROR_TARGET) == 0)
 837                        ++newinfo->stacksize;
 838        }
 839
 840        if (i != repl->num_entries) {
 841                duprintf("translate_table: %u not %u entries\n",
 842                         i, repl->num_entries);
 843                return -EINVAL;
 844        }
 845
 846        /* Check hooks all assigned */
 847        for (i = 0; i < NF_INET_NUMHOOKS; i++) {
 848                /* Only hooks which are valid */
 849                if (!(repl->valid_hooks & (1 << i)))
 850                        continue;
 851                if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
 852                        duprintf("Invalid hook entry %u %u\n",
 853                                 i, repl->hook_entry[i]);
 854                        return -EINVAL;
 855                }
 856                if (newinfo->underflow[i] == 0xFFFFFFFF) {
 857                        duprintf("Invalid underflow %u %u\n",
 858                                 i, repl->underflow[i]);
 859                        return -EINVAL;
 860                }
 861        }
 862
 863        if (!mark_source_chains(newinfo, repl->valid_hooks, entry0))
 864                return -ELOOP;
 865
 866        /* Finally, each sanity check must pass */
 867        i = 0;
 868        xt_entry_foreach(iter, entry0, newinfo->size) {
 869                ret = find_check_entry(iter, net, repl->name, repl->size);
 870                if (ret != 0)
 871                        break;
 872                ++i;
 873        }
 874
 875        if (ret != 0) {
 876                xt_entry_foreach(iter, entry0, newinfo->size) {
 877                        if (i-- == 0)
 878                                break;
 879                        cleanup_entry(iter, net);
 880                }
 881                return ret;
 882        }
 883
 884        /* And one copy for every other CPU */
 885        for_each_possible_cpu(i) {
 886                if (newinfo->entries[i] && newinfo->entries[i] != entry0)
 887                        memcpy(newinfo->entries[i], entry0, newinfo->size);
 888        }
 889
 890        return ret;
 891}
 892
 893static void
 894get_counters(const struct xt_table_info *t,
 895             struct xt_counters counters[])
 896{
 897        struct ip6t_entry *iter;
 898        unsigned int cpu;
 899        unsigned int i;
 900
 901        for_each_possible_cpu(cpu) {
 902                seqlock_t *lock = &per_cpu(xt_info_locks, cpu).lock;
 903
 904                i = 0;
 905                xt_entry_foreach(iter, t->entries[cpu], t->size) {
 906                        u64 bcnt, pcnt;
 907                        unsigned int start;
 908
 909                        do {
 910                                start = read_seqbegin(lock);
 911                                bcnt = iter->counters.bcnt;
 912                                pcnt = iter->counters.pcnt;
 913                        } while (read_seqretry(lock, start));
 914
 915                        ADD_COUNTER(counters[i], bcnt, pcnt);
 916                        ++i;
 917                }
 918        }
 919}
 920
 921static struct xt_counters *alloc_counters(const struct xt_table *table)
 922{
 923        unsigned int countersize;
 924        struct xt_counters *counters;
 925        const struct xt_table_info *private = table->private;
 926
 927        /* We need atomic snapshot of counters: rest doesn't change
 928           (other than comefrom, which userspace doesn't care
 929           about). */
 930        countersize = sizeof(struct xt_counters) * private->number;
 931        counters = vzalloc(countersize);
 932
 933        if (counters == NULL)
 934                return ERR_PTR(-ENOMEM);
 935
 936        get_counters(private, counters);
 937
 938        return counters;
 939}
 940
 941static int
 942copy_entries_to_user(unsigned int total_size,
 943                     const struct xt_table *table,
 944                     void __user *userptr)
 945{
 946        unsigned int off, num;
 947        const struct ip6t_entry *e;
 948        struct xt_counters *counters;
 949        const struct xt_table_info *private = table->private;
 950        int ret = 0;
 951        const void *loc_cpu_entry;
 952
 953        counters = alloc_counters(table);
 954        if (IS_ERR(counters))
 955                return PTR_ERR(counters);
 956
 957        /* choose the copy that is on our node/cpu, ...
 958         * This choice is lazy (because current thread is
 959         * allowed to migrate to another cpu)
 960         */
 961        loc_cpu_entry = private->entries[raw_smp_processor_id()];
 962        if (copy_to_user(userptr, loc_cpu_entry, total_size) != 0) {
 963                ret = -EFAULT;
 964                goto free_counters;
 965        }
 966
 967        /* FIXME: use iterator macros --RR */
 968        /* ... then go back and fix counters and names */
 969        for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
 970                unsigned int i;
 971                const struct xt_entry_match *m;
 972                const struct xt_entry_target *t;
 973
 974                e = (struct ip6t_entry *)(loc_cpu_entry + off);
 975                if (copy_to_user(userptr + off
 976                                 + offsetof(struct ip6t_entry, counters),
 977                                 &counters[num],
 978                                 sizeof(counters[num])) != 0) {
 979                        ret = -EFAULT;
 980                        goto free_counters;
 981                }
 982
 983                for (i = sizeof(struct ip6t_entry);
 984                     i < e->target_offset;
 985                     i += m->u.match_size) {
 986                        m = (void *)e + i;
 987
 988                        if (copy_to_user(userptr + off + i
 989                                         + offsetof(struct xt_entry_match,
 990                                                    u.user.name),
 991                                         m->u.kernel.match->name,
 992                                         strlen(m->u.kernel.match->name)+1)
 993                            != 0) {
 994                                ret = -EFAULT;
 995                                goto free_counters;
 996                        }
 997                }
 998
 999                t = ip6t_get_target_c(e);
1000                if (copy_to_user(userptr + off + e->target_offset
1001                                 + offsetof(struct xt_entry_target,
1002                                            u.user.name),
1003                                 t->u.kernel.target->name,
1004                                 strlen(t->u.kernel.target->name)+1) != 0) {
1005                        ret = -EFAULT;
1006                        goto free_counters;
1007                }
1008        }
1009
1010 free_counters:
1011        vfree(counters);
1012        return ret;
1013}
1014
1015#ifdef CONFIG_COMPAT
1016static void compat_standard_from_user(void *dst, const void *src)
1017{
1018        int v = *(compat_int_t *)src;
1019
1020        if (v > 0)
1021                v += xt_compat_calc_jump(AF_INET6, v);
1022        memcpy(dst, &v, sizeof(v));
1023}
1024
1025static int compat_standard_to_user(void __user *dst, const void *src)
1026{
1027        compat_int_t cv = *(int *)src;
1028
1029        if (cv > 0)
1030                cv -= xt_compat_calc_jump(AF_INET6, cv);
1031        return copy_to_user(dst, &cv, sizeof(cv)) ? -EFAULT : 0;
1032}
1033
1034static int compat_calc_entry(const struct ip6t_entry *e,
1035                             const struct xt_table_info *info,
1036                             const void *base, struct xt_table_info *newinfo)
1037{
1038        const struct xt_entry_match *ematch;
1039        const struct xt_entry_target *t;
1040        unsigned int entry_offset;
1041        int off, i, ret;
1042
1043        off = sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
1044        entry_offset = (void *)e - base;
1045        xt_ematch_foreach(ematch, e)
1046                off += xt_compat_match_offset(ematch->u.kernel.match);
1047        t = ip6t_get_target_c(e);
1048        off += xt_compat_target_offset(t->u.kernel.target);
1049        newinfo->size -= off;
1050        ret = xt_compat_add_offset(AF_INET6, entry_offset, off);
1051        if (ret)
1052                return ret;
1053
1054        for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1055                if (info->hook_entry[i] &&
1056                    (e < (struct ip6t_entry *)(base + info->hook_entry[i])))
1057                        newinfo->hook_entry[i] -= off;
1058                if (info->underflow[i] &&
1059                    (e < (struct ip6t_entry *)(base + info->underflow[i])))
1060                        newinfo->underflow[i] -= off;
1061        }
1062        return 0;
1063}
1064
1065static int compat_table_info(const struct xt_table_info *info,
1066                             struct xt_table_info *newinfo)
1067{
1068        struct ip6t_entry *iter;
1069        void *loc_cpu_entry;
1070        int ret;
1071
1072        if (!newinfo || !info)
1073                return -EINVAL;
1074
1075        /* we dont care about newinfo->entries[] */
1076        memcpy(newinfo, info, offsetof(struct xt_table_info, entries));
1077        newinfo->initial_entries = 0;
1078        loc_cpu_entry = info->entries[raw_smp_processor_id()];
1079        xt_entry_foreach(iter, loc_cpu_entry, info->size) {
1080                ret = compat_calc_entry(iter, info, loc_cpu_entry, newinfo);
1081                if (ret != 0)
1082                        return ret;
1083        }
1084        return 0;
1085}
1086#endif
1087
1088static int get_info(struct net *net, void __user *user,
1089                    const int *len, int compat)
1090{
1091        char name[XT_TABLE_MAXNAMELEN];
1092        struct xt_table *t;
1093        int ret;
1094
1095        if (*len != sizeof(struct ip6t_getinfo)) {
1096                duprintf("length %u != %zu\n", *len,
1097                         sizeof(struct ip6t_getinfo));
1098                return -EINVAL;
1099        }
1100
1101        if (copy_from_user(name, user, sizeof(name)) != 0)
1102                return -EFAULT;
1103
1104        name[XT_TABLE_MAXNAMELEN-1] = '\0';
1105#ifdef CONFIG_COMPAT
1106        if (compat)
1107                xt_compat_lock(AF_INET6);
1108#endif
1109        t = try_then_request_module(xt_find_table_lock(net, AF_INET6, name),
1110                                    "ip6table_%s", name);
1111        if (t && !IS_ERR(t)) {
1112                struct ip6t_getinfo info;
1113                const struct xt_table_info *private = t->private;
1114#ifdef CONFIG_COMPAT
1115                struct xt_table_info tmp;
1116
1117                if (compat) {
1118                        ret = compat_table_info(private, &tmp);
1119                        xt_compat_flush_offsets(AF_INET6);
1120                        private = &tmp;
1121                }
1122#endif
1123                memset(&info, 0, sizeof(info));
1124                info.valid_hooks = t->valid_hooks;
1125                memcpy(info.hook_entry, private->hook_entry,
1126                       sizeof(info.hook_entry));
1127                memcpy(info.underflow, private->underflow,
1128                       sizeof(info.underflow));
1129                info.num_entries = private->number;
1130                info.size = private->size;
1131                strcpy(info.name, name);
1132
1133                if (copy_to_user(user, &info, *len) != 0)
1134                        ret = -EFAULT;
1135                else
1136                        ret = 0;
1137
1138                xt_table_unlock(t);
1139                module_put(t->me);
1140        } else
1141                ret = t ? PTR_ERR(t) : -ENOENT;
1142#ifdef CONFIG_COMPAT
1143        if (compat)
1144                xt_compat_unlock(AF_INET6);
1145#endif
1146        return ret;
1147}
1148
1149static int
1150get_entries(struct net *net, struct ip6t_get_entries __user *uptr,
1151            const int *len)
1152{
1153        int ret;
1154        struct ip6t_get_entries get;
1155        struct xt_table *t;
1156
1157        if (*len < sizeof(get)) {
1158                duprintf("get_entries: %u < %zu\n", *len, sizeof(get));
1159                return -EINVAL;
1160        }
1161        if (copy_from_user(&get, uptr, sizeof(get)) != 0)
1162                return -EFAULT;
1163        if (*len != sizeof(struct ip6t_get_entries) + get.size) {
1164                duprintf("get_entries: %u != %zu\n",
1165                         *len, sizeof(get) + get.size);
1166                return -EINVAL;
1167        }
1168
1169        t = xt_find_table_lock(net, AF_INET6, get.name);
1170        if (t && !IS_ERR(t)) {
1171                struct xt_table_info *private = t->private;
1172                duprintf("t->private->number = %u\n", private->number);
1173                if (get.size == private->size)
1174                        ret = copy_entries_to_user(private->size,
1175                                                   t, uptr->entrytable);
1176                else {
1177                        duprintf("get_entries: I've got %u not %u!\n",
1178                                 private->size, get.size);
1179                        ret = -EAGAIN;
1180                }
1181                module_put(t->me);
1182                xt_table_unlock(t);
1183        } else
1184                ret = t ? PTR_ERR(t) : -ENOENT;
1185
1186        return ret;
1187}
1188
1189static int
1190__do_replace(struct net *net, const char *name, unsigned int valid_hooks,
1191             struct xt_table_info *newinfo, unsigned int num_counters,
1192             void __user *counters_ptr)
1193{
1194        int ret;
1195        struct xt_table *t;
1196        struct xt_table_info *oldinfo;
1197        struct xt_counters *counters;
1198        const void *loc_cpu_old_entry;
1199        struct ip6t_entry *iter;
1200
1201        ret = 0;
1202        counters = vzalloc(num_counters * sizeof(struct xt_counters));
1203        if (!counters) {
1204                ret = -ENOMEM;
1205                goto out;
1206        }
1207
1208        t = try_then_request_module(xt_find_table_lock(net, AF_INET6, name),
1209                                    "ip6table_%s", name);
1210        if (!t || IS_ERR(t)) {
1211                ret = t ? PTR_ERR(t) : -ENOENT;
1212                goto free_newinfo_counters_untrans;
1213        }
1214
1215        /* You lied! */
1216        if (valid_hooks != t->valid_hooks) {
1217                duprintf("Valid hook crap: %08X vs %08X\n",
1218                         valid_hooks, t->valid_hooks);
1219                ret = -EINVAL;
1220                goto put_module;
1221        }
1222
1223        oldinfo = xt_replace_table(t, num_counters, newinfo, &ret);
1224        if (!oldinfo)
1225                goto put_module;
1226
1227        /* Update module usage count based on number of rules */
1228        duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
1229                oldinfo->number, oldinfo->initial_entries, newinfo->number);
1230        if ((oldinfo->number > oldinfo->initial_entries) ||
1231            (newinfo->number <= oldinfo->initial_entries))
1232                module_put(t->me);
1233        if ((oldinfo->number > oldinfo->initial_entries) &&
1234            (newinfo->number <= oldinfo->initial_entries))
1235                module_put(t->me);
1236
1237        /* Get the old counters, and synchronize with replace */
1238        get_counters(oldinfo, counters);
1239
1240        /* Decrease module usage counts and free resource */
1241        loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
1242        xt_entry_foreach(iter, loc_cpu_old_entry, oldinfo->size)
1243                cleanup_entry(iter, net);
1244
1245        xt_free_table_info(oldinfo);
1246        if (copy_to_user(counters_ptr, counters,
1247                         sizeof(struct xt_counters) * num_counters) != 0)
1248                ret = -EFAULT;
1249        vfree(counters);
1250        xt_table_unlock(t);
1251        return ret;
1252
1253 put_module:
1254        module_put(t->me);
1255        xt_table_unlock(t);
1256 free_newinfo_counters_untrans:
1257        vfree(counters);
1258 out:
1259        return ret;
1260}
1261
1262static int
1263do_replace(struct net *net, const void __user *user, unsigned int len)
1264{
1265        int ret;
1266        struct ip6t_replace tmp;
1267        struct xt_table_info *newinfo;
1268        void *loc_cpu_entry;
1269        struct ip6t_entry *iter;
1270
1271        if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1272                return -EFAULT;
1273
1274        /* overflow check */
1275        if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
1276                return -ENOMEM;
1277
1278        newinfo = xt_alloc_table_info(tmp.size);
1279        if (!newinfo)
1280                return -ENOMEM;
1281
1282        /* choose the copy that is on our node/cpu */
1283        loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1284        if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
1285                           tmp.size) != 0) {
1286                ret = -EFAULT;
1287                goto free_newinfo;
1288        }
1289
1290        ret = translate_table(net, newinfo, loc_cpu_entry, &tmp);
1291        if (ret != 0)
1292                goto free_newinfo;
1293
1294        duprintf("ip_tables: Translated table\n");
1295
1296        ret = __do_replace(net, tmp.name, tmp.valid_hooks, newinfo,
1297                           tmp.num_counters, tmp.counters);
1298        if (ret)
1299                goto free_newinfo_untrans;
1300        return 0;
1301
1302 free_newinfo_untrans:
1303        xt_entry_foreach(iter, loc_cpu_entry, newinfo->size)
1304                cleanup_entry(iter, net);
1305 free_newinfo:
1306        xt_free_table_info(newinfo);
1307        return ret;
1308}
1309
1310static int
1311do_add_counters(struct net *net, const void __user *user, unsigned int len,
1312                int compat)
1313{
1314        unsigned int i, curcpu;
1315        struct xt_counters_info tmp;
1316        struct xt_counters *paddc;
1317        unsigned int num_counters;
1318        char *name;
1319        int size;
1320        void *ptmp;
1321        struct xt_table *t;
1322        const struct xt_table_info *private;
1323        int ret = 0;
1324        const void *loc_cpu_entry;
1325        struct ip6t_entry *iter;
1326#ifdef CONFIG_COMPAT
1327        struct compat_xt_counters_info compat_tmp;
1328
1329        if (compat) {
1330                ptmp = &compat_tmp;
1331                size = sizeof(struct compat_xt_counters_info);
1332        } else
1333#endif
1334        {
1335                ptmp = &tmp;
1336                size = sizeof(struct xt_counters_info);
1337        }
1338
1339        if (copy_from_user(ptmp, user, size) != 0)
1340                return -EFAULT;
1341
1342#ifdef CONFIG_COMPAT
1343        if (compat) {
1344                num_counters = compat_tmp.num_counters;
1345                name = compat_tmp.name;
1346        } else
1347#endif
1348        {
1349                num_counters = tmp.num_counters;
1350                name = tmp.name;
1351        }
1352
1353        if (len != size + num_counters * sizeof(struct xt_counters))
1354                return -EINVAL;
1355
1356        paddc = vmalloc(len - size);
1357        if (!paddc)
1358                return -ENOMEM;
1359
1360        if (copy_from_user(paddc, user + size, len - size) != 0) {
1361                ret = -EFAULT;
1362                goto free;
1363        }
1364
1365        t = xt_find_table_lock(net, AF_INET6, name);
1366        if (!t || IS_ERR(t)) {
1367                ret = t ? PTR_ERR(t) : -ENOENT;
1368                goto free;
1369        }
1370
1371
1372        local_bh_disable();
1373        private = t->private;
1374        if (private->number != num_counters) {
1375                ret = -EINVAL;
1376                goto unlock_up_free;
1377        }
1378
1379        i = 0;
1380        /* Choose the copy that is on our node */
1381        curcpu = smp_processor_id();
1382        xt_info_wrlock(curcpu);
1383        loc_cpu_entry = private->entries[curcpu];
1384        xt_entry_foreach(iter, loc_cpu_entry, private->size) {
1385                ADD_COUNTER(iter->counters, paddc[i].bcnt, paddc[i].pcnt);
1386                ++i;
1387        }
1388        xt_info_wrunlock(curcpu);
1389
1390 unlock_up_free:
1391        local_bh_enable();
1392        xt_table_unlock(t);
1393        module_put(t->me);
1394 free:
1395        vfree(paddc);
1396
1397        return ret;
1398}
1399
1400#ifdef CONFIG_COMPAT
1401struct compat_ip6t_replace {
1402        char                    name[XT_TABLE_MAXNAMELEN];
1403        u32                     valid_hooks;
1404        u32                     num_entries;
1405        u32                     size;
1406        u32                     hook_entry[NF_INET_NUMHOOKS];
1407        u32                     underflow[NF_INET_NUMHOOKS];
1408        u32                     num_counters;
1409        compat_uptr_t           counters;       /* struct xt_counters * */
1410        struct compat_ip6t_entry entries[0];
1411};
1412
1413static int
1414compat_copy_entry_to_user(struct ip6t_entry *e, void __user **dstptr,
1415                          unsigned int *size, struct xt_counters *counters,
1416                          unsigned int i)
1417{
1418        struct xt_entry_target *t;
1419        struct compat_ip6t_entry __user *ce;
1420        u_int16_t target_offset, next_offset;
1421        compat_uint_t origsize;
1422        const struct xt_entry_match *ematch;
1423        int ret = 0;
1424
1425        origsize = *size;
1426        ce = (struct compat_ip6t_entry __user *)*dstptr;
1427        if (copy_to_user(ce, e, sizeof(struct ip6t_entry)) != 0 ||
1428            copy_to_user(&ce->counters, &counters[i],
1429            sizeof(counters[i])) != 0)
1430                return -EFAULT;
1431
1432        *dstptr += sizeof(struct compat_ip6t_entry);
1433        *size -= sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
1434
1435        xt_ematch_foreach(ematch, e) {
1436                ret = xt_compat_match_to_user(ematch, dstptr, size);
1437                if (ret != 0)
1438                        return ret;
1439        }
1440        target_offset = e->target_offset - (origsize - *size);
1441        t = ip6t_get_target(e);
1442        ret = xt_compat_target_to_user(t, dstptr, size);
1443        if (ret)
1444                return ret;
1445        next_offset = e->next_offset - (origsize - *size);
1446        if (put_user(target_offset, &ce->target_offset) != 0 ||
1447            put_user(next_offset, &ce->next_offset) != 0)
1448                return -EFAULT;
1449        return 0;
1450}
1451
1452static int
1453compat_find_calc_match(struct xt_entry_match *m,
1454                       const char *name,
1455                       const struct ip6t_ip6 *ipv6,
1456                       unsigned int hookmask,
1457                       int *size)
1458{
1459        struct xt_match *match;
1460
1461        match = xt_request_find_match(NFPROTO_IPV6, m->u.user.name,
1462                                      m->u.user.revision);
1463        if (IS_ERR(match)) {
1464                duprintf("compat_check_calc_match: `%s' not found\n",
1465                         m->u.user.name);
1466                return PTR_ERR(match);
1467        }
1468        m->u.kernel.match = match;
1469        *size += xt_compat_match_offset(match);
1470        return 0;
1471}
1472
1473static void compat_release_entry(struct compat_ip6t_entry *e)
1474{
1475        struct xt_entry_target *t;
1476        struct xt_entry_match *ematch;
1477
1478        /* Cleanup all matches */
1479        xt_ematch_foreach(ematch, e)
1480                module_put(ematch->u.kernel.match->me);
1481        t = compat_ip6t_get_target(e);
1482        module_put(t->u.kernel.target->me);
1483}
1484
1485static int
1486check_compat_entry_size_and_hooks(struct compat_ip6t_entry *e,
1487                                  struct xt_table_info *newinfo,
1488                                  unsigned int *size,
1489                                  const unsigned char *base,
1490                                  const unsigned char *limit,
1491                                  const unsigned int *hook_entries,
1492                                  const unsigned int *underflows,
1493                                  const char *name)
1494{
1495        struct xt_entry_match *ematch;
1496        struct xt_entry_target *t;
1497        struct xt_target *target;
1498        unsigned int entry_offset;
1499        unsigned int j;
1500        int ret, off, h;
1501
1502        duprintf("check_compat_entry_size_and_hooks %p\n", e);
1503        if ((unsigned long)e % __alignof__(struct compat_ip6t_entry) != 0 ||
1504            (unsigned char *)e + sizeof(struct compat_ip6t_entry) >= limit) {
1505                duprintf("Bad offset %p, limit = %p\n", e, limit);
1506                return -EINVAL;
1507        }
1508
1509        if (e->next_offset < sizeof(struct compat_ip6t_entry) +
1510                             sizeof(struct compat_xt_entry_target)) {
1511                duprintf("checking: element %p size %u\n",
1512                         e, e->next_offset);
1513                return -EINVAL;
1514        }
1515
1516        /* For purposes of check_entry casting the compat entry is fine */
1517        ret = check_entry((struct ip6t_entry *)e, name);
1518        if (ret)
1519                return ret;
1520
1521        off = sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
1522        entry_offset = (void *)e - (void *)base;
1523        j = 0;
1524        xt_ematch_foreach(ematch, e) {
1525                ret = compat_find_calc_match(ematch, name,
1526                                             &e->ipv6, e->comefrom, &off);
1527                if (ret != 0)
1528                        goto release_matches;
1529                ++j;
1530        }
1531
1532        t = compat_ip6t_get_target(e);
1533        target = xt_request_find_target(NFPROTO_IPV6, t->u.user.name,
1534                                        t->u.user.revision);
1535        if (IS_ERR(target)) {
1536                duprintf("check_compat_entry_size_and_hooks: `%s' not found\n",
1537                         t->u.user.name);
1538                ret = PTR_ERR(target);
1539                goto release_matches;
1540        }
1541        t->u.kernel.target = target;
1542
1543        off += xt_compat_target_offset(target);
1544        *size += off;
1545        ret = xt_compat_add_offset(AF_INET6, entry_offset, off);
1546        if (ret)
1547                goto out;
1548
1549        /* Check hooks & underflows */
1550        for (h = 0; h < NF_INET_NUMHOOKS; h++) {
1551                if ((unsigned char *)e - base == hook_entries[h])
1552                        newinfo->hook_entry[h] = hook_entries[h];
1553                if ((unsigned char *)e - base == underflows[h])
1554                        newinfo->underflow[h] = underflows[h];
1555        }
1556
1557        /* Clear counters and comefrom */
1558        memset(&e->counters, 0, sizeof(e->counters));
1559        e->comefrom = 0;
1560        return 0;
1561
1562out:
1563        module_put(t->u.kernel.target->me);
1564release_matches:
1565        xt_ematch_foreach(ematch, e) {
1566                if (j-- == 0)
1567                        break;
1568                module_put(ematch->u.kernel.match->me);
1569        }
1570        return ret;
1571}
1572
1573static int
1574compat_copy_entry_from_user(struct compat_ip6t_entry *e, void **dstptr,
1575                            unsigned int *size, const char *name,
1576                            struct xt_table_info *newinfo, unsigned char *base)
1577{
1578        struct xt_entry_target *t;
1579        struct xt_target *target;
1580        struct ip6t_entry *de;
1581        unsigned int origsize;
1582        int ret, h;
1583        struct xt_entry_match *ematch;
1584
1585        ret = 0;
1586        origsize = *size;
1587        de = (struct ip6t_entry *)*dstptr;
1588        memcpy(de, e, sizeof(struct ip6t_entry));
1589        memcpy(&de->counters, &e->counters, sizeof(e->counters));
1590
1591        *dstptr += sizeof(struct ip6t_entry);
1592        *size += sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
1593
1594        xt_ematch_foreach(ematch, e) {
1595                ret = xt_compat_match_from_user(ematch, dstptr, size);
1596                if (ret != 0)
1597                        return ret;
1598        }
1599        de->target_offset = e->target_offset - (origsize - *size);
1600        t = compat_ip6t_get_target(e);
1601        target = t->u.kernel.target;
1602        xt_compat_target_from_user(t, dstptr, size);
1603
1604        de->next_offset = e->next_offset - (origsize - *size);
1605        for (h = 0; h < NF_INET_NUMHOOKS; h++) {
1606                if ((unsigned char *)de - base < newinfo->hook_entry[h])
1607                        newinfo->hook_entry[h] -= origsize - *size;
1608                if ((unsigned char *)de - base < newinfo->underflow[h])
1609                        newinfo->underflow[h] -= origsize - *size;
1610        }
1611        return ret;
1612}
1613
1614static int compat_check_entry(struct ip6t_entry *e, struct net *net,
1615                              const char *name)
1616{
1617        unsigned int j;
1618        int ret = 0;
1619        struct xt_mtchk_param mtpar;
1620        struct xt_entry_match *ematch;
1621
1622        j = 0;
1623        mtpar.net       = net;
1624        mtpar.table     = name;
1625        mtpar.entryinfo = &e->ipv6;
1626        mtpar.hook_mask = e->comefrom;
1627        mtpar.family    = NFPROTO_IPV6;
1628        xt_ematch_foreach(ematch, e) {
1629                ret = check_match(ematch, &mtpar);
1630                if (ret != 0)
1631                        goto cleanup_matches;
1632                ++j;
1633        }
1634
1635        ret = check_target(e, net, name);
1636        if (ret)
1637                goto cleanup_matches;
1638        return 0;
1639
1640 cleanup_matches:
1641        xt_ematch_foreach(ematch, e) {
1642                if (j-- == 0)
1643                        break;
1644                cleanup_match(ematch, net);
1645        }
1646        return ret;
1647}
1648
1649static int
1650translate_compat_table(struct net *net,
1651                       const char *name,
1652                       unsigned int valid_hooks,
1653                       struct xt_table_info **pinfo,
1654                       void **pentry0,
1655                       unsigned int total_size,
1656                       unsigned int number,
1657                       unsigned int *hook_entries,
1658                       unsigned int *underflows)
1659{
1660        unsigned int i, j;
1661        struct xt_table_info *newinfo, *info;
1662        void *pos, *entry0, *entry1;
1663        struct compat_ip6t_entry *iter0;
1664        struct ip6t_entry *iter1;
1665        unsigned int size;
1666        int ret = 0;
1667
1668        info = *pinfo;
1669        entry0 = *pentry0;
1670        size = total_size;
1671        info->number = number;
1672
1673        /* Init all hooks to impossible value. */
1674        for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1675                info->hook_entry[i] = 0xFFFFFFFF;
1676                info->underflow[i] = 0xFFFFFFFF;
1677        }
1678
1679        duprintf("translate_compat_table: size %u\n", info->size);
1680        j = 0;
1681        xt_compat_lock(AF_INET6);
1682        /* Walk through entries, checking offsets. */
1683        xt_entry_foreach(iter0, entry0, total_size) {
1684                ret = check_compat_entry_size_and_hooks(iter0, info, &size,
1685                                                        entry0,
1686                                                        entry0 + total_size,
1687                                                        hook_entries,
1688                                                        underflows,
1689                                                        name);
1690                if (ret != 0)
1691                        goto out_unlock;
1692                ++j;
1693        }
1694
1695        ret = -EINVAL;
1696        if (j != number) {
1697                duprintf("translate_compat_table: %u not %u entries\n",
1698                         j, number);
1699                goto out_unlock;
1700        }
1701
1702        /* Check hooks all assigned */
1703        for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1704                /* Only hooks which are valid */
1705                if (!(valid_hooks & (1 << i)))
1706                        continue;
1707                if (info->hook_entry[i] == 0xFFFFFFFF) {
1708                        duprintf("Invalid hook entry %u %u\n",
1709                                 i, hook_entries[i]);
1710                        goto out_unlock;
1711                }
1712                if (info->underflow[i] == 0xFFFFFFFF) {
1713                        duprintf("Invalid underflow %u %u\n",
1714                                 i, underflows[i]);
1715                        goto out_unlock;
1716                }
1717        }
1718
1719        ret = -ENOMEM;
1720        newinfo = xt_alloc_table_info(size);
1721        if (!newinfo)
1722                goto out_unlock;
1723
1724        newinfo->number = number;
1725        for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1726                newinfo->hook_entry[i] = info->hook_entry[i];
1727                newinfo->underflow[i] = info->underflow[i];
1728        }
1729        entry1 = newinfo->entries[raw_smp_processor_id()];
1730        pos = entry1;
1731        size = total_size;
1732        xt_entry_foreach(iter0, entry0, total_size) {
1733                ret = compat_copy_entry_from_user(iter0, &pos, &size,
1734                                                  name, newinfo, entry1);
1735                if (ret != 0)
1736                        break;
1737        }
1738        xt_compat_flush_offsets(AF_INET6);
1739        xt_compat_unlock(AF_INET6);
1740        if (ret)
1741                goto free_newinfo;
1742
1743        ret = -ELOOP;
1744        if (!mark_source_chains(newinfo, valid_hooks, entry1))
1745                goto free_newinfo;
1746
1747        i = 0;
1748        xt_entry_foreach(iter1, entry1, newinfo->size) {
1749                ret = compat_check_entry(iter1, net, name);
1750                if (ret != 0)
1751                        break;
1752                ++i;
1753                if (strcmp(ip6t_get_target(iter1)->u.user.name,
1754                    XT_ERROR_TARGET) == 0)
1755                        ++newinfo->stacksize;
1756        }
1757        if (ret) {
1758                /*
1759                 * The first i matches need cleanup_entry (calls ->destroy)
1760                 * because they had called ->check already. The other j-i
1761                 * entries need only release.
1762                 */
1763                int skip = i;
1764                j -= i;
1765                xt_entry_foreach(iter0, entry0, newinfo->size) {
1766                        if (skip-- > 0)
1767                                continue;
1768                        if (j-- == 0)
1769                                break;
1770                        compat_release_entry(iter0);
1771                }
1772                xt_entry_foreach(iter1, entry1, newinfo->size) {
1773                        if (i-- == 0)
1774                                break;
1775                        cleanup_entry(iter1, net);
1776                }
1777                xt_free_table_info(newinfo);
1778                return ret;
1779        }
1780
1781        /* And one copy for every other CPU */
1782        for_each_possible_cpu(i)
1783                if (newinfo->entries[i] && newinfo->entries[i] != entry1)
1784                        memcpy(newinfo->entries[i], entry1, newinfo->size);
1785
1786        *pinfo = newinfo;
1787        *pentry0 = entry1;
1788        xt_free_table_info(info);
1789        return 0;
1790
1791free_newinfo:
1792        xt_free_table_info(newinfo);
1793out:
1794        xt_entry_foreach(iter0, entry0, total_size) {
1795                if (j-- == 0)
1796                        break;
1797                compat_release_entry(iter0);
1798        }
1799        return ret;
1800out_unlock:
1801        xt_compat_flush_offsets(AF_INET6);
1802        xt_compat_unlock(AF_INET6);
1803        goto out;
1804}
1805
1806static int
1807compat_do_replace(struct net *net, void __user *user, unsigned int len)
1808{
1809        int ret;
1810        struct compat_ip6t_replace tmp;
1811        struct xt_table_info *newinfo;
1812        void *loc_cpu_entry;
1813        struct ip6t_entry *iter;
1814
1815        if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1816                return -EFAULT;
1817
1818        /* overflow check */
1819        if (tmp.size >= INT_MAX / num_possible_cpus())
1820                return -ENOMEM;
1821        if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
1822                return -ENOMEM;
1823
1824        newinfo = xt_alloc_table_info(tmp.size);
1825        if (!newinfo)
1826                return -ENOMEM;
1827
1828        /* choose the copy that is on our node/cpu */
1829        loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1830        if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
1831                           tmp.size) != 0) {
1832                ret = -EFAULT;
1833                goto free_newinfo;
1834        }
1835
1836        ret = translate_compat_table(net, tmp.name, tmp.valid_hooks,
1837                                     &newinfo, &loc_cpu_entry, tmp.size,
1838                                     tmp.num_entries, tmp.hook_entry,
1839                                     tmp.underflow);
1840        if (ret != 0)
1841                goto free_newinfo;
1842
1843        duprintf("compat_do_replace: Translated table\n");
1844
1845        ret = __do_replace(net, tmp.name, tmp.valid_hooks, newinfo,
1846                           tmp.num_counters, compat_ptr(tmp.counters));
1847        if (ret)
1848                goto free_newinfo_untrans;
1849        return 0;
1850
1851 free_newinfo_untrans:
1852        xt_entry_foreach(iter, loc_cpu_entry, newinfo->size)
1853                cleanup_entry(iter, net);
1854 free_newinfo:
1855        xt_free_table_info(newinfo);
1856        return ret;
1857}
1858
1859static int
1860compat_do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user,
1861                       unsigned int len)
1862{
1863        int ret;
1864
1865        if (!capable(CAP_NET_ADMIN))
1866                return -EPERM;
1867
1868        switch (cmd) {
1869        case IP6T_SO_SET_REPLACE:
1870                ret = compat_do_replace(sock_net(sk), user, len);
1871                break;
1872
1873        case IP6T_SO_SET_ADD_COUNTERS:
1874                ret = do_add_counters(sock_net(sk), user, len, 1);
1875                break;
1876
1877        default:
1878                duprintf("do_ip6t_set_ctl:  unknown request %i\n", cmd);
1879                ret = -EINVAL;
1880        }
1881
1882        return ret;
1883}
1884
1885struct compat_ip6t_get_entries {
1886        char name[XT_TABLE_MAXNAMELEN];
1887        compat_uint_t size;
1888        struct compat_ip6t_entry entrytable[0];
1889};
1890
1891static int
1892compat_copy_entries_to_user(unsigned int total_size, struct xt_table *table,
1893                            void __user *userptr)
1894{
1895        struct xt_counters *counters;
1896        const struct xt_table_info *private = table->private;
1897        void __user *pos;
1898        unsigned int size;
1899        int ret = 0;
1900        const void *loc_cpu_entry;
1901        unsigned int i = 0;
1902        struct ip6t_entry *iter;
1903
1904        counters = alloc_counters(table);
1905        if (IS_ERR(counters))
1906                return PTR_ERR(counters);
1907
1908        /* choose the copy that is on our node/cpu, ...
1909         * This choice is lazy (because current thread is
1910         * allowed to migrate to another cpu)
1911         */
1912        loc_cpu_entry = private->entries[raw_smp_processor_id()];
1913        pos = userptr;
1914        size = total_size;
1915        xt_entry_foreach(iter, loc_cpu_entry, total_size) {
1916                ret = compat_copy_entry_to_user(iter, &pos,
1917                                                &size, counters, i++);
1918                if (ret != 0)
1919                        break;
1920        }
1921
1922        vfree(counters);
1923        return ret;
1924}
1925
1926static int
1927compat_get_entries(struct net *net, struct compat_ip6t_get_entries __user *uptr,
1928                   int *len)
1929{
1930        int ret;
1931        struct compat_ip6t_get_entries get;
1932        struct xt_table *t;
1933
1934        if (*len < sizeof(get)) {
1935                duprintf("compat_get_entries: %u < %zu\n", *len, sizeof(get));
1936                return -EINVAL;
1937        }
1938
1939        if (copy_from_user(&get, uptr, sizeof(get)) != 0)
1940                return -EFAULT;
1941
1942        if (*len != sizeof(struct compat_ip6t_get_entries) + get.size) {
1943                duprintf("compat_get_entries: %u != %zu\n",
1944                         *len, sizeof(get) + get.size);
1945                return -EINVAL;
1946        }
1947
1948        xt_compat_lock(AF_INET6);
1949        t = xt_find_table_lock(net, AF_INET6, get.name);
1950        if (t && !IS_ERR(t)) {
1951                const struct xt_table_info *private = t->private;
1952                struct xt_table_info info;
1953                duprintf("t->private->number = %u\n", private->number);
1954                ret = compat_table_info(private, &info);
1955                if (!ret && get.size == info.size) {
1956                        ret = compat_copy_entries_to_user(private->size,
1957                                                          t, uptr->entrytable);
1958                } else if (!ret) {
1959                        duprintf("compat_get_entries: I've got %u not %u!\n",
1960                                 private->size, get.size);
1961                        ret = -EAGAIN;
1962                }
1963                xt_compat_flush_offsets(AF_INET6);
1964                module_put(t->me);
1965                xt_table_unlock(t);
1966        } else
1967                ret = t ? PTR_ERR(t) : -ENOENT;
1968
1969        xt_compat_unlock(AF_INET6);
1970        return ret;
1971}
1972
1973static int do_ip6t_get_ctl(struct sock *, int, void __user *, int *);
1974
1975static int
1976compat_do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
1977{
1978        int ret;
1979
1980        if (!capable(CAP_NET_ADMIN))
1981                return -EPERM;
1982
1983        switch (cmd) {
1984        case IP6T_SO_GET_INFO:
1985                ret = get_info(sock_net(sk), user, len, 1);
1986                break;
1987        case IP6T_SO_GET_ENTRIES:
1988                ret = compat_get_entries(sock_net(sk), user, len);
1989                break;
1990        default:
1991                ret = do_ip6t_get_ctl(sk, cmd, user, len);
1992        }
1993        return ret;
1994}
1995#endif
1996
1997static int
1998do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
1999{
2000        int ret;
2001
2002        if (!capable(CAP_NET_ADMIN))
2003                return -EPERM;
2004
2005        switch (cmd) {
2006        case IP6T_SO_SET_REPLACE:
2007                ret = do_replace(sock_net(sk), user, len);
2008                break;
2009
2010        case IP6T_SO_SET_ADD_COUNTERS:
2011                ret = do_add_counters(sock_net(sk), user, len, 0);
2012                break;
2013
2014        default:
2015                duprintf("do_ip6t_set_ctl:  unknown request %i\n", cmd);
2016                ret = -EINVAL;
2017        }
2018
2019        return ret;
2020}
2021
2022static int
2023do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
2024{
2025        int ret;
2026
2027        if (!capable(CAP_NET_ADMIN))
2028                return -EPERM;
2029
2030        switch (cmd) {
2031        case IP6T_SO_GET_INFO:
2032                ret = get_info(sock_net(sk), user, len, 0);
2033                break;
2034
2035        case IP6T_SO_GET_ENTRIES:
2036                ret = get_entries(sock_net(sk), user, len);
2037                break;
2038
2039        case IP6T_SO_GET_REVISION_MATCH:
2040        case IP6T_SO_GET_REVISION_TARGET: {
2041                struct xt_get_revision rev;
2042                int target;
2043
2044                if (*len != sizeof(rev)) {
2045                        ret = -EINVAL;
2046                        break;
2047                }
2048                if (copy_from_user(&rev, user, sizeof(rev)) != 0) {
2049                        ret = -EFAULT;
2050                        break;
2051                }
2052
2053                if (cmd == IP6T_SO_GET_REVISION_TARGET)
2054                        target = 1;
2055                else
2056                        target = 0;
2057
2058                try_then_request_module(xt_find_revision(AF_INET6, rev.name,
2059                                                         rev.revision,
2060                                                         target, &ret),
2061                                        "ip6t_%s", rev.name);
2062                break;
2063        }
2064
2065        default:
2066                duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd);
2067                ret = -EINVAL;
2068        }
2069
2070        return ret;
2071}
2072
2073struct xt_table *ip6t_register_table(struct net *net,
2074                                     const struct xt_table *table,
2075                                     const struct ip6t_replace *repl)
2076{
2077        int ret;
2078        struct xt_table_info *newinfo;
2079        struct xt_table_info bootstrap = {0};
2080        void *loc_cpu_entry;
2081        struct xt_table *new_table;
2082
2083        newinfo = xt_alloc_table_info(repl->size);
2084        if (!newinfo) {
2085                ret = -ENOMEM;
2086                goto out;
2087        }
2088
2089        /* choose the copy on our node/cpu, but dont care about preemption */
2090        loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
2091        memcpy(loc_cpu_entry, repl->entries, repl->size);
2092
2093        ret = translate_table(net, newinfo, loc_cpu_entry, repl);
2094        if (ret != 0)
2095                goto out_free;
2096
2097        new_table = xt_register_table(net, table, &bootstrap, newinfo);
2098        if (IS_ERR(new_table)) {
2099                ret = PTR_ERR(new_table);
2100                goto out_free;
2101        }
2102        return new_table;
2103
2104out_free:
2105        xt_free_table_info(newinfo);
2106out:
2107        return ERR_PTR(ret);
2108}
2109
2110void ip6t_unregister_table(struct net *net, struct xt_table *table)
2111{
2112        struct xt_table_info *private;
2113        void *loc_cpu_entry;
2114        struct module *table_owner = table->me;
2115        struct ip6t_entry *iter;
2116
2117        private = xt_unregister_table(table);
2118
2119        /* Decrease module usage counts and free resources */
2120        loc_cpu_entry = private->entries[raw_smp_processor_id()];
2121        xt_entry_foreach(iter, loc_cpu_entry, private->size)
2122                cleanup_entry(iter, net);
2123        if (private->number > private->initial_entries)
2124                module_put(table_owner);
2125        xt_free_table_info(private);
2126}
2127
2128/* Returns 1 if the type and code is matched by the range, 0 otherwise */
2129static inline bool
2130icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
2131                     u_int8_t type, u_int8_t code,
2132                     bool invert)
2133{
2134        return (type == test_type && code >= min_code && code <= max_code)
2135                ^ invert;
2136}
2137
2138static bool
2139icmp6_match(const struct sk_buff *skb, struct xt_action_param *par)
2140{
2141        const struct icmp6hdr *ic;
2142        struct icmp6hdr _icmph;
2143        const struct ip6t_icmp *icmpinfo = par->matchinfo;
2144
2145        /* Must not be a fragment. */
2146        if (par->fragoff != 0)
2147                return false;
2148
2149        ic = skb_header_pointer(skb, par->thoff, sizeof(_icmph), &_icmph);
2150        if (ic == NULL) {
2151                /* We've been asked to examine this packet, and we
2152                 * can't.  Hence, no choice but to drop.
2153                 */
2154                duprintf("Dropping evil ICMP tinygram.\n");
2155                par->hotdrop = true;
2156                return false;
2157        }
2158
2159        return icmp6_type_code_match(icmpinfo->type,
2160                                     icmpinfo->code[0],
2161                                     icmpinfo->code[1],
2162                                     ic->icmp6_type, ic->icmp6_code,
2163                                     !!(icmpinfo->invflags&IP6T_ICMP_INV));
2164}
2165
2166/* Called when user tries to insert an entry of this type. */
2167static int icmp6_checkentry(const struct xt_mtchk_param *par)
2168{
2169        const struct ip6t_icmp *icmpinfo = par->matchinfo;
2170
2171        /* Must specify no unknown invflags */
2172        return (icmpinfo->invflags & ~IP6T_ICMP_INV) ? -EINVAL : 0;
2173}
2174
2175/* The built-in targets: standard (NULL) and error. */
2176static struct xt_target ip6t_builtin_tg[] __read_mostly = {
2177        {
2178                .name             = XT_STANDARD_TARGET,
2179                .targetsize       = sizeof(int),
2180                .family           = NFPROTO_IPV6,
2181#ifdef CONFIG_COMPAT
2182                .compatsize       = sizeof(compat_int_t),
2183                .compat_from_user = compat_standard_from_user,
2184                .compat_to_user   = compat_standard_to_user,
2185#endif
2186        },
2187        {
2188                .name             = XT_ERROR_TARGET,
2189                .target           = ip6t_error,
2190                .targetsize       = XT_FUNCTION_MAXNAMELEN,
2191                .family           = NFPROTO_IPV6,
2192        },
2193};
2194
2195static struct nf_sockopt_ops ip6t_sockopts = {
2196        .pf             = PF_INET6,
2197        .set_optmin     = IP6T_BASE_CTL,
2198        .set_optmax     = IP6T_SO_SET_MAX+1,
2199        .set            = do_ip6t_set_ctl,
2200#ifdef CONFIG_COMPAT
2201        .compat_set     = compat_do_ip6t_set_ctl,
2202#endif
2203        .get_optmin     = IP6T_BASE_CTL,
2204        .get_optmax     = IP6T_SO_GET_MAX+1,
2205        .get            = do_ip6t_get_ctl,
2206#ifdef CONFIG_COMPAT
2207        .compat_get     = compat_do_ip6t_get_ctl,
2208#endif
2209        .owner          = THIS_MODULE,
2210};
2211
2212static struct xt_match ip6t_builtin_mt[] __read_mostly = {
2213        {
2214                .name       = "icmp6",
2215                .match      = icmp6_match,
2216                .matchsize  = sizeof(struct ip6t_icmp),
2217                .checkentry = icmp6_checkentry,
2218                .proto      = IPPROTO_ICMPV6,
2219                .family     = NFPROTO_IPV6,
2220        },
2221};
2222
2223static int __net_init ip6_tables_net_init(struct net *net)
2224{
2225        return xt_proto_init(net, NFPROTO_IPV6);
2226}
2227
2228static void __net_exit ip6_tables_net_exit(struct net *net)
2229{
2230        xt_proto_fini(net, NFPROTO_IPV6);
2231}
2232
2233static struct pernet_operations ip6_tables_net_ops = {
2234        .init = ip6_tables_net_init,
2235        .exit = ip6_tables_net_exit,
2236};
2237
2238static int __init ip6_tables_init(void)
2239{
2240        int ret;
2241
2242        ret = register_pernet_subsys(&ip6_tables_net_ops);
2243        if (ret < 0)
2244                goto err1;
2245
2246        /* Noone else will be downing sem now, so we won't sleep */
2247        ret = xt_register_targets(ip6t_builtin_tg, ARRAY_SIZE(ip6t_builtin_tg));
2248        if (ret < 0)
2249                goto err2;
2250        ret = xt_register_matches(ip6t_builtin_mt, ARRAY_SIZE(ip6t_builtin_mt));
2251        if (ret < 0)
2252                goto err4;
2253
2254        /* Register setsockopt */
2255        ret = nf_register_sockopt(&ip6t_sockopts);
2256        if (ret < 0)
2257                goto err5;
2258
2259        pr_info("(C) 2000-2006 Netfilter Core Team\n");
2260        return 0;
2261
2262err5:
2263        xt_unregister_matches(ip6t_builtin_mt, ARRAY_SIZE(ip6t_builtin_mt));
2264err4:
2265        xt_unregister_targets(ip6t_builtin_tg, ARRAY_SIZE(ip6t_builtin_tg));
2266err2:
2267        unregister_pernet_subsys(&ip6_tables_net_ops);
2268err1:
2269        return ret;
2270}
2271
2272static void __exit ip6_tables_fini(void)
2273{
2274        nf_unregister_sockopt(&ip6t_sockopts);
2275
2276        xt_unregister_matches(ip6t_builtin_mt, ARRAY_SIZE(ip6t_builtin_mt));
2277        xt_unregister_targets(ip6t_builtin_tg, ARRAY_SIZE(ip6t_builtin_tg));
2278        unregister_pernet_subsys(&ip6_tables_net_ops);
2279}
2280
2281/*
2282 * find the offset to specified header or the protocol number of last header
2283 * if target < 0. "last header" is transport protocol header, ESP, or
2284 * "No next header".
2285 *
2286 * If target header is found, its offset is set in *offset and return protocol
2287 * number. Otherwise, return -1.
2288 *
2289 * If the first fragment doesn't contain the final protocol header or
2290 * NEXTHDR_NONE it is considered invalid.
2291 *
2292 * Note that non-1st fragment is special case that "the protocol number
2293 * of last header" is "next header" field in Fragment header. In this case,
2294 * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
2295 * isn't NULL.
2296 *
2297 */
2298int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
2299                  int target, unsigned short *fragoff)
2300{
2301        unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr);
2302        u8 nexthdr = ipv6_hdr(skb)->nexthdr;
2303        unsigned int len = skb->len - start;
2304
2305        if (fragoff)
2306                *fragoff = 0;
2307
2308        while (nexthdr != target) {
2309                struct ipv6_opt_hdr _hdr, *hp;
2310                unsigned int hdrlen;
2311
2312                if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
2313                        if (target < 0)
2314                                break;
2315                        return -ENOENT;
2316                }
2317
2318                hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
2319                if (hp == NULL)
2320                        return -EBADMSG;
2321                if (nexthdr == NEXTHDR_FRAGMENT) {
2322                        unsigned short _frag_off;
2323                        __be16 *fp;
2324                        fp = skb_header_pointer(skb,
2325                                                start+offsetof(struct frag_hdr,
2326                                                               frag_off),
2327                                                sizeof(_frag_off),
2328                                                &_frag_off);
2329                        if (fp == NULL)
2330                                return -EBADMSG;
2331
2332                        _frag_off = ntohs(*fp) & ~0x7;
2333                        if (_frag_off) {
2334                                if (target < 0 &&
2335                                    ((!ipv6_ext_hdr(hp->nexthdr)) ||
2336                                     hp->nexthdr == NEXTHDR_NONE)) {
2337                                        if (fragoff)
2338                                                *fragoff = _frag_off;
2339                                        return hp->nexthdr;
2340                                }
2341                                return -ENOENT;
2342                        }
2343                        hdrlen = 8;
2344                } else if (nexthdr == NEXTHDR_AUTH)
2345                        hdrlen = (hp->hdrlen + 2) << 2;
2346                else
2347                        hdrlen = ipv6_optlen(hp);
2348
2349                nexthdr = hp->nexthdr;
2350                len -= hdrlen;
2351                start += hdrlen;
2352        }
2353
2354        *offset = start;
2355        return nexthdr;
2356}
2357
2358EXPORT_SYMBOL(ip6t_register_table);
2359EXPORT_SYMBOL(ip6t_unregister_table);
2360EXPORT_SYMBOL(ip6t_do_table);
2361EXPORT_SYMBOL(ip6t_ext_hdr);
2362EXPORT_SYMBOL(ipv6_find_hdr);
2363
2364module_init(ip6_tables_init);
2365module_exit(ip6_tables_fini);
2366