Commit 98d1bd80 authored by Florian Westphal's avatar Florian Westphal Committed by Pablo Neira Ayuso

netfilter: xtables: compute exact size needed for jumpstack

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: default avatarFlorian Westphal <fw@strlen.de>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent fd2ecda0
...@@ -372,10 +372,13 @@ static inline bool unconditional(const struct arpt_arp *arp) ...@@ -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 /* 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, static int mark_source_chains(struct xt_table_info *newinfo,
unsigned int valid_hooks, void *entry0) unsigned int valid_hooks, void *entry0)
{ {
unsigned int calldepth, max_calldepth = 0;
unsigned int hook; unsigned int hook;
/* No recursion; use packet counter to save back ptrs (reset /* 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, ...@@ -391,6 +394,7 @@ static int mark_source_chains(const struct xt_table_info *newinfo,
/* Set initial back pointer. */ /* Set initial back pointer. */
e->counters.pcnt = pos; e->counters.pcnt = pos;
calldepth = 0;
for (;;) { for (;;) {
const struct xt_standard_target *t const struct xt_standard_target *t
...@@ -445,6 +449,8 @@ static int mark_source_chains(const struct xt_table_info *newinfo, ...@@ -445,6 +449,8 @@ static int mark_source_chains(const struct xt_table_info *newinfo,
(entry0 + pos + size); (entry0 + pos + size);
e->counters.pcnt = pos; e->counters.pcnt = pos;
pos += size; pos += size;
if (calldepth > 0)
--calldepth;
} else { } else {
int newpos = t->verdict; int newpos = t->verdict;
...@@ -459,6 +465,10 @@ static int mark_source_chains(const struct xt_table_info *newinfo, ...@@ -459,6 +465,10 @@ static int mark_source_chains(const struct xt_table_info *newinfo,
return 0; return 0;
} }
if (entry0 + newpos != arpt_next_entry(e) &&
++calldepth > max_calldepth)
max_calldepth = calldepth;
/* This a jump; chase it. */ /* This a jump; chase it. */
duprintf("Jump rule %u -> %u\n", duprintf("Jump rule %u -> %u\n",
pos, newpos); pos, newpos);
...@@ -475,6 +485,7 @@ static int mark_source_chains(const struct xt_table_info *newinfo, ...@@ -475,6 +485,7 @@ static int mark_source_chains(const struct xt_table_info *newinfo,
next: next:
duprintf("Finished chain %u\n", hook); duprintf("Finished chain %u\n", hook);
} }
newinfo->stacksize = max_calldepth;
return 1; return 1;
} }
...@@ -664,9 +675,6 @@ static int translate_table(struct xt_table_info *newinfo, void *entry0, ...@@ -664,9 +675,6 @@ static int translate_table(struct xt_table_info *newinfo, void *entry0,
if (ret != 0) if (ret != 0)
break; break;
++i; ++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); duprintf("translate_table: ARPT_ENTRY_ITERATE gives %d\n", ret);
if (ret != 0) if (ret != 0)
...@@ -1439,9 +1447,6 @@ static int translate_compat_table(const char *name, ...@@ -1439,9 +1447,6 @@ static int translate_compat_table(const char *name,
break; break;
} }
++i; ++i;
if (strcmp(arpt_get_target(iter1)->u.user.name,
XT_ERROR_TARGET) == 0)
++newinfo->stacksize;
} }
if (ret) { if (ret) {
/* /*
......
...@@ -439,11 +439,15 @@ ipt_do_table(struct sk_buff *skb, ...@@ -439,11 +439,15 @@ ipt_do_table(struct sk_buff *skb,
} }
/* Figures out from what hook each rule can be called: returns 0 if /* 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 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 valid_hooks, void *entry0)
{ {
unsigned int calldepth, max_calldepth = 0;
unsigned int hook; unsigned int hook;
/* No recursion; use packet counter to save back ptrs (reset /* No recursion; use packet counter to save back ptrs (reset
...@@ -457,6 +461,7 @@ mark_source_chains(const struct xt_table_info *newinfo, ...@@ -457,6 +461,7 @@ mark_source_chains(const struct xt_table_info *newinfo,
/* Set initial back pointer. */ /* Set initial back pointer. */
e->counters.pcnt = pos; e->counters.pcnt = pos;
calldepth = 0;
for (;;) { for (;;) {
const struct xt_standard_target *t const struct xt_standard_target *t
...@@ -518,6 +523,9 @@ mark_source_chains(const struct xt_table_info *newinfo, ...@@ -518,6 +523,9 @@ mark_source_chains(const struct xt_table_info *newinfo,
(entry0 + pos + size); (entry0 + pos + size);
e->counters.pcnt = pos; e->counters.pcnt = pos;
pos += size; pos += size;
WARN_ON_ONCE(calldepth == 0);
if (calldepth > 0)
--calldepth;
} else { } else {
int newpos = t->verdict; int newpos = t->verdict;
...@@ -531,9 +539,14 @@ mark_source_chains(const struct xt_table_info *newinfo, ...@@ -531,9 +539,14 @@ mark_source_chains(const struct xt_table_info *newinfo,
newpos); newpos);
return 0; 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. */ /* This a jump; chase it. */
duprintf("Jump rule %u -> %u\n", duprintf("Jump rule %u -> %u, calldepth %d\n",
pos, newpos); pos, newpos, calldepth);
} else { } else {
/* ... this is a fallthru */ /* ... this is a fallthru */
newpos = pos + e->next_offset; newpos = pos + e->next_offset;
...@@ -547,6 +560,7 @@ mark_source_chains(const struct xt_table_info *newinfo, ...@@ -547,6 +560,7 @@ mark_source_chains(const struct xt_table_info *newinfo,
next: next:
duprintf("Finished chain %u\n", hook); duprintf("Finished chain %u\n", hook);
} }
newinfo->stacksize = max_calldepth;
return 1; return 1;
} }
...@@ -826,9 +840,6 @@ translate_table(struct net *net, struct xt_table_info *newinfo, void *entry0, ...@@ -826,9 +840,6 @@ translate_table(struct net *net, struct xt_table_info *newinfo, void *entry0,
if (ret != 0) if (ret != 0)
return ret; return ret;
++i; ++i;
if (strcmp(ipt_get_target(iter)->u.user.name,
XT_ERROR_TARGET) == 0)
++newinfo->stacksize;
} }
if (i != repl->num_entries) { if (i != repl->num_entries) {
...@@ -1744,9 +1755,6 @@ translate_compat_table(struct net *net, ...@@ -1744,9 +1755,6 @@ translate_compat_table(struct net *net,
if (ret != 0) if (ret != 0)
break; break;
++i; ++i;
if (strcmp(ipt_get_target(iter1)->u.user.name,
XT_ERROR_TARGET) == 0)
++newinfo->stacksize;
} }
if (ret) { if (ret) {
/* /*
......
...@@ -452,11 +452,15 @@ ip6t_do_table(struct sk_buff *skb, ...@@ -452,11 +452,15 @@ ip6t_do_table(struct sk_buff *skb,
} }
/* Figures out from what hook each rule can be called: returns 0 if /* 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 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 valid_hooks, void *entry0)
{ {
unsigned int calldepth, max_calldepth = 0;
unsigned int hook; unsigned int hook;
/* No recursion; use packet counter to save back ptrs (reset /* No recursion; use packet counter to save back ptrs (reset
...@@ -470,6 +474,7 @@ mark_source_chains(const struct xt_table_info *newinfo, ...@@ -470,6 +474,7 @@ mark_source_chains(const struct xt_table_info *newinfo,
/* Set initial back pointer. */ /* Set initial back pointer. */
e->counters.pcnt = pos; e->counters.pcnt = pos;
calldepth = 0;
for (;;) { for (;;) {
const struct xt_standard_target *t const struct xt_standard_target *t
...@@ -531,6 +536,8 @@ mark_source_chains(const struct xt_table_info *newinfo, ...@@ -531,6 +536,8 @@ mark_source_chains(const struct xt_table_info *newinfo,
(entry0 + pos + size); (entry0 + pos + size);
e->counters.pcnt = pos; e->counters.pcnt = pos;
pos += size; pos += size;
if (calldepth > 0)
--calldepth;
} else { } else {
int newpos = t->verdict; int newpos = t->verdict;
...@@ -544,6 +551,11 @@ mark_source_chains(const struct xt_table_info *newinfo, ...@@ -544,6 +551,11 @@ mark_source_chains(const struct xt_table_info *newinfo,
newpos); newpos);
return 0; 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. */ /* This a jump; chase it. */
duprintf("Jump rule %u -> %u\n", duprintf("Jump rule %u -> %u\n",
pos, newpos); pos, newpos);
...@@ -560,6 +572,7 @@ mark_source_chains(const struct xt_table_info *newinfo, ...@@ -560,6 +572,7 @@ mark_source_chains(const struct xt_table_info *newinfo,
next: next:
duprintf("Finished chain %u\n", hook); duprintf("Finished chain %u\n", hook);
} }
newinfo->stacksize = max_calldepth;
return 1; return 1;
} }
...@@ -839,9 +852,6 @@ translate_table(struct net *net, struct xt_table_info *newinfo, void *entry0, ...@@ -839,9 +852,6 @@ translate_table(struct net *net, struct xt_table_info *newinfo, void *entry0,
if (ret != 0) if (ret != 0)
return ret; return ret;
++i; ++i;
if (strcmp(ip6t_get_target(iter)->u.user.name,
XT_ERROR_TARGET) == 0)
++newinfo->stacksize;
} }
if (i != repl->num_entries) { if (i != repl->num_entries) {
...@@ -1754,9 +1764,6 @@ translate_compat_table(struct net *net, ...@@ -1754,9 +1764,6 @@ translate_compat_table(struct net *net,
if (ret != 0) if (ret != 0)
break; break;
++i; ++i;
if (strcmp(ip6t_get_target(iter1)->u.user.name,
XT_ERROR_TARGET) == 0)
++newinfo->stacksize;
} }
if (ret) { if (ret) {
/* /*
......
...@@ -749,6 +749,10 @@ static int xt_jumpstack_alloc(struct xt_table_info *i) ...@@ -749,6 +749,10 @@ static int xt_jumpstack_alloc(struct xt_table_info *i)
if (i->jumpstack == NULL) if (i->jumpstack == NULL)
return -ENOMEM; return -ENOMEM;
/* ruleset without jumps -- no stack needed */
if (i->stacksize == 0)
return 0;
i->stacksize *= xt_jumpstack_multiplier; i->stacksize *= xt_jumpstack_multiplier;
size = sizeof(void *) * i->stacksize; size = sizeof(void *) * i->stacksize;
for_each_possible_cpu(cpu) { for_each_possible_cpu(cpu) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment