diff options
author | Jan Engelhardt <jengelh@medozas.de> | 2010-04-19 10:05:10 -0400 |
---|---|---|
committer | Patrick McHardy <kaber@trash.net> | 2010-04-19 10:05:10 -0400 |
commit | f3c5c1bfd430858d3a05436f82c51e53104feb6b (patch) | |
tree | ada5b570b66e141e79fdb256f69e2541a3d30c04 /net/ipv4 | |
parent | e281b19897dc21c1071802808d461627d747a877 (diff) |
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>
Diffstat (limited to 'net/ipv4')
-rw-r--r-- | net/ipv4/netfilter/arp_tables.c | 6 | ||||
-rw-r--r-- | net/ipv4/netfilter/ip_tables.c | 65 |
2 files changed, 39 insertions, 32 deletions
diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index e8e363d90365..07a699059390 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c | |||
@@ -649,6 +649,9 @@ static int translate_table(struct xt_table_info *newinfo, void *entry0, | |||
649 | if (ret != 0) | 649 | if (ret != 0) |
650 | break; | 650 | break; |
651 | ++i; | 651 | ++i; |
652 | if (strcmp(arpt_get_target(iter)->u.user.name, | ||
653 | XT_ERROR_TARGET) == 0) | ||
654 | ++newinfo->stacksize; | ||
652 | } | 655 | } |
653 | duprintf("translate_table: ARPT_ENTRY_ITERATE gives %d\n", ret); | 656 | duprintf("translate_table: ARPT_ENTRY_ITERATE gives %d\n", ret); |
654 | if (ret != 0) | 657 | if (ret != 0) |
@@ -1774,8 +1777,7 @@ struct xt_table *arpt_register_table(struct net *net, | |||
1774 | { | 1777 | { |
1775 | int ret; | 1778 | int ret; |
1776 | struct xt_table_info *newinfo; | 1779 | struct xt_table_info *newinfo; |
1777 | struct xt_table_info bootstrap | 1780 | struct xt_table_info bootstrap = {0}; |
1778 | = { 0, 0, 0, { 0 }, { 0 }, { } }; | ||
1779 | void *loc_cpu_entry; | 1781 | void *loc_cpu_entry; |
1780 | struct xt_table *new_table; | 1782 | struct xt_table *new_table; |
1781 | 1783 | ||
diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 18c5b1573f3e..70900ecf88e2 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c | |||
@@ -321,8 +321,6 @@ ipt_do_table(struct sk_buff *skb, | |||
321 | const struct net_device *out, | 321 | const struct net_device *out, |
322 | struct xt_table *table) | 322 | struct xt_table *table) |
323 | { | 323 | { |
324 | #define tb_comefrom ((struct ipt_entry *)table_base)->comefrom | ||
325 | |||
326 | static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long)))); | 324 | static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long)))); |
327 | const struct iphdr *ip; | 325 | const struct iphdr *ip; |
328 | bool hotdrop = false; | 326 | bool hotdrop = false; |
@@ -330,7 +328,8 @@ ipt_do_table(struct sk_buff *skb, | |||
330 | unsigned int verdict = NF_DROP; | 328 | unsigned int verdict = NF_DROP; |
331 | const char *indev, *outdev; | 329 | const char *indev, *outdev; |
332 | const void *table_base; | 330 | const void *table_base; |
333 | struct ipt_entry *e, *back; | 331 | struct ipt_entry *e, **jumpstack; |
332 | unsigned int *stackptr, origptr, cpu; | ||
334 | const struct xt_table_info *private; | 333 | const struct xt_table_info *private; |
335 | struct xt_match_param mtpar; | 334 | struct xt_match_param mtpar; |
336 | struct xt_target_param tgpar; | 335 | struct xt_target_param tgpar; |
@@ -356,19 +355,23 @@ ipt_do_table(struct sk_buff *skb, | |||
356 | IP_NF_ASSERT(table->valid_hooks & (1 << hook)); | 355 | IP_NF_ASSERT(table->valid_hooks & (1 << hook)); |
357 | xt_info_rdlock_bh(); | 356 | xt_info_rdlock_bh(); |
358 | private = table->private; | 357 | private = table->private; |
359 | table_base = private->entries[smp_processor_id()]; | 358 | cpu = smp_processor_id(); |
359 | table_base = private->entries[cpu]; | ||
360 | jumpstack = (struct ipt_entry **)private->jumpstack[cpu]; | ||
361 | stackptr = &private->stackptr[cpu]; | ||
362 | origptr = *stackptr; | ||
360 | 363 | ||
361 | e = get_entry(table_base, private->hook_entry[hook]); | 364 | e = get_entry(table_base, private->hook_entry[hook]); |
362 | 365 | ||
363 | /* For return from builtin chain */ | 366 | pr_devel("Entering %s(hook %u); sp at %u (UF %p)\n", |
364 | back = get_entry(table_base, private->underflow[hook]); | 367 | table->name, hook, origptr, |
368 | get_entry(table_base, private->underflow[hook])); | ||
365 | 369 | ||
366 | do { | 370 | do { |
367 | const struct ipt_entry_target *t; | 371 | const struct ipt_entry_target *t; |
368 | const struct xt_entry_match *ematch; | 372 | const struct xt_entry_match *ematch; |
369 | 373 | ||
370 | IP_NF_ASSERT(e); | 374 | IP_NF_ASSERT(e); |
371 | IP_NF_ASSERT(back); | ||
372 | if (!ip_packet_match(ip, indev, outdev, | 375 | if (!ip_packet_match(ip, indev, outdev, |
373 | &e->ip, mtpar.fragoff)) { | 376 | &e->ip, mtpar.fragoff)) { |
374 | no_match: | 377 | no_match: |
@@ -403,17 +406,28 @@ ipt_do_table(struct sk_buff *skb, | |||
403 | verdict = (unsigned)(-v) - 1; | 406 | verdict = (unsigned)(-v) - 1; |
404 | break; | 407 | break; |
405 | } | 408 | } |
406 | e = back; | 409 | if (*stackptr == 0) { |
407 | back = get_entry(table_base, back->comefrom); | 410 | e = get_entry(table_base, |
411 | private->underflow[hook]); | ||
412 | pr_devel("Underflow (this is normal) " | ||
413 | "to %p\n", e); | ||
414 | } else { | ||
415 | e = jumpstack[--*stackptr]; | ||
416 | pr_devel("Pulled %p out from pos %u\n", | ||
417 | e, *stackptr); | ||
418 | e = ipt_next_entry(e); | ||
419 | } | ||
408 | continue; | 420 | continue; |
409 | } | 421 | } |
410 | if (table_base + v != ipt_next_entry(e) && | 422 | if (table_base + v != ipt_next_entry(e) && |
411 | !(e->ip.flags & IPT_F_GOTO)) { | 423 | !(e->ip.flags & IPT_F_GOTO)) { |
412 | /* Save old back ptr in next entry */ | 424 | if (*stackptr >= private->stacksize) { |
413 | struct ipt_entry *next = ipt_next_entry(e); | 425 | verdict = NF_DROP; |
414 | next->comefrom = (void *)back - table_base; | 426 | break; |
415 | /* set back pointer to next entry */ | 427 | } |
416 | back = next; | 428 | jumpstack[(*stackptr)++] = e; |
429 | pr_devel("Pushed %p into pos %u\n", | ||
430 | e, *stackptr - 1); | ||
417 | } | 431 | } |
418 | 432 | ||
419 | e = get_entry(table_base, v); | 433 | e = get_entry(table_base, v); |
@@ -426,18 +440,7 @@ ipt_do_table(struct sk_buff *skb, | |||
426 | tgpar.targinfo = t->data; | 440 | tgpar.targinfo = t->data; |
427 | 441 | ||
428 | 442 | ||
429 | #ifdef CONFIG_NETFILTER_DEBUG | ||
430 | tb_comefrom = 0xeeeeeeec; | ||
431 | #endif | ||
432 | verdict = t->u.kernel.target->target(skb, &tgpar); | 443 | verdict = t->u.kernel.target->target(skb, &tgpar); |
433 | #ifdef CONFIG_NETFILTER_DEBUG | ||
434 | if (tb_comefrom != 0xeeeeeeec && verdict == IPT_CONTINUE) { | ||
435 | printk("Target %s reentered!\n", | ||
436 | t->u.kernel.target->name); | ||
437 | verdict = NF_DROP; | ||
438 | } | ||
439 | tb_comefrom = 0x57acc001; | ||
440 | #endif | ||
441 | /* Target might have changed stuff. */ | 444 | /* Target might have changed stuff. */ |
442 | ip = ip_hdr(skb); | 445 | ip = ip_hdr(skb); |
443 | if (verdict == IPT_CONTINUE) | 446 | if (verdict == IPT_CONTINUE) |
@@ -447,7 +450,9 @@ ipt_do_table(struct sk_buff *skb, | |||
447 | break; | 450 | break; |
448 | } while (!hotdrop); | 451 | } while (!hotdrop); |
449 | xt_info_rdunlock_bh(); | 452 | xt_info_rdunlock_bh(); |
450 | 453 | pr_devel("Exiting %s; resetting sp from %u to %u\n", | |
454 | __func__, *stackptr, origptr); | ||
455 | *stackptr = origptr; | ||
451 | #ifdef DEBUG_ALLOW_ALL | 456 | #ifdef DEBUG_ALLOW_ALL |
452 | return NF_ACCEPT; | 457 | return NF_ACCEPT; |
453 | #else | 458 | #else |
@@ -455,8 +460,6 @@ ipt_do_table(struct sk_buff *skb, | |||
455 | return NF_DROP; | 460 | return NF_DROP; |
456 | else return verdict; | 461 | else return verdict; |
457 | #endif | 462 | #endif |
458 | |||
459 | #undef tb_comefrom | ||
460 | } | 463 | } |
461 | 464 | ||
462 | /* Figures out from what hook each rule can be called: returns 0 if | 465 | /* Figures out from what hook each rule can be called: returns 0 if |
@@ -838,6 +841,9 @@ translate_table(struct net *net, struct xt_table_info *newinfo, void *entry0, | |||
838 | if (ret != 0) | 841 | if (ret != 0) |
839 | return ret; | 842 | return ret; |
840 | ++i; | 843 | ++i; |
844 | if (strcmp(ipt_get_target(iter)->u.user.name, | ||
845 | XT_ERROR_TARGET) == 0) | ||
846 | ++newinfo->stacksize; | ||
841 | } | 847 | } |
842 | 848 | ||
843 | if (i != repl->num_entries) { | 849 | if (i != repl->num_entries) { |
@@ -2086,8 +2092,7 @@ struct xt_table *ipt_register_table(struct net *net, | |||
2086 | { | 2092 | { |
2087 | int ret; | 2093 | int ret; |
2088 | struct xt_table_info *newinfo; | 2094 | struct xt_table_info *newinfo; |
2089 | struct xt_table_info bootstrap | 2095 | struct xt_table_info bootstrap = {0}; |
2090 | = { 0, 0, 0, { 0 }, { 0 }, { } }; | ||
2091 | void *loc_cpu_entry; | 2096 | void *loc_cpu_entry; |
2092 | struct xt_table *new_table; | 2097 | struct xt_table *new_table; |
2093 | 2098 | ||