Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[cascardo/linux.git] / net / netfilter / xt_recent.c
index a9faae8..30dbe34 100644 (file)
@@ -43,25 +43,29 @@ MODULE_LICENSE("GPL");
 MODULE_ALIAS("ipt_recent");
 MODULE_ALIAS("ip6t_recent");
 
-static unsigned int ip_list_tot = 100;
-static unsigned int ip_pkt_list_tot = 20;
-static unsigned int ip_list_hash_size = 0;
-static unsigned int ip_list_perms = 0644;
-static unsigned int ip_list_uid = 0;
-static unsigned int ip_list_gid = 0;
+static unsigned int ip_list_tot __read_mostly = 100;
+static unsigned int ip_list_hash_size __read_mostly;
+static unsigned int ip_list_perms __read_mostly = 0644;
+static unsigned int ip_list_uid __read_mostly;
+static unsigned int ip_list_gid __read_mostly;
 module_param(ip_list_tot, uint, 0400);
-module_param(ip_pkt_list_tot, uint, 0400);
 module_param(ip_list_hash_size, uint, 0400);
 module_param(ip_list_perms, uint, 0400);
 module_param(ip_list_uid, uint, S_IRUGO | S_IWUSR);
 module_param(ip_list_gid, uint, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(ip_list_tot, "number of IPs to remember per list");
-MODULE_PARM_DESC(ip_pkt_list_tot, "number of packets per IP address to remember (max. 255)");
 MODULE_PARM_DESC(ip_list_hash_size, "size of hash table used to look up IPs");
 MODULE_PARM_DESC(ip_list_perms, "permissions on /proc/net/xt_recent/* files");
 MODULE_PARM_DESC(ip_list_uid, "default owner of /proc/net/xt_recent/* files");
 MODULE_PARM_DESC(ip_list_gid, "default owning group of /proc/net/xt_recent/* files");
 
+/* retained for backwards compatibility */
+static unsigned int ip_pkt_list_tot __read_mostly;
+module_param(ip_pkt_list_tot, uint, 0400);
+MODULE_PARM_DESC(ip_pkt_list_tot, "number of packets per IP address to remember (max. 255)");
+
+#define XT_RECENT_MAX_NSTAMPS  256
+
 struct recent_entry {
        struct list_head        list;
        struct list_head        lru_list;
@@ -79,6 +83,7 @@ struct recent_table {
        union nf_inet_addr      mask;
        unsigned int            refcnt;
        unsigned int            entries;
+       u8                      nstamps_max_mask;
        struct list_head        lru_list;
        struct list_head        iphash[0];
 };
@@ -90,7 +95,8 @@ struct recent_net {
 #endif
 };
 
-static int recent_net_id;
+static int recent_net_id __read_mostly;
+
 static inline struct recent_net *recent_pernet(struct net *net)
 {
        return net_generic(net, recent_net_id);
@@ -171,12 +177,15 @@ recent_entry_init(struct recent_table *t, const union nf_inet_addr *addr,
                  u_int16_t family, u_int8_t ttl)
 {
        struct recent_entry *e;
+       unsigned int nstamps_max = t->nstamps_max_mask;
 
        if (t->entries >= ip_list_tot) {
                e = list_entry(t->lru_list.next, struct recent_entry, lru_list);
                recent_entry_remove(t, e);
        }
-       e = kmalloc(sizeof(*e) + sizeof(e->stamps[0]) * ip_pkt_list_tot,
+
+       nstamps_max += 1;
+       e = kmalloc(sizeof(*e) + sizeof(e->stamps[0]) * nstamps_max,
                    GFP_ATOMIC);
        if (e == NULL)
                return NULL;
@@ -197,7 +206,7 @@ recent_entry_init(struct recent_table *t, const union nf_inet_addr *addr,
 
 static void recent_entry_update(struct recent_table *t, struct recent_entry *e)
 {
-       e->index %= ip_pkt_list_tot;
+       e->index &= t->nstamps_max_mask;
        e->stamps[e->index++] = jiffies;
        if (e->index > e->nstamps)
                e->nstamps = e->index;
@@ -326,6 +335,7 @@ static int recent_mt_check(const struct xt_mtchk_param *par,
        kuid_t uid;
        kgid_t gid;
 #endif
+       unsigned int nstamp_mask;
        unsigned int i;
        int ret = -EINVAL;
        size_t sz;
@@ -349,19 +359,33 @@ static int recent_mt_check(const struct xt_mtchk_param *par,
                return -EINVAL;
        if ((info->check_set & XT_RECENT_REAP) && !info->seconds)
                return -EINVAL;
-       if (info->hit_count > ip_pkt_list_tot) {
-               pr_info("hitcount (%u) is larger than "
-                       "packets to be remembered (%u)\n",
-                       info->hit_count, ip_pkt_list_tot);
+       if (info->hit_count >= XT_RECENT_MAX_NSTAMPS) {
+               pr_info("hitcount (%u) is larger than allowed maximum (%u)\n",
+                       info->hit_count, XT_RECENT_MAX_NSTAMPS - 1);
                return -EINVAL;
        }
        if (info->name[0] == '\0' ||
            strnlen(info->name, XT_RECENT_NAME_LEN) == XT_RECENT_NAME_LEN)
                return -EINVAL;
 
+       if (ip_pkt_list_tot && info->hit_count < ip_pkt_list_tot)
+               nstamp_mask = roundup_pow_of_two(ip_pkt_list_tot) - 1;
+       else if (info->hit_count)
+               nstamp_mask = roundup_pow_of_two(info->hit_count) - 1;
+       else
+               nstamp_mask = 32 - 1;
+
        mutex_lock(&recent_mutex);
        t = recent_table_lookup(recent_net, info->name);
        if (t != NULL) {
+               if (info->hit_count > t->nstamps_max_mask) {
+                       pr_info("hitcount (%u) is larger than packets to be remembered (%u) for table %s\n",
+                               info->hit_count, t->nstamps_max_mask + 1,
+                               info->name);
+                       ret = -EINVAL;
+                       goto out;
+               }
+
                t->refcnt++;
                ret = 0;
                goto out;
@@ -377,6 +401,7 @@ static int recent_mt_check(const struct xt_mtchk_param *par,
                goto out;
        }
        t->refcnt = 1;
+       t->nstamps_max_mask = nstamp_mask;
 
        memcpy(&t->mask, &info->mask, sizeof(t->mask));
        strcpy(t->name, info->name);
@@ -497,9 +522,12 @@ static void recent_seq_stop(struct seq_file *s, void *v)
 static int recent_seq_show(struct seq_file *seq, void *v)
 {
        const struct recent_entry *e = v;
+       struct recent_iter_state *st = seq->private;
+       const struct recent_table *t = st->table;
        unsigned int i;
 
-       i = (e->index - 1) % ip_pkt_list_tot;
+       i = (e->index - 1) & t->nstamps_max_mask;
+
        if (e->family == NFPROTO_IPV4)
                seq_printf(seq, "src=%pI4 ttl: %u last_seen: %lu oldest_pkt: %u",
                           &e->addr.ip, e->ttl, e->stamps[i], e->index);
@@ -717,7 +745,9 @@ static int __init recent_mt_init(void)
 {
        int err;
 
-       if (!ip_list_tot || !ip_pkt_list_tot || ip_pkt_list_tot > 255)
+       BUILD_BUG_ON_NOT_POWER_OF_2(XT_RECENT_MAX_NSTAMPS);
+
+       if (!ip_list_tot || ip_pkt_list_tot >= XT_RECENT_MAX_NSTAMPS)
                return -EINVAL;
        ip_list_hash_size = 1 << fls(ip_list_tot);