linux/net/ipv4/netfilter/ipt_recent.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2006 Patrick McHardy <kaber@trash.net>
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License version 2 as
   6 * published by the Free Software Foundation.
   7 *
   8 * This is a replacement of the old ipt_recent module, which carried the
   9 * following copyright notice:
  10 *
  11 * Author: Stephen Frost <sfrost@snowman.net>
  12 * Copyright 2002-2003, Stephen Frost, 2.5.x port by laforge@netfilter.org
  13 */
  14#include <linux/init.h>
  15#include <linux/ip.h>
  16#include <linux/moduleparam.h>
  17#include <linux/proc_fs.h>
  18#include <linux/seq_file.h>
  19#include <linux/string.h>
  20#include <linux/ctype.h>
  21#include <linux/list.h>
  22#include <linux/random.h>
  23#include <linux/jhash.h>
  24#include <linux/bitops.h>
  25#include <linux/skbuff.h>
  26#include <linux/inet.h>
  27#include <net/net_namespace.h>
  28
  29#include <linux/netfilter/x_tables.h>
  30#include <linux/netfilter_ipv4/ipt_recent.h>
  31
  32MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
  33MODULE_DESCRIPTION("IP tables recently seen matching module");
  34MODULE_LICENSE("GPL");
  35
  36static unsigned int ip_list_tot = 100;
  37static unsigned int ip_pkt_list_tot = 20;
  38static unsigned int ip_list_hash_size = 0;
  39static unsigned int ip_list_perms = 0644;
  40static unsigned int ip_list_uid = 0;
  41static unsigned int ip_list_gid = 0;
  42module_param(ip_list_tot, uint, 0400);
  43module_param(ip_pkt_list_tot, uint, 0400);
  44module_param(ip_list_hash_size, uint, 0400);
  45module_param(ip_list_perms, uint, 0400);
  46module_param(ip_list_uid, uint, 0400);
  47module_param(ip_list_gid, uint, 0400);
  48MODULE_PARM_DESC(ip_list_tot, "number of IPs to remember per list");
  49MODULE_PARM_DESC(ip_pkt_list_tot, "number of packets per IP to remember (max. 255)");
  50MODULE_PARM_DESC(ip_list_hash_size, "size of hash table used to look up IPs");
  51MODULE_PARM_DESC(ip_list_perms, "permissions on /proc/net/ipt_recent/* files");
  52MODULE_PARM_DESC(ip_list_uid,"owner of /proc/net/ipt_recent/* files");
  53MODULE_PARM_DESC(ip_list_gid,"owning group of /proc/net/ipt_recent/* files");
  54
  55struct recent_entry {
  56        struct list_head        list;
  57        struct list_head        lru_list;
  58        __be32                  addr;
  59        u_int8_t                ttl;
  60        u_int8_t                index;
  61        u_int16_t               nstamps;
  62        unsigned long           stamps[0];
  63};
  64
  65struct recent_table {
  66        struct list_head        list;
  67        char                    name[IPT_RECENT_NAME_LEN];
  68#ifdef CONFIG_PROC_FS
  69        struct proc_dir_entry   *proc;
  70#endif
  71        unsigned int            refcnt;
  72        unsigned int            entries;
  73        struct list_head        lru_list;
  74        struct list_head        iphash[0];
  75};
  76
  77static LIST_HEAD(tables);
  78static DEFINE_SPINLOCK(recent_lock);
  79static DEFINE_MUTEX(recent_mutex);
  80
  81#ifdef CONFIG_PROC_FS
  82static struct proc_dir_entry    *proc_dir;
  83static const struct file_operations     recent_fops;
  84#endif
  85
  86static u_int32_t hash_rnd;
  87static int hash_rnd_initted;
  88
  89static unsigned int recent_entry_hash(__be32 addr)
  90{
  91        if (!hash_rnd_initted) {
  92                get_random_bytes(&hash_rnd, 4);
  93                hash_rnd_initted = 1;
  94        }
  95        return jhash_1word((__force u32)addr, hash_rnd) & (ip_list_hash_size - 1);
  96}
  97
  98static struct recent_entry *
  99recent_entry_lookup(const struct recent_table *table, __be32 addr, u_int8_t ttl)
 100{
 101        struct recent_entry *e;
 102        unsigned int h;
 103
 104        h = recent_entry_hash(addr);
 105        list_for_each_entry(e, &table->iphash[h], list)
 106                if (e->addr == addr && (ttl == e->ttl || !ttl || !e->ttl))
 107                        return e;
 108        return NULL;
 109}
 110
 111static void recent_entry_remove(struct recent_table *t, struct recent_entry *e)
 112{
 113        list_del(&e->list);
 114        list_del(&e->lru_list);
 115        kfree(e);
 116        t->entries--;
 117}
 118
 119static struct recent_entry *
 120recent_entry_init(struct recent_table *t, __be32 addr, u_int8_t ttl)
 121{
 122        struct recent_entry *e;
 123
 124        if (t->entries >= ip_list_tot) {
 125                e = list_entry(t->lru_list.next, struct recent_entry, lru_list);
 126                recent_entry_remove(t, e);
 127        }
 128        e = kmalloc(sizeof(*e) + sizeof(e->stamps[0]) * ip_pkt_list_tot,
 129                    GFP_ATOMIC);
 130        if (e == NULL)
 131                return NULL;
 132        e->addr      = addr;
 133        e->ttl       = ttl;
 134        e->stamps[0] = jiffies;
 135        e->nstamps   = 1;
 136        e->index     = 1;
 137        list_add_tail(&e->list, &t->iphash[recent_entry_hash(addr)]);
 138        list_add_tail(&e->lru_list, &t->lru_list);
 139        t->entries++;
 140        return e;
 141}
 142
 143static void recent_entry_update(struct recent_table *t, struct recent_entry *e)
 144{
 145        e->stamps[e->index++] = jiffies;
 146        if (e->index > e->nstamps)
 147                e->nstamps = e->index;
 148        e->index %= ip_pkt_list_tot;
 149        list_move_tail(&e->lru_list, &t->lru_list);
 150}
 151
 152static struct recent_table *recent_table_lookup(const char *name)
 153{
 154        struct recent_table *t;
 155
 156        list_for_each_entry(t, &tables, list)
 157                if (!strcmp(t->name, name))
 158                        return t;
 159        return NULL;
 160}
 161
 162static void recent_table_flush(struct recent_table *t)
 163{
 164        struct recent_entry *e, *next;
 165        unsigned int i;
 166
 167        for (i = 0; i < ip_list_hash_size; i++)
 168                list_for_each_entry_safe(e, next, &t->iphash[i], list)
 169                        recent_entry_remove(t, e);
 170}
 171
 172static bool
 173ipt_recent_match(const struct sk_buff *skb,
 174                 const struct net_device *in, const struct net_device *out,
 175                 const struct xt_match *match, const void *matchinfo,
 176                 int offset, unsigned int protoff, bool *hotdrop)
 177{
 178        const struct ipt_recent_info *info = matchinfo;
 179        struct recent_table *t;
 180        struct recent_entry *e;
 181        __be32 addr;
 182        u_int8_t ttl;
 183        bool ret = info->invert;
 184
 185        if (info->side == IPT_RECENT_DEST)
 186                addr = ip_hdr(skb)->daddr;
 187        else
 188                addr = ip_hdr(skb)->saddr;
 189
 190        ttl = ip_hdr(skb)->ttl;
 191        /* use TTL as seen before forwarding */
 192        if (out && !skb->sk)
 193                ttl++;
 194
 195        spin_lock_bh(&recent_lock);
 196        t = recent_table_lookup(info->name);
 197        e = recent_entry_lookup(t, addr,
 198                                info->check_set & IPT_RECENT_TTL ? ttl : 0);
 199        if (e == NULL) {
 200                if (!(info->check_set & IPT_RECENT_SET))
 201                        goto out;
 202                e = recent_entry_init(t, addr, ttl);
 203                if (e == NULL)
 204                        *hotdrop = true;
 205                ret = !ret;
 206                goto out;
 207        }
 208
 209        if (info->check_set & IPT_RECENT_SET)
 210                ret = !ret;
 211        else if (info->check_set & IPT_RECENT_REMOVE) {
 212                recent_entry_remove(t, e);
 213                ret = !ret;
 214        } else if (info->check_set & (IPT_RECENT_CHECK | IPT_RECENT_UPDATE)) {
 215                unsigned long t = jiffies - info->seconds * HZ;
 216                unsigned int i, hits = 0;
 217
 218                for (i = 0; i < e->nstamps; i++) {
 219                        if (info->seconds && time_after(t, e->stamps[i]))
 220                                continue;
 221                        if (++hits >= info->hit_count) {
 222                                ret = !ret;
 223                                break;
 224                        }
 225                }
 226        }
 227
 228        if (info->check_set & IPT_RECENT_SET ||
 229            (info->check_set & IPT_RECENT_UPDATE && ret)) {
 230                recent_entry_update(t, e);
 231                e->ttl = ttl;
 232        }
 233out:
 234        spin_unlock_bh(&recent_lock);
 235        return ret;
 236}
 237
 238static bool
 239ipt_recent_checkentry(const char *tablename, const void *ip,
 240                      const struct xt_match *match, void *matchinfo,
 241                      unsigned int hook_mask)
 242{
 243        const struct ipt_recent_info *info = matchinfo;
 244        struct recent_table *t;
 245        unsigned i;
 246        bool ret = false;
 247
 248        if (hweight8(info->check_set &
 249                     (IPT_RECENT_SET | IPT_RECENT_REMOVE |
 250                      IPT_RECENT_CHECK | IPT_RECENT_UPDATE)) != 1)
 251                return false;
 252        if ((info->check_set & (IPT_RECENT_SET | IPT_RECENT_REMOVE)) &&
 253            (info->seconds || info->hit_count))
 254                return false;
 255        if (info->name[0] == '\0' ||
 256            strnlen(info->name, IPT_RECENT_NAME_LEN) == IPT_RECENT_NAME_LEN)
 257                return false;
 258
 259        mutex_lock(&recent_mutex);
 260        t = recent_table_lookup(info->name);
 261        if (t != NULL) {
 262                t->refcnt++;
 263                ret = true;
 264                goto out;
 265        }
 266
 267        t = kzalloc(sizeof(*t) + sizeof(t->iphash[0]) * ip_list_hash_size,
 268                    GFP_KERNEL);
 269        if (t == NULL)
 270                goto out;
 271        t->refcnt = 1;
 272        strcpy(t->name, info->name);
 273        INIT_LIST_HEAD(&t->lru_list);
 274        for (i = 0; i < ip_list_hash_size; i++)
 275                INIT_LIST_HEAD(&t->iphash[i]);
 276#ifdef CONFIG_PROC_FS
 277        t->proc = create_proc_entry(t->name, ip_list_perms, proc_dir);
 278        if (t->proc == NULL) {
 279                kfree(t);
 280                goto out;
 281        }
 282        t->proc->proc_fops = &recent_fops;
 283        t->proc->uid       = ip_list_uid;
 284        t->proc->gid       = ip_list_gid;
 285        t->proc->data      = t;
 286#endif
 287        spin_lock_bh(&recent_lock);
 288        list_add_tail(&t->list, &tables);
 289        spin_unlock_bh(&recent_lock);
 290        ret = true;
 291out:
 292        mutex_unlock(&recent_mutex);
 293        return ret;
 294}
 295
 296static void
 297ipt_recent_destroy(const struct xt_match *match, void *matchinfo)
 298{
 299        const struct ipt_recent_info *info = matchinfo;
 300        struct recent_table *t;
 301
 302        mutex_lock(&recent_mutex);
 303        t = recent_table_lookup(info->name);
 304        if (--t->refcnt == 0) {
 305                spin_lock_bh(&recent_lock);
 306                list_del(&t->list);
 307                spin_unlock_bh(&recent_lock);
 308                recent_table_flush(t);
 309#ifdef CONFIG_PROC_FS
 310                remove_proc_entry(t->name, proc_dir);
 311#endif
 312                kfree(t);
 313        }
 314        mutex_unlock(&recent_mutex);
 315}
 316
 317#ifdef CONFIG_PROC_FS
 318struct recent_iter_state {
 319        struct recent_table     *table;
 320        unsigned int            bucket;
 321};
 322
 323static void *recent_seq_start(struct seq_file *seq, loff_t *pos)
 324{
 325        struct recent_iter_state *st = seq->private;
 326        const struct recent_table *t = st->table;
 327        struct recent_entry *e;
 328        loff_t p = *pos;
 329
 330        spin_lock_bh(&recent_lock);
 331
 332        for (st->bucket = 0; st->bucket < ip_list_hash_size; st->bucket++)
 333                list_for_each_entry(e, &t->iphash[st->bucket], list)
 334                        if (p-- == 0)
 335                                return e;
 336        return NULL;
 337}
 338
 339static void *recent_seq_next(struct seq_file *seq, void *v, loff_t *pos)
 340{
 341        struct recent_iter_state *st = seq->private;
 342        struct recent_table *t = st->table;
 343        struct recent_entry *e = v;
 344        struct list_head *head = e->list.next;
 345
 346        while (head == &t->iphash[st->bucket]) {
 347                if (++st->bucket >= ip_list_hash_size)
 348                        return NULL;
 349                head = t->iphash[st->bucket].next;
 350        }
 351        (*pos)++;
 352        return list_entry(head, struct recent_entry, list);
 353}
 354
 355static void recent_seq_stop(struct seq_file *s, void *v)
 356{
 357        spin_unlock_bh(&recent_lock);
 358}
 359
 360static int recent_seq_show(struct seq_file *seq, void *v)
 361{
 362        struct recent_entry *e = v;
 363        unsigned int i;
 364
 365        i = (e->index - 1) % ip_pkt_list_tot;
 366        seq_printf(seq, "src=%u.%u.%u.%u ttl: %u last_seen: %lu oldest_pkt: %u",
 367                   NIPQUAD(e->addr), e->ttl, e->stamps[i], e->index);
 368        for (i = 0; i < e->nstamps; i++)
 369                seq_printf(seq, "%s %lu", i ? "," : "", e->stamps[i]);
 370        seq_printf(seq, "\n");
 371        return 0;
 372}
 373
 374static const struct seq_operations recent_seq_ops = {
 375        .start          = recent_seq_start,
 376        .next           = recent_seq_next,
 377        .stop           = recent_seq_stop,
 378        .show           = recent_seq_show,
 379};
 380
 381static int recent_seq_open(struct inode *inode, struct file *file)
 382{
 383        struct proc_dir_entry *pde = PDE(inode);
 384        struct recent_iter_state *st;
 385
 386        st = __seq_open_private(file, &recent_seq_ops, sizeof(*st));
 387        if (st == NULL)
 388                return -ENOMEM;
 389
 390        st->table    = pde->data;
 391        return 0;
 392}
 393
 394static ssize_t recent_proc_write(struct file *file, const char __user *input,
 395                                 size_t size, loff_t *loff)
 396{
 397        struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode);
 398        struct recent_table *t = pde->data;
 399        struct recent_entry *e;
 400        char buf[sizeof("+255.255.255.255")], *c = buf;
 401        __be32 addr;
 402        int add;
 403
 404        if (size > sizeof(buf))
 405                size = sizeof(buf);
 406        if (copy_from_user(buf, input, size))
 407                return -EFAULT;
 408        while (isspace(*c))
 409                c++;
 410
 411        if (size - (c - buf) < 5)
 412                return c - buf;
 413        if (!strncmp(c, "clear", 5)) {
 414                c += 5;
 415                spin_lock_bh(&recent_lock);
 416                recent_table_flush(t);
 417                spin_unlock_bh(&recent_lock);
 418                return c - buf;
 419        }
 420
 421        switch (*c) {
 422        case '-':
 423                add = 0;
 424                c++;
 425                break;
 426        case '+':
 427                c++;
 428        default:
 429                add = 1;
 430                break;
 431        }
 432        addr = in_aton(c);
 433
 434        spin_lock_bh(&recent_lock);
 435        e = recent_entry_lookup(t, addr, 0);
 436        if (e == NULL) {
 437                if (add)
 438                        recent_entry_init(t, addr, 0);
 439        } else {
 440                if (add)
 441                        recent_entry_update(t, e);
 442                else
 443                        recent_entry_remove(t, e);
 444        }
 445        spin_unlock_bh(&recent_lock);
 446        return size;
 447}
 448
 449static const struct file_operations recent_fops = {
 450        .open           = recent_seq_open,
 451        .read           = seq_read,
 452        .write          = recent_proc_write,
 453        .release        = seq_release_private,
 454        .owner          = THIS_MODULE,
 455};
 456#endif /* CONFIG_PROC_FS */
 457
 458static struct xt_match recent_match __read_mostly = {
 459        .name           = "recent",
 460        .family         = AF_INET,
 461        .match          = ipt_recent_match,
 462        .matchsize      = sizeof(struct ipt_recent_info),
 463        .checkentry     = ipt_recent_checkentry,
 464        .destroy        = ipt_recent_destroy,
 465        .me             = THIS_MODULE,
 466};
 467
 468static int __init ipt_recent_init(void)
 469{
 470        int err;
 471
 472        if (!ip_list_tot || !ip_pkt_list_tot || ip_pkt_list_tot > 255)
 473                return -EINVAL;
 474        ip_list_hash_size = 1 << fls(ip_list_tot);
 475
 476        err = xt_register_match(&recent_match);
 477#ifdef CONFIG_PROC_FS
 478        if (err)
 479                return err;
 480        proc_dir = proc_mkdir("ipt_recent", init_net.proc_net);
 481        if (proc_dir == NULL) {
 482                xt_unregister_match(&recent_match);
 483                err = -ENOMEM;
 484        }
 485#endif
 486        return err;
 487}
 488
 489static void __exit ipt_recent_exit(void)
 490{
 491        BUG_ON(!list_empty(&tables));
 492        xt_unregister_match(&recent_match);
 493#ifdef CONFIG_PROC_FS
 494        remove_proc_entry("ipt_recent", init_net.proc_net);
 495#endif
 496}
 497
 498module_init(ipt_recent_init);
 499module_exit(ipt_recent_exit);
 500