aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/fib_hash.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv4/fib_hash.c')
-rw-r--r--net/ipv4/fib_hash.c47
1 files changed, 31 insertions, 16 deletions
diff --git a/net/ipv4/fib_hash.c b/net/ipv4/fib_hash.c
index a15b2f1b2721..76b9c684cccd 100644
--- a/net/ipv4/fib_hash.c
+++ b/net/ipv4/fib_hash.c
@@ -424,19 +424,43 @@ static int fn_hash_insert(struct fib_table *tb, struct fib_config *cfg)
424 424
425 if (fa && fa->fa_tos == tos && 425 if (fa && fa->fa_tos == tos &&
426 fa->fa_info->fib_priority == fi->fib_priority) { 426 fa->fa_info->fib_priority == fi->fib_priority) {
427 struct fib_alias *fa_orig; 427 struct fib_alias *fa_first, *fa_match;
428 428
429 err = -EEXIST; 429 err = -EEXIST;
430 if (cfg->fc_nlflags & NLM_F_EXCL) 430 if (cfg->fc_nlflags & NLM_F_EXCL)
431 goto out; 431 goto out;
432 432
433 /* We have 2 goals:
434 * 1. Find exact match for type, scope, fib_info to avoid
435 * duplicate routes
436 * 2. Find next 'fa' (or head), NLM_F_APPEND inserts before it
437 */
438 fa_match = NULL;
439 fa_first = fa;
440 fa = list_entry(fa->fa_list.prev, struct fib_alias, fa_list);
441 list_for_each_entry_continue(fa, &f->fn_alias, fa_list) {
442 if (fa->fa_tos != tos)
443 break;
444 if (fa->fa_info->fib_priority != fi->fib_priority)
445 break;
446 if (fa->fa_type == cfg->fc_type &&
447 fa->fa_scope == cfg->fc_scope &&
448 fa->fa_info == fi) {
449 fa_match = fa;
450 break;
451 }
452 }
453
433 if (cfg->fc_nlflags & NLM_F_REPLACE) { 454 if (cfg->fc_nlflags & NLM_F_REPLACE) {
434 struct fib_info *fi_drop; 455 struct fib_info *fi_drop;
435 u8 state; 456 u8 state;
436 457
437 if (fi->fib_treeref > 1) 458 fa = fa_first;
459 if (fa_match) {
460 if (fa == fa_match)
461 err = 0;
438 goto out; 462 goto out;
439 463 }
440 write_lock_bh(&fib_hash_lock); 464 write_lock_bh(&fib_hash_lock);
441 fi_drop = fa->fa_info; 465 fi_drop = fa->fa_info;
442 fa->fa_info = fi; 466 fa->fa_info = fi;
@@ -459,20 +483,11 @@ static int fn_hash_insert(struct fib_table *tb, struct fib_config *cfg)
459 * uses the same scope, type, and nexthop 483 * uses the same scope, type, and nexthop
460 * information. 484 * information.
461 */ 485 */
462 fa_orig = fa; 486 if (fa_match)
463 fa = list_entry(fa->fa_list.prev, struct fib_alias, fa_list); 487 goto out;
464 list_for_each_entry_continue(fa, &f->fn_alias, fa_list) { 488
465 if (fa->fa_tos != tos)
466 break;
467 if (fa->fa_info->fib_priority != fi->fib_priority)
468 break;
469 if (fa->fa_type == cfg->fc_type &&
470 fa->fa_scope == cfg->fc_scope &&
471 fa->fa_info == fi)
472 goto out;
473 }
474 if (!(cfg->fc_nlflags & NLM_F_APPEND)) 489 if (!(cfg->fc_nlflags & NLM_F_APPEND))
475 fa = fa_orig; 490 fa = fa_first;
476 } 491 }
477 492
478 err = -ENOENT; 493 err = -ENOENT;