[NETFILTER]: {ip,ip6,arp}_tables: fix exponential worst-case search for loops

If we come to node we'd already marked as seen and it's not a part of path
(i.e. we don't have a loop right there), we already know that it isn't a
part of any loop, so we don't need to revisit it.

That speeds the things up if some chain is refered to from several places
and kills O(exp(table size)) worst-case behaviour (without sleeping,
at that, so if you manage to self-LART that way, you are SOL for a long
time)...

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by Al Viro and committed by David S. Miller e1b4b9f3 a96be246

+9 -6
+3 -2
net/ipv4/netfilter/arp_tables.c
··· 358 for (;;) { 359 struct arpt_standard_target *t 360 = (void *)arpt_get_target(e); 361 362 if (e->comefrom & (1 << NF_ARP_NUMHOOKS)) { 363 printk("arptables: loop hook %u pos %u %08X.\n", ··· 369 |= ((1 << hook) | (1 << NF_ARP_NUMHOOKS)); 370 371 /* Unconditional return/END. */ 372 - if (e->target_offset == sizeof(struct arpt_entry) 373 && (strcmp(t->target.u.user.name, 374 ARPT_STANDARD_TARGET) == 0) 375 && t->verdict < 0 376 - && unconditional(&e->arp)) { 377 unsigned int oldpos, size; 378 379 if (t->verdict < -NF_MAX_VERDICT - 1) {
··· 358 for (;;) { 359 struct arpt_standard_target *t 360 = (void *)arpt_get_target(e); 361 + int visited = e->comefrom & (1 << hook); 362 363 if (e->comefrom & (1 << NF_ARP_NUMHOOKS)) { 364 printk("arptables: loop hook %u pos %u %08X.\n", ··· 368 |= ((1 << hook) | (1 << NF_ARP_NUMHOOKS)); 369 370 /* Unconditional return/END. */ 371 + if ((e->target_offset == sizeof(struct arpt_entry) 372 && (strcmp(t->target.u.user.name, 373 ARPT_STANDARD_TARGET) == 0) 374 && t->verdict < 0 375 + && unconditional(&e->arp)) || visited) { 376 unsigned int oldpos, size; 377 378 if (t->verdict < -NF_MAX_VERDICT - 1) {
+3 -2
net/ipv4/netfilter/ip_tables.c
··· 384 for (;;) { 385 struct ipt_standard_target *t 386 = (void *)ipt_get_target(e); 387 388 if (e->comefrom & (1 << NF_IP_NUMHOOKS)) { 389 printk("iptables: loop hook %u pos %u %08X.\n", ··· 395 |= ((1 << hook) | (1 << NF_IP_NUMHOOKS)); 396 397 /* Unconditional return/END. */ 398 - if (e->target_offset == sizeof(struct ipt_entry) 399 && (strcmp(t->target.u.user.name, 400 IPT_STANDARD_TARGET) == 0) 401 && t->verdict < 0 402 - && unconditional(&e->ip)) { 403 unsigned int oldpos, size; 404 405 if (t->verdict < -NF_MAX_VERDICT - 1) {
··· 384 for (;;) { 385 struct ipt_standard_target *t 386 = (void *)ipt_get_target(e); 387 + int visited = e->comefrom & (1 << hook); 388 389 if (e->comefrom & (1 << NF_IP_NUMHOOKS)) { 390 printk("iptables: loop hook %u pos %u %08X.\n", ··· 394 |= ((1 << hook) | (1 << NF_IP_NUMHOOKS)); 395 396 /* Unconditional return/END. */ 397 + if ((e->target_offset == sizeof(struct ipt_entry) 398 && (strcmp(t->target.u.user.name, 399 IPT_STANDARD_TARGET) == 0) 400 && t->verdict < 0 401 + && unconditional(&e->ip)) || visited) { 402 unsigned int oldpos, size; 403 404 if (t->verdict < -NF_MAX_VERDICT - 1) {
+3 -2
net/ipv6/netfilter/ip6_tables.c
··· 413 unsigned int pos = newinfo->hook_entry[hook]; 414 struct ip6t_entry *e 415 = (struct ip6t_entry *)(entry0 + pos); 416 417 if (!(valid_hooks & (1 << hook))) 418 continue; ··· 434 |= ((1 << hook) | (1 << NF_IP6_NUMHOOKS)); 435 436 /* Unconditional return/END. */ 437 - if (e->target_offset == sizeof(struct ip6t_entry) 438 && (strcmp(t->target.u.user.name, 439 IP6T_STANDARD_TARGET) == 0) 440 && t->verdict < 0 441 - && unconditional(&e->ipv6)) { 442 unsigned int oldpos, size; 443 444 if (t->verdict < -NF_MAX_VERDICT - 1) {
··· 413 unsigned int pos = newinfo->hook_entry[hook]; 414 struct ip6t_entry *e 415 = (struct ip6t_entry *)(entry0 + pos); 416 + int visited = e->comefrom & (1 << hook); 417 418 if (!(valid_hooks & (1 << hook))) 419 continue; ··· 433 |= ((1 << hook) | (1 << NF_IP6_NUMHOOKS)); 434 435 /* Unconditional return/END. */ 436 + if ((e->target_offset == sizeof(struct ip6t_entry) 437 && (strcmp(t->target.u.user.name, 438 IP6T_STANDARD_TARGET) == 0) 439 && t->verdict < 0 440 + && unconditional(&e->ipv6)) || visited) { 441 unsigned int oldpos, size; 442 443 if (t->verdict < -NF_MAX_VERDICT - 1) {