netfilter: xtables: make ip_tables reentrant
Currently, the table traverser stores return addresses in the ruleset itself (struct ip6t_entry->comefrom). This has a well-known drawback: the jumpstack is overwritten on reentry, making it necessary for targets to return absolute verdicts. Also, the ruleset (which might be heavy memory-wise) needs to be replicated for each CPU that can possibly invoke ip6t_do_table. This patch decouples the jumpstack from struct ip6t_entry and instead puts it into xt_table_info. Not being restricted by 'comefrom' anymore, we can set up a stack as needed. By default, there is room allocated for two entries into the traverser. arp_tables is not touched though, because there is just one/two modules and further patches seek to collapse the table traverser anyhow. Signed-off-by: Jan Engelhardt <jengelh@medozas.de> Signed-off-by: Patrick McHardy <kaber@trash.net>
This commit is contained in:
committed by
Patrick McHardy
parent
e281b19897
commit
f3c5c1bfd4
@@ -351,15 +351,14 @@ ip6t_do_table(struct sk_buff *skb,
|
||||
const struct net_device *out,
|
||||
struct xt_table *table)
|
||||
{
|
||||
#define tb_comefrom ((struct ip6t_entry *)table_base)->comefrom
|
||||
|
||||
static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
|
||||
bool hotdrop = false;
|
||||
/* Initializing verdict to NF_DROP keeps gcc happy. */
|
||||
unsigned int verdict = NF_DROP;
|
||||
const char *indev, *outdev;
|
||||
const void *table_base;
|
||||
struct ip6t_entry *e, *back;
|
||||
struct ip6t_entry *e, **jumpstack;
|
||||
unsigned int *stackptr, origptr, cpu;
|
||||
const struct xt_table_info *private;
|
||||
struct xt_match_param mtpar;
|
||||
struct xt_target_param tgpar;
|
||||
@@ -383,19 +382,19 @@ ip6t_do_table(struct sk_buff *skb,
|
||||
|
||||
xt_info_rdlock_bh();
|
||||
private = table->private;
|
||||
table_base = private->entries[smp_processor_id()];
|
||||
cpu = smp_processor_id();
|
||||
table_base = private->entries[cpu];
|
||||
jumpstack = (struct ip6t_entry **)private->jumpstack[cpu];
|
||||
stackptr = &private->stackptr[cpu];
|
||||
origptr = *stackptr;
|
||||
|
||||
e = get_entry(table_base, private->hook_entry[hook]);
|
||||
|
||||
/* For return from builtin chain */
|
||||
back = get_entry(table_base, private->underflow[hook]);
|
||||
|
||||
do {
|
||||
const struct ip6t_entry_target *t;
|
||||
const struct xt_entry_match *ematch;
|
||||
|
||||
IP_NF_ASSERT(e);
|
||||
IP_NF_ASSERT(back);
|
||||
if (!ip6_packet_match(skb, indev, outdev, &e->ipv6,
|
||||
&mtpar.thoff, &mtpar.fragoff, &hotdrop)) {
|
||||
no_match:
|
||||
@@ -432,17 +431,20 @@ ip6t_do_table(struct sk_buff *skb,
|
||||
verdict = (unsigned)(-v) - 1;
|
||||
break;
|
||||
}
|
||||
e = back;
|
||||
back = get_entry(table_base, back->comefrom);
|
||||
if (*stackptr == 0)
|
||||
e = get_entry(table_base,
|
||||
private->underflow[hook]);
|
||||
else
|
||||
e = ip6t_next_entry(jumpstack[--*stackptr]);
|
||||
continue;
|
||||
}
|
||||
if (table_base + v != ip6t_next_entry(e) &&
|
||||
!(e->ipv6.flags & IP6T_F_GOTO)) {
|
||||
/* Save old back ptr in next entry */
|
||||
struct ip6t_entry *next = ip6t_next_entry(e);
|
||||
next->comefrom = (void *)back - table_base;
|
||||
/* set back pointer to next entry */
|
||||
back = next;
|
||||
if (*stackptr >= private->stacksize) {
|
||||
verdict = NF_DROP;
|
||||
break;
|
||||
}
|
||||
jumpstack[(*stackptr)++] = e;
|
||||
}
|
||||
|
||||
e = get_entry(table_base, v);
|
||||
@@ -454,19 +456,7 @@ ip6t_do_table(struct sk_buff *skb,
|
||||
tgpar.target = t->u.kernel.target;
|
||||
tgpar.targinfo = t->data;
|
||||
|
||||
#ifdef CONFIG_NETFILTER_DEBUG
|
||||
tb_comefrom = 0xeeeeeeec;
|
||||
#endif
|
||||
verdict = t->u.kernel.target->target(skb, &tgpar);
|
||||
|
||||
#ifdef CONFIG_NETFILTER_DEBUG
|
||||
if (tb_comefrom != 0xeeeeeeec && verdict == IP6T_CONTINUE) {
|
||||
printk("Target %s reentered!\n",
|
||||
t->u.kernel.target->name);
|
||||
verdict = NF_DROP;
|
||||
}
|
||||
tb_comefrom = 0x57acc001;
|
||||
#endif
|
||||
if (verdict == IP6T_CONTINUE)
|
||||
e = ip6t_next_entry(e);
|
||||
else
|
||||
@@ -474,10 +464,8 @@ ip6t_do_table(struct sk_buff *skb,
|
||||
break;
|
||||
} while (!hotdrop);
|
||||
|
||||
#ifdef CONFIG_NETFILTER_DEBUG
|
||||
tb_comefrom = NETFILTER_LINK_POISON;
|
||||
#endif
|
||||
xt_info_rdunlock_bh();
|
||||
*stackptr = origptr;
|
||||
|
||||
#ifdef DEBUG_ALLOW_ALL
|
||||
return NF_ACCEPT;
|
||||
@@ -486,8 +474,6 @@ ip6t_do_table(struct sk_buff *skb,
|
||||
return NF_DROP;
|
||||
else return verdict;
|
||||
#endif
|
||||
|
||||
#undef tb_comefrom
|
||||
}
|
||||
|
||||
/* Figures out from what hook each rule can be called: returns 0 if
|
||||
@@ -869,6 +855,9 @@ 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) {
|
||||
@@ -2120,8 +2109,7 @@ struct xt_table *ip6t_register_table(struct net *net,
|
||||
{
|
||||
int ret;
|
||||
struct xt_table_info *newinfo;
|
||||
struct xt_table_info bootstrap
|
||||
= { 0, 0, 0, { 0 }, { 0 }, { } };
|
||||
struct xt_table_info bootstrap = {0};
|
||||
void *loc_cpu_entry;
|
||||
struct xt_table *new_table;
|
||||
|
||||
|
Reference in New Issue
Block a user