netfilter: xtables: compute exact size needed for jumpstack
authorFlorian Westphal <fw@strlen.de>
Tue, 14 Jul 2015 15:51:06 +0000 (17:51 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Wed, 15 Jul 2015 16:18:04 +0000 (18:18 +0200)
The {arp,ip,ip6tables} jump stack is currently sized based
on the number of user chains.

However, its rather unlikely that every user defined chain jumps to the
next, so lets use the existing loop detection logic to also track the
chain depths.

The stacksize is then set to the largest chain depth seen.

Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
net/ipv4/netfilter/arp_tables.c
net/ipv4/netfilter/ip_tables.c
net/ipv6/netfilter/ip6_tables.c
net/netfilter/x_tables.c

index 92305a1a021a7936206f9078008532bc151ec757..ae6d0a1242133202f3515e59d39a140fcd7a28ba 100644 (file)
@@ -372,10 +372,13 @@ static inline bool unconditional(const struct arpt_arp *arp)
 
 /* Figures out from what hook each rule can be called: returns 0 if
  * there are loops.  Puts hook bitmask in comefrom.
+ *
+ * Keeps track of largest call depth seen and stores it in newinfo->stacksize.
  */
-static int mark_source_chains(const struct xt_table_info *newinfo,
+static int mark_source_chains(struct xt_table_info *newinfo,
                              unsigned int valid_hooks, void *entry0)
 {
+       unsigned int calldepth, max_calldepth = 0;
        unsigned int hook;
 
        /* No recursion; use packet counter to save back ptrs (reset
@@ -391,6 +394,7 @@ static int mark_source_chains(const struct xt_table_info *newinfo,
 
                /* Set initial back pointer. */
                e->counters.pcnt = pos;
+               calldepth = 0;
 
                for (;;) {
                        const struct xt_standard_target *t
@@ -445,6 +449,8 @@ static int mark_source_chains(const struct xt_table_info *newinfo,
                                        (entry0 + pos + size);
                                e->counters.pcnt = pos;
                                pos += size;
+                               if (calldepth > 0)
+                                       --calldepth;
                        } else {
                                int newpos = t->verdict;
 
@@ -459,6 +465,10 @@ static int mark_source_chains(const struct xt_table_info *newinfo,
                                                return 0;
                                        }
 
+                                       if (entry0 + newpos != arpt_next_entry(e) &&
+                                           ++calldepth > max_calldepth)
+                                               max_calldepth = calldepth;
+
                                        /* This a jump; chase it. */
                                        duprintf("Jump rule %u -> %u\n",
                                                 pos, newpos);
@@ -475,6 +485,7 @@ static int mark_source_chains(const struct xt_table_info *newinfo,
                next:
                duprintf("Finished chain %u\n", hook);
        }
+       newinfo->stacksize = max_calldepth;
        return 1;
 }
 
@@ -664,9 +675,6 @@ static int translate_table(struct xt_table_info *newinfo, void *entry0,
                if (ret != 0)
                        break;
                ++i;
-               if (strcmp(arpt_get_target(iter)->u.user.name,
-                   XT_ERROR_TARGET) == 0)
-                       ++newinfo->stacksize;
        }
        duprintf("translate_table: ARPT_ENTRY_ITERATE gives %d\n", ret);
        if (ret != 0)
@@ -1439,9 +1447,6 @@ static int translate_compat_table(const char *name,
                        break;
                }
                ++i;
-               if (strcmp(arpt_get_target(iter1)->u.user.name,
-                   XT_ERROR_TARGET) == 0)
-                       ++newinfo->stacksize;
        }
        if (ret) {
                /*
index 6c72fbb7b49eb97d3574fd8c10174f9580752911..5e44b35a8de82857d74c9e396c9a616ddedfbef3 100644 (file)
@@ -439,11 +439,15 @@ ipt_do_table(struct sk_buff *skb,
 }
 
 /* Figures out from what hook each rule can be called: returns 0 if
-   there are loops.  Puts hook bitmask in comefrom. */
+ * there are loops.  Puts hook bitmask in comefrom.
+ *
+ * Keeps track of largest call depth seen and stores it in newinfo->stacksize.
+ */
 static int
-mark_source_chains(const struct xt_table_info *newinfo,
+mark_source_chains(struct xt_table_info *newinfo,
                   unsigned int valid_hooks, void *entry0)
 {
+       unsigned int calldepth, max_calldepth = 0;
        unsigned int hook;
 
        /* No recursion; use packet counter to save back ptrs (reset
@@ -457,6 +461,7 @@ mark_source_chains(const struct xt_table_info *newinfo,
 
                /* Set initial back pointer. */
                e->counters.pcnt = pos;
+               calldepth = 0;
 
                for (;;) {
                        const struct xt_standard_target *t
@@ -518,6 +523,9 @@ mark_source_chains(const struct xt_table_info *newinfo,
                                        (entry0 + pos + size);
                                e->counters.pcnt = pos;
                                pos += size;
+                               WARN_ON_ONCE(calldepth == 0);
+                               if (calldepth > 0)
+                                       --calldepth;
                        } else {
                                int newpos = t->verdict;
 
@@ -531,9 +539,14 @@ mark_source_chains(const struct xt_table_info *newinfo,
                                                                newpos);
                                                return 0;
                                        }
+                                       if (entry0 + newpos != ipt_next_entry(e) &&
+                                           !(e->ip.flags & IPT_F_GOTO) &&
+                                           ++calldepth > max_calldepth)
+                                               max_calldepth = calldepth;
+
                                        /* This a jump; chase it. */
-                                       duprintf("Jump rule %u -> %u\n",
-                                                pos, newpos);
+                                       duprintf("Jump rule %u -> %u, calldepth %d\n",
+                                                pos, newpos, calldepth);
                                } else {
                                        /* ... this is a fallthru */
                                        newpos = pos + e->next_offset;
@@ -547,6 +560,7 @@ mark_source_chains(const struct xt_table_info *newinfo,
                next:
                duprintf("Finished chain %u\n", hook);
        }
+       newinfo->stacksize = max_calldepth;
        return 1;
 }
 
@@ -826,9 +840,6 @@ translate_table(struct net *net, struct xt_table_info *newinfo, void *entry0,
                if (ret != 0)
                        return ret;
                ++i;
-               if (strcmp(ipt_get_target(iter)->u.user.name,
-                   XT_ERROR_TARGET) == 0)
-                       ++newinfo->stacksize;
        }
 
        if (i != repl->num_entries) {
@@ -1744,9 +1755,6 @@ translate_compat_table(struct net *net,
                if (ret != 0)
                        break;
                ++i;
-               if (strcmp(ipt_get_target(iter1)->u.user.name,
-                   XT_ERROR_TARGET) == 0)
-                       ++newinfo->stacksize;
        }
        if (ret) {
                /*
index 3c35ced39b42b48d3b81a2bfb8e448bd2dc02e17..baf03217991826ac874741c00174f00e6a7f5c22 100644 (file)
@@ -452,11 +452,15 @@ ip6t_do_table(struct sk_buff *skb,
 }
 
 /* Figures out from what hook each rule can be called: returns 0 if
-   there are loops.  Puts hook bitmask in comefrom. */
+ * there are loops.  Puts hook bitmask in comefrom.
+ *
+ * Keeps track of largest call depth seen and stores it in newinfo->stacksize.
+ */
 static int
-mark_source_chains(const struct xt_table_info *newinfo,
+mark_source_chains(struct xt_table_info *newinfo,
                   unsigned int valid_hooks, void *entry0)
 {
+       unsigned int calldepth, max_calldepth = 0;
        unsigned int hook;
 
        /* No recursion; use packet counter to save back ptrs (reset
@@ -470,6 +474,7 @@ mark_source_chains(const struct xt_table_info *newinfo,
 
                /* Set initial back pointer. */
                e->counters.pcnt = pos;
+               calldepth = 0;
 
                for (;;) {
                        const struct xt_standard_target *t
@@ -531,6 +536,8 @@ mark_source_chains(const struct xt_table_info *newinfo,
                                        (entry0 + pos + size);
                                e->counters.pcnt = pos;
                                pos += size;
+                               if (calldepth > 0)
+                                       --calldepth;
                        } else {
                                int newpos = t->verdict;
 
@@ -544,6 +551,11 @@ mark_source_chains(const struct xt_table_info *newinfo,
                                                                newpos);
                                                return 0;
                                        }
+                                       if (entry0 + newpos != ip6t_next_entry(e) &&
+                                           !(e->ipv6.flags & IP6T_F_GOTO) &&
+                                           ++calldepth > max_calldepth)
+                                               max_calldepth = calldepth;
+
                                        /* This a jump; chase it. */
                                        duprintf("Jump rule %u -> %u\n",
                                                 pos, newpos);
@@ -560,6 +572,7 @@ mark_source_chains(const struct xt_table_info *newinfo,
                next:
                duprintf("Finished chain %u\n", hook);
        }
+       newinfo->stacksize = max_calldepth;
        return 1;
 }
 
@@ -839,9 +852,6 @@ translate_table(struct net *net, struct xt_table_info *newinfo, void *entry0,
                if (ret != 0)
                        return ret;
                ++i;
-               if (strcmp(ip6t_get_target(iter)->u.user.name,
-                   XT_ERROR_TARGET) == 0)
-                       ++newinfo->stacksize;
        }
 
        if (i != repl->num_entries) {
@@ -1754,9 +1764,6 @@ translate_compat_table(struct net *net,
                if (ret != 0)
                        break;
                ++i;
-               if (strcmp(ip6t_get_target(iter1)->u.user.name,
-                   XT_ERROR_TARGET) == 0)
-                       ++newinfo->stacksize;
        }
        if (ret) {
                /*
index d324fe71260c9f24b02507e4f429c0ba1e328d98..4db7d60d42faeb8a4d6bbd9ce1e6f8040eb0d838 100644 (file)
@@ -749,6 +749,10 @@ static int xt_jumpstack_alloc(struct xt_table_info *i)
        if (i->jumpstack == NULL)
                return -ENOMEM;
 
+       /* ruleset without jumps -- no stack needed */
+       if (i->stacksize == 0)
+               return 0;
+
        i->stacksize *= xt_jumpstack_multiplier;
        size = sizeof(void *) * i->stacksize;
        for_each_possible_cpu(cpu) {