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