linux/net/netfilter/xt_recent.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2006 Patrick McHardy <kaber@trash.net>
   3 * Copyright © CC Computer Consultants GmbH, 2007 - 2008
   4 *
   5 * This program is free software; you can redistribute it and/or modify
   6 * it under the terms of the GNU General Public License version 2 as
   7 * published by the Free Software Foundation.
   8 *
   9 * This is a replacement of the old ipt_recent module, which carried the
  10 * following copyright notice:
  11 *
  12 * Author: Stephen Frost <sfrost@snowman.net>
  13 * Copyright 2002-2003, Stephen Frost, 2.5.x port by laforge@netfilter.org
  14 */
  15#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  16#include <linux/init.h>
  17#include <linux/ip.h>
  18#include <linux/ipv6.h>
  19#include <linux/module.h>
  20#include <linux/moduleparam.h>
  21#include <linux/proc_fs.h>
  22#include <linux/seq_file.h>
  23#include <linux/string.h>
  24#include <linux/ctype.h>
  25#include <linux/list.h>
  26#include <linux/random.h>
  27#include <linux/jhash.h>
  28#include <linux/bitops.h>
  29#include <linux/skbuff.h>
  30#include <linux/inet.h>
  31#include <linux/slab.h>
  32#include <linux/vmalloc.h>
  33#include <net/net_namespace.h>
  34#include <net/netns/generic.h>
  35
  36#include <linux/netfilter/x_tables.h>
  37#include <linux/netfilter/xt_recent.h>
  38
  39MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
  40MODULE_AUTHOR("Jan Engelhardt <jengelh@medozas.de>");
  41MODULE_DESCRIPTION("Xtables: \"recently-seen\" host matching");
  42MODULE_LICENSE("GPL");
  43MODULE_ALIAS("ipt_recent");
  44MODULE_ALIAS("ip6t_recent");
  45
  46static unsigned int ip_list_tot __read_mostly = 100;
  47static unsigned int ip_list_hash_size __read_mostly;
  48static unsigned int ip_list_perms __read_mostly = 0644;
  49static unsigned int ip_list_uid __read_mostly;
  50static unsigned int ip_list_gid __read_mostly;
  51module_param(ip_list_tot, uint, 0400);
  52module_param(ip_list_hash_size, uint, 0400);
  53module_param(ip_list_perms, uint, 0400);
  54module_param(ip_list_uid, uint, S_IRUGO | S_IWUSR);
  55module_param(ip_list_gid, uint, S_IRUGO | S_IWUSR);
  56MODULE_PARM_DESC(ip_list_tot, "number of IPs to remember per list");
  57MODULE_PARM_DESC(ip_list_hash_size, "size of hash table used to look up IPs");
  58MODULE_PARM_DESC(ip_list_perms, "permissions on /proc/net/xt_recent/* files");
  59MODULE_PARM_DESC(ip_list_uid, "default owner of /proc/net/xt_recent/* files");
  60MODULE_PARM_DESC(ip_list_gid, "default owning group of /proc/net/xt_recent/* files");
  61
  62/* retained for backwards compatibility */
  63static unsigned int ip_pkt_list_tot __read_mostly;
  64module_param(ip_pkt_list_tot, uint, 0400);
  65MODULE_PARM_DESC(ip_pkt_list_tot, "number of packets per IP address to remember (max. 255)");
  66
  67#define XT_RECENT_MAX_NSTAMPS   256
  68
  69struct recent_entry {
  70        struct list_head        list;
  71        struct list_head        lru_list;
  72        union nf_inet_addr      addr;
  73        u_int16_t               family;
  74        u_int8_t                ttl;
  75        u_int8_t                index;
  76        u_int16_t               nstamps;
  77        unsigned long           stamps[0];
  78};
  79
  80struct recent_table {
  81        struct list_head        list;
  82        char                    name[XT_RECENT_NAME_LEN];
  83        union nf_inet_addr      mask;
  84        unsigned int            refcnt;
  85        unsigned int            entries;
  86        u8                      nstamps_max_mask;
  87        struct list_head        lru_list;
  88        struct list_head        iphash[0];
  89};
  90
  91struct recent_net {
  92        struct list_head        tables;
  93#ifdef CONFIG_PROC_FS
  94        struct proc_dir_entry   *xt_recent;
  95#endif
  96};
  97
  98static int recent_net_id __read_mostly;
  99
 100static inline struct recent_net *recent_pernet(struct net *net)
 101{
 102        return net_generic(net, recent_net_id);
 103}
 104
 105static DEFINE_SPINLOCK(recent_lock);
 106static DEFINE_MUTEX(recent_mutex);
 107
 108#ifdef CONFIG_PROC_FS
 109static const struct file_operations recent_old_fops, recent_mt_fops;
 110#endif
 111
 112static u_int32_t hash_rnd __read_mostly;
 113static bool hash_rnd_inited __read_mostly;
 114
 115static inline unsigned int recent_entry_hash4(const union nf_inet_addr *addr)
 116{
 117        return jhash_1word((__force u32)addr->ip, hash_rnd) &
 118               (ip_list_hash_size - 1);
 119}
 120
 121static inline unsigned int recent_entry_hash6(const union nf_inet_addr *addr)
 122{
 123        return jhash2((u32 *)addr->ip6, ARRAY_SIZE(addr->ip6), hash_rnd) &
 124               (ip_list_hash_size - 1);
 125}
 126
 127static struct recent_entry *
 128recent_entry_lookup(const struct recent_table *table,
 129                    const union nf_inet_addr *addrp, u_int16_t family,
 130                    u_int8_t ttl)
 131{
 132        struct recent_entry *e;
 133        unsigned int h;
 134
 135        if (family == NFPROTO_IPV4)
 136                h = recent_entry_hash4(addrp);
 137        else
 138                h = recent_entry_hash6(addrp);
 139
 140        list_for_each_entry(e, &table->iphash[h], list)
 141                if (e->family == family &&
 142                    memcmp(&e->addr, addrp, sizeof(e->addr)) == 0 &&
 143                    (ttl == e->ttl || ttl == 0 || e->ttl == 0))
 144                        return e;
 145        return NULL;
 146}
 147
 148static void recent_entry_remove(struct recent_table *t, struct recent_entry *e)
 149{
 150        list_del(&e->list);
 151        list_del(&e->lru_list);
 152        kfree(e);
 153        t->entries--;
 154}
 155
 156/*
 157 * Drop entries with timestamps older then 'time'.
 158 */
 159static void recent_entry_reap(struct recent_table *t, unsigned long time)
 160{
 161        struct recent_entry *e;
 162
 163        /*
 164         * The head of the LRU list is always the oldest entry.
 165         */
 166        e = list_entry(t->lru_list.next, struct recent_entry, lru_list);
 167
 168        /*
 169         * The last time stamp is the most recent.
 170         */
 171        if (time_after(time, e->stamps[e->index-1]))
 172                recent_entry_remove(t, e);
 173}
 174
 175static struct recent_entry *
 176recent_entry_init(struct recent_table *t, const union nf_inet_addr *addr,
 177                  u_int16_t family, u_int8_t ttl)
 178{
 179        struct recent_entry *e;
 180        unsigned int nstamps_max = t->nstamps_max_mask;
 181
 182        if (t->entries >= ip_list_tot) {
 183                e = list_entry(t->lru_list.next, struct recent_entry, lru_list);
 184                recent_entry_remove(t, e);
 185        }
 186
 187        nstamps_max += 1;
 188        e = kmalloc(sizeof(*e) + sizeof(e->stamps[0]) * nstamps_max,
 189                    GFP_ATOMIC);
 190        if (e == NULL)
 191                return NULL;
 192        memcpy(&e->addr, addr, sizeof(e->addr));
 193        e->ttl       = ttl;
 194        e->stamps[0] = jiffies;
 195        e->nstamps   = 1;
 196        e->index     = 1;
 197        e->family    = family;
 198        if (family == NFPROTO_IPV4)
 199                list_add_tail(&e->list, &t->iphash[recent_entry_hash4(addr)]);
 200        else
 201                list_add_tail(&e->list, &t->iphash[recent_entry_hash6(addr)]);
 202        list_add_tail(&e->lru_list, &t->lru_list);
 203        t->entries++;
 204        return e;
 205}
 206
 207static void recent_entry_update(struct recent_table *t, struct recent_entry *e)
 208{
 209        e->index &= t->nstamps_max_mask;
 210        e->stamps[e->index++] = jiffies;
 211        if (e->index > e->nstamps)
 212                e->nstamps = e->index;
 213        list_move_tail(&e->lru_list, &t->lru_list);
 214}
 215
 216static struct recent_table *recent_table_lookup(struct recent_net *recent_net,
 217                                                const char *name)
 218{
 219        struct recent_table *t;
 220
 221        list_for_each_entry(t, &recent_net->tables, list)
 222                if (!strcmp(t->name, name))
 223                        return t;
 224        return NULL;
 225}
 226
 227static void recent_table_flush(struct recent_table *t)
 228{
 229        struct recent_entry *e, *next;
 230        unsigned int i;
 231
 232        for (i = 0; i < ip_list_hash_size; i++)
 233                list_for_each_entry_safe(e, next, &t->iphash[i], list)
 234                        recent_entry_remove(t, e);
 235}
 236
 237static bool
 238recent_mt(const struct sk_buff *skb, struct xt_action_param *par)
 239{
 240        struct net *net = par->net;
 241        struct recent_net *recent_net = recent_pernet(net);
 242        const struct xt_recent_mtinfo_v1 *info = par->matchinfo;
 243        struct recent_table *t;
 244        struct recent_entry *e;
 245        union nf_inet_addr addr = {}, addr_mask;
 246        u_int8_t ttl;
 247        bool ret = info->invert;
 248
 249        if (par->family == NFPROTO_IPV4) {
 250                const struct iphdr *iph = ip_hdr(skb);
 251
 252                if (info->side == XT_RECENT_DEST)
 253                        addr.ip = iph->daddr;
 254                else
 255                        addr.ip = iph->saddr;
 256
 257                ttl = iph->ttl;
 258        } else {
 259                const struct ipv6hdr *iph = ipv6_hdr(skb);
 260
 261                if (info->side == XT_RECENT_DEST)
 262                        memcpy(&addr.in6, &iph->daddr, sizeof(addr.in6));
 263                else
 264                        memcpy(&addr.in6, &iph->saddr, sizeof(addr.in6));
 265
 266                ttl = iph->hop_limit;
 267        }
 268
 269        /* use TTL as seen before forwarding */
 270        if (par->out != NULL && skb->sk == NULL)
 271                ttl++;
 272
 273        spin_lock_bh(&recent_lock);
 274        t = recent_table_lookup(recent_net, info->name);
 275
 276        nf_inet_addr_mask(&addr, &addr_mask, &t->mask);
 277
 278        e = recent_entry_lookup(t, &addr_mask, par->family,
 279                                (info->check_set & XT_RECENT_TTL) ? ttl : 0);
 280        if (e == NULL) {
 281                if (!(info->check_set & XT_RECENT_SET))
 282                        goto out;
 283                e = recent_entry_init(t, &addr_mask, par->family, ttl);
 284                if (e == NULL)
 285                        par->hotdrop = true;
 286                ret = !ret;
 287                goto out;
 288        }
 289
 290        if (info->check_set & XT_RECENT_SET)
 291                ret = !ret;
 292        else if (info->check_set & XT_RECENT_REMOVE) {
 293                recent_entry_remove(t, e);
 294                ret = !ret;
 295        } else if (info->check_set & (XT_RECENT_CHECK | XT_RECENT_UPDATE)) {
 296                unsigned long time = jiffies - info->seconds * HZ;
 297                unsigned int i, hits = 0;
 298
 299                for (i = 0; i < e->nstamps; i++) {
 300                        if (info->seconds && time_after(time, e->stamps[i]))
 301                                continue;
 302                        if (!info->hit_count || ++hits >= info->hit_count) {
 303                                ret = !ret;
 304                                break;
 305                        }
 306                }
 307
 308                /* info->seconds must be non-zero */
 309                if (info->check_set & XT_RECENT_REAP)
 310                        recent_entry_reap(t, time);
 311        }
 312
 313        if (info->check_set & XT_RECENT_SET ||
 314            (info->check_set & XT_RECENT_UPDATE && ret)) {
 315                recent_entry_update(t, e);
 316                e->ttl = ttl;
 317        }
 318out:
 319        spin_unlock_bh(&recent_lock);
 320        return ret;
 321}
 322
 323static void recent_table_free(void *addr)
 324{
 325        kvfree(addr);
 326}
 327
 328static int recent_mt_check(const struct xt_mtchk_param *par,
 329                           const struct xt_recent_mtinfo_v1 *info)
 330{
 331        struct recent_net *recent_net = recent_pernet(par->net);
 332        struct recent_table *t;
 333#ifdef CONFIG_PROC_FS
 334        struct proc_dir_entry *pde;
 335        kuid_t uid;
 336        kgid_t gid;
 337#endif
 338        unsigned int nstamp_mask;
 339        unsigned int i;
 340        int ret = -EINVAL;
 341        size_t sz;
 342
 343        if (unlikely(!hash_rnd_inited)) {
 344                get_random_bytes(&hash_rnd, sizeof(hash_rnd));
 345                hash_rnd_inited = true;
 346        }
 347        if (info->check_set & ~XT_RECENT_VALID_FLAGS) {
 348                pr_info("Unsupported user space flags (%08x)\n",
 349                        info->check_set);
 350                return -EINVAL;
 351        }
 352        if (hweight8(info->check_set &
 353                     (XT_RECENT_SET | XT_RECENT_REMOVE |
 354                      XT_RECENT_CHECK | XT_RECENT_UPDATE)) != 1)
 355                return -EINVAL;
 356        if ((info->check_set & (XT_RECENT_SET | XT_RECENT_REMOVE)) &&
 357            (info->seconds || info->hit_count ||
 358            (info->check_set & XT_RECENT_MODIFIERS)))
 359                return -EINVAL;
 360        if ((info->check_set & XT_RECENT_REAP) && !info->seconds)
 361                return -EINVAL;
 362        if (info->hit_count >= XT_RECENT_MAX_NSTAMPS) {
 363                pr_info("hitcount (%u) is larger than allowed maximum (%u)\n",
 364                        info->hit_count, XT_RECENT_MAX_NSTAMPS - 1);
 365                return -EINVAL;
 366        }
 367        if (info->name[0] == '\0' ||
 368            strnlen(info->name, XT_RECENT_NAME_LEN) == XT_RECENT_NAME_LEN)
 369                return -EINVAL;
 370
 371        if (ip_pkt_list_tot && info->hit_count < ip_pkt_list_tot)
 372                nstamp_mask = roundup_pow_of_two(ip_pkt_list_tot) - 1;
 373        else if (info->hit_count)
 374                nstamp_mask = roundup_pow_of_two(info->hit_count) - 1;
 375        else
 376                nstamp_mask = 32 - 1;
 377
 378        mutex_lock(&recent_mutex);
 379        t = recent_table_lookup(recent_net, info->name);
 380        if (t != NULL) {
 381                if (nstamp_mask > t->nstamps_max_mask) {
 382                        spin_lock_bh(&recent_lock);
 383                        recent_table_flush(t);
 384                        t->nstamps_max_mask = nstamp_mask;
 385                        spin_unlock_bh(&recent_lock);
 386                }
 387
 388                t->refcnt++;
 389                ret = 0;
 390                goto out;
 391        }
 392
 393        sz = sizeof(*t) + sizeof(t->iphash[0]) * ip_list_hash_size;
 394        if (sz <= PAGE_SIZE)
 395                t = kzalloc(sz, GFP_KERNEL);
 396        else
 397                t = vzalloc(sz);
 398        if (t == NULL) {
 399                ret = -ENOMEM;
 400                goto out;
 401        }
 402        t->refcnt = 1;
 403        t->nstamps_max_mask = nstamp_mask;
 404
 405        memcpy(&t->mask, &info->mask, sizeof(t->mask));
 406        strcpy(t->name, info->name);
 407        INIT_LIST_HEAD(&t->lru_list);
 408        for (i = 0; i < ip_list_hash_size; i++)
 409                INIT_LIST_HEAD(&t->iphash[i]);
 410#ifdef CONFIG_PROC_FS
 411        uid = make_kuid(&init_user_ns, ip_list_uid);
 412        gid = make_kgid(&init_user_ns, ip_list_gid);
 413        if (!uid_valid(uid) || !gid_valid(gid)) {
 414                recent_table_free(t);
 415                ret = -EINVAL;
 416                goto out;
 417        }
 418        pde = proc_create_data(t->name, ip_list_perms, recent_net->xt_recent,
 419                  &recent_mt_fops, t);
 420        if (pde == NULL) {
 421                recent_table_free(t);
 422                ret = -ENOMEM;
 423                goto out;
 424        }
 425        proc_set_user(pde, uid, gid);
 426#endif
 427        spin_lock_bh(&recent_lock);
 428        list_add_tail(&t->list, &recent_net->tables);
 429        spin_unlock_bh(&recent_lock);
 430        ret = 0;
 431out:
 432        mutex_unlock(&recent_mutex);
 433        return ret;
 434}
 435
 436static int recent_mt_check_v0(const struct xt_mtchk_param *par)
 437{
 438        const struct xt_recent_mtinfo_v0 *info_v0 = par->matchinfo;
 439        struct xt_recent_mtinfo_v1 info_v1;
 440
 441        /* Copy revision 0 structure to revision 1 */
 442        memcpy(&info_v1, info_v0, sizeof(struct xt_recent_mtinfo));
 443        /* Set default mask to ensure backward compatible behaviour */
 444        memset(info_v1.mask.all, 0xFF, sizeof(info_v1.mask.all));
 445
 446        return recent_mt_check(par, &info_v1);
 447}
 448
 449static int recent_mt_check_v1(const struct xt_mtchk_param *par)
 450{
 451        return recent_mt_check(par, par->matchinfo);
 452}
 453
 454static void recent_mt_destroy(const struct xt_mtdtor_param *par)
 455{
 456        struct recent_net *recent_net = recent_pernet(par->net);
 457        const struct xt_recent_mtinfo_v1 *info = par->matchinfo;
 458        struct recent_table *t;
 459
 460        mutex_lock(&recent_mutex);
 461        t = recent_table_lookup(recent_net, info->name);
 462        if (--t->refcnt == 0) {
 463                spin_lock_bh(&recent_lock);
 464                list_del(&t->list);
 465                spin_unlock_bh(&recent_lock);
 466#ifdef CONFIG_PROC_FS
 467                if (recent_net->xt_recent != NULL)
 468                        remove_proc_entry(t->name, recent_net->xt_recent);
 469#endif
 470                recent_table_flush(t);
 471                recent_table_free(t);
 472        }
 473        mutex_unlock(&recent_mutex);
 474}
 475
 476#ifdef CONFIG_PROC_FS
 477struct recent_iter_state {
 478        const struct recent_table *table;
 479        unsigned int            bucket;
 480};
 481
 482static void *recent_seq_start(struct seq_file *seq, loff_t *pos)
 483        __acquires(recent_lock)
 484{
 485        struct recent_iter_state *st = seq->private;
 486        const struct recent_table *t = st->table;
 487        struct recent_entry *e;
 488        loff_t p = *pos;
 489
 490        spin_lock_bh(&recent_lock);
 491
 492        for (st->bucket = 0; st->bucket < ip_list_hash_size; st->bucket++)
 493                list_for_each_entry(e, &t->iphash[st->bucket], list)
 494                        if (p-- == 0)
 495                                return e;
 496        return NULL;
 497}
 498
 499static void *recent_seq_next(struct seq_file *seq, void *v, loff_t *pos)
 500{
 501        struct recent_iter_state *st = seq->private;
 502        const struct recent_table *t = st->table;
 503        const struct recent_entry *e = v;
 504        const struct list_head *head = e->list.next;
 505
 506        while (head == &t->iphash[st->bucket]) {
 507                if (++st->bucket >= ip_list_hash_size)
 508                        return NULL;
 509                head = t->iphash[st->bucket].next;
 510        }
 511        (*pos)++;
 512        return list_entry(head, struct recent_entry, list);
 513}
 514
 515static void recent_seq_stop(struct seq_file *s, void *v)
 516        __releases(recent_lock)
 517{
 518        spin_unlock_bh(&recent_lock);
 519}
 520
 521static int recent_seq_show(struct seq_file *seq, void *v)
 522{
 523        const struct recent_entry *e = v;
 524        struct recent_iter_state *st = seq->private;
 525        const struct recent_table *t = st->table;
 526        unsigned int i;
 527
 528        i = (e->index - 1) & t->nstamps_max_mask;
 529
 530        if (e->family == NFPROTO_IPV4)
 531                seq_printf(seq, "src=%pI4 ttl: %u last_seen: %lu oldest_pkt: %u",
 532                           &e->addr.ip, e->ttl, e->stamps[i], e->index);
 533        else
 534                seq_printf(seq, "src=%pI6 ttl: %u last_seen: %lu oldest_pkt: %u",
 535                           &e->addr.in6, e->ttl, e->stamps[i], e->index);
 536        for (i = 0; i < e->nstamps; i++)
 537                seq_printf(seq, "%s %lu", i ? "," : "", e->stamps[i]);
 538        seq_printf(seq, "\n");
 539        return 0;
 540}
 541
 542static const struct seq_operations recent_seq_ops = {
 543        .start          = recent_seq_start,
 544        .next           = recent_seq_next,
 545        .stop           = recent_seq_stop,
 546        .show           = recent_seq_show,
 547};
 548
 549static int recent_seq_open(struct inode *inode, struct file *file)
 550{
 551        struct recent_iter_state *st;
 552
 553        st = __seq_open_private(file, &recent_seq_ops, sizeof(*st));
 554        if (st == NULL)
 555                return -ENOMEM;
 556
 557        st->table    = PDE_DATA(inode);
 558        return 0;
 559}
 560
 561static ssize_t
 562recent_mt_proc_write(struct file *file, const char __user *input,
 563                     size_t size, loff_t *loff)
 564{
 565        struct recent_table *t = PDE_DATA(file_inode(file));
 566        struct recent_entry *e;
 567        char buf[sizeof("+b335:1d35:1e55:dead:c0de:1715:5afe:c0de")];
 568        const char *c = buf;
 569        union nf_inet_addr addr = {};
 570        u_int16_t family;
 571        bool add, succ;
 572
 573        if (size == 0)
 574                return 0;
 575        if (size > sizeof(buf))
 576                size = sizeof(buf);
 577        if (copy_from_user(buf, input, size) != 0)
 578                return -EFAULT;
 579
 580        /* Strict protocol! */
 581        if (*loff != 0)
 582                return -ESPIPE;
 583        switch (*c) {
 584        case '/': /* flush table */
 585                spin_lock_bh(&recent_lock);
 586                recent_table_flush(t);
 587                spin_unlock_bh(&recent_lock);
 588                return size;
 589        case '-': /* remove address */
 590                add = false;
 591                break;
 592        case '+': /* add address */
 593                add = true;
 594                break;
 595        default:
 596                pr_info("Need \"+ip\", \"-ip\" or \"/\"\n");
 597                return -EINVAL;
 598        }
 599
 600        ++c;
 601        --size;
 602        if (strnchr(c, size, ':') != NULL) {
 603                family = NFPROTO_IPV6;
 604                succ   = in6_pton(c, size, (void *)&addr, '\n', NULL);
 605        } else {
 606                family = NFPROTO_IPV4;
 607                succ   = in4_pton(c, size, (void *)&addr, '\n', NULL);
 608        }
 609
 610        if (!succ) {
 611                pr_info("illegal address written to procfs\n");
 612                return -EINVAL;
 613        }
 614
 615        spin_lock_bh(&recent_lock);
 616        e = recent_entry_lookup(t, &addr, family, 0);
 617        if (e == NULL) {
 618                if (add)
 619                        recent_entry_init(t, &addr, family, 0);
 620        } else {
 621                if (add)
 622                        recent_entry_update(t, e);
 623                else
 624                        recent_entry_remove(t, e);
 625        }
 626        spin_unlock_bh(&recent_lock);
 627        /* Note we removed one above */
 628        *loff += size + 1;
 629        return size + 1;
 630}
 631
 632static const struct file_operations recent_mt_fops = {
 633        .open    = recent_seq_open,
 634        .read    = seq_read,
 635        .write   = recent_mt_proc_write,
 636        .release = seq_release_private,
 637        .owner   = THIS_MODULE,
 638        .llseek = seq_lseek,
 639};
 640
 641static int __net_init recent_proc_net_init(struct net *net)
 642{
 643        struct recent_net *recent_net = recent_pernet(net);
 644
 645        recent_net->xt_recent = proc_mkdir("xt_recent", net->proc_net);
 646        if (!recent_net->xt_recent)
 647                return -ENOMEM;
 648        return 0;
 649}
 650
 651static void __net_exit recent_proc_net_exit(struct net *net)
 652{
 653        struct recent_net *recent_net = recent_pernet(net);
 654        struct recent_table *t;
 655
 656        /* recent_net_exit() is called before recent_mt_destroy(). Make sure
 657         * that the parent xt_recent proc entry is is empty before trying to
 658         * remove it.
 659         */
 660        spin_lock_bh(&recent_lock);
 661        list_for_each_entry(t, &recent_net->tables, list)
 662                remove_proc_entry(t->name, recent_net->xt_recent);
 663
 664        recent_net->xt_recent = NULL;
 665        spin_unlock_bh(&recent_lock);
 666
 667        remove_proc_entry("xt_recent", net->proc_net);
 668}
 669#else
 670static inline int recent_proc_net_init(struct net *net)
 671{
 672        return 0;
 673}
 674
 675static inline void recent_proc_net_exit(struct net *net)
 676{
 677}
 678#endif /* CONFIG_PROC_FS */
 679
 680static int __net_init recent_net_init(struct net *net)
 681{
 682        struct recent_net *recent_net = recent_pernet(net);
 683
 684        INIT_LIST_HEAD(&recent_net->tables);
 685        return recent_proc_net_init(net);
 686}
 687
 688static void __net_exit recent_net_exit(struct net *net)
 689{
 690        recent_proc_net_exit(net);
 691}
 692
 693static struct pernet_operations recent_net_ops = {
 694        .init   = recent_net_init,
 695        .exit   = recent_net_exit,
 696        .id     = &recent_net_id,
 697        .size   = sizeof(struct recent_net),
 698};
 699
 700static struct xt_match recent_mt_reg[] __read_mostly = {
 701        {
 702                .name       = "recent",
 703                .revision   = 0,
 704                .family     = NFPROTO_IPV4,
 705                .match      = recent_mt,
 706                .matchsize  = sizeof(struct xt_recent_mtinfo),
 707                .checkentry = recent_mt_check_v0,
 708                .destroy    = recent_mt_destroy,
 709                .me         = THIS_MODULE,
 710        },
 711        {
 712                .name       = "recent",
 713                .revision   = 0,
 714                .family     = NFPROTO_IPV6,
 715                .match      = recent_mt,
 716                .matchsize  = sizeof(struct xt_recent_mtinfo),
 717                .checkentry = recent_mt_check_v0,
 718                .destroy    = recent_mt_destroy,
 719                .me         = THIS_MODULE,
 720        },
 721        {
 722                .name       = "recent",
 723                .revision   = 1,
 724                .family     = NFPROTO_IPV4,
 725                .match      = recent_mt,
 726                .matchsize  = sizeof(struct xt_recent_mtinfo_v1),
 727                .checkentry = recent_mt_check_v1,
 728                .destroy    = recent_mt_destroy,
 729                .me         = THIS_MODULE,
 730        },
 731        {
 732                .name       = "recent",
 733                .revision   = 1,
 734                .family     = NFPROTO_IPV6,
 735                .match      = recent_mt,
 736                .matchsize  = sizeof(struct xt_recent_mtinfo_v1),
 737                .checkentry = recent_mt_check_v1,
 738                .destroy    = recent_mt_destroy,
 739                .me         = THIS_MODULE,
 740        }
 741};
 742
 743static int __init recent_mt_init(void)
 744{
 745        int err;
 746
 747        BUILD_BUG_ON_NOT_POWER_OF_2(XT_RECENT_MAX_NSTAMPS);
 748
 749        if (!ip_list_tot || ip_pkt_list_tot >= XT_RECENT_MAX_NSTAMPS)
 750                return -EINVAL;
 751        ip_list_hash_size = 1 << fls(ip_list_tot);
 752
 753        err = register_pernet_subsys(&recent_net_ops);
 754        if (err)
 755                return err;
 756        err = xt_register_matches(recent_mt_reg, ARRAY_SIZE(recent_mt_reg));
 757        if (err)
 758                unregister_pernet_subsys(&recent_net_ops);
 759        return err;
 760}
 761
 762static void __exit recent_mt_exit(void)
 763{
 764        xt_unregister_matches(recent_mt_reg, ARRAY_SIZE(recent_mt_reg));
 765        unregister_pernet_subsys(&recent_net_ops);
 766}
 767
 768module_init(recent_mt_init);
 769module_exit(recent_mt_exit);
 770