[PATCH 2.6.24] fib: fix route replacement, fib_info is shared
From: Julian Anastasov <ja@ssi.bg>
Date: 2008-01-26 00:21:31
fib_info can be shared by many route prefixes but we don't want duplicate alternative routes for a prefix+tos+priority. Last change was not correct to check fib_treeref because it accounts usage from other prefixes. Signed-off-by: Julian Anastasov <ja@ssi.bg> ---
--- linux-2.6.24/net/ipv4/fib_hash.c_orig 2008-01-25 10:45:06.000000000 +0200
+++ linux-2.6.24/net/ipv4/fib_hash.c 2008-01-26 01:29:36.000000000 +0200@@ -434,19 +434,40 @@ static int fn_hash_insert(struct fib_tab if (fa && fa->fa_tos == tos && fa->fa_info->fib_priority == fi->fib_priority) { - struct fib_alias *fa_orig; + struct fib_alias *fa_first, *fa_match; err = -EEXIST; if (cfg->fc_nlflags & NLM_F_EXCL) goto out; + /* We have 2 goals: + * 1. Find exact match for type, scope, fib_info to avoid + * duplicate routes + * 2. Find next 'fa' (or head), NLM_F_APPEND inserts before it + */ + fa_match = NULL; + fa_first = fa; + fa = list_entry(fa->fa_list.prev, struct fib_alias, fa_list); + list_for_each_entry_continue(fa, &f->fn_alias, fa_list) { + if (fa->fa_tos != tos) + break; + if (fa->fa_info->fib_priority != fi->fib_priority) + break; + if (fa->fa_type == cfg->fc_type && + fa->fa_scope == cfg->fc_scope && + fa->fa_info == fi) { + fa_match = fa; + break; + } + } + if (cfg->fc_nlflags & NLM_F_REPLACE) { struct fib_info *fi_drop; u8 state; - if (fi->fib_treeref > 1) + fa = fa_first; + if (fa_match && fa != fa_match) goto out; - write_lock_bh(&fib_hash_lock); fi_drop = fa->fa_info; fa->fa_info = fi;
@@ -469,20 +490,11 @@ static int fn_hash_insert(struct fib_tab * uses the same scope, type, and nexthop * information. */ - fa_orig = fa; - fa = list_entry(fa->fa_list.prev, struct fib_alias, fa_list); - list_for_each_entry_continue(fa, &f->fn_alias, fa_list) { - if (fa->fa_tos != tos) - break; - if (fa->fa_info->fib_priority != fi->fib_priority) - break; - if (fa->fa_type == cfg->fc_type && - fa->fa_scope == cfg->fc_scope && - fa->fa_info == fi) - goto out; - } + if (fa_match) + goto out; + if (!(cfg->fc_nlflags & NLM_F_APPEND)) - fa = fa_orig; + fa = fa_first; } err = -ENOENT;