[IPV4] fib: fix route replacement, fib_info is shared
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. Additionally, avoid replacement without error if new route is same, as Joonwoo Park suggests. Signed-off-by: Julian Anastasov <ja@ssi.bg> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
committed by
David S. Miller
parent
ec9dbb1c3e
commit
c18865f392
@@ -424,19 +424,43 @@ static int fn_hash_insert(struct fib_table *tb, struct fib_config *cfg)
|
|||||||
|
|
||||||
if (fa && fa->fa_tos == tos &&
|
if (fa && fa->fa_tos == tos &&
|
||||||
fa->fa_info->fib_priority == fi->fib_priority) {
|
fa->fa_info->fib_priority == fi->fib_priority) {
|
||||||
struct fib_alias *fa_orig;
|
struct fib_alias *fa_first, *fa_match;
|
||||||
|
|
||||||
err = -EEXIST;
|
err = -EEXIST;
|
||||||
if (cfg->fc_nlflags & NLM_F_EXCL)
|
if (cfg->fc_nlflags & NLM_F_EXCL)
|
||||||
goto out;
|
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) {
|
if (cfg->fc_nlflags & NLM_F_REPLACE) {
|
||||||
struct fib_info *fi_drop;
|
struct fib_info *fi_drop;
|
||||||
u8 state;
|
u8 state;
|
||||||
|
|
||||||
if (fi->fib_treeref > 1)
|
fa = fa_first;
|
||||||
|
if (fa_match) {
|
||||||
|
if (fa == fa_match)
|
||||||
|
err = 0;
|
||||||
goto out;
|
goto out;
|
||||||
|
}
|
||||||
write_lock_bh(&fib_hash_lock);
|
write_lock_bh(&fib_hash_lock);
|
||||||
fi_drop = fa->fa_info;
|
fi_drop = fa->fa_info;
|
||||||
fa->fa_info = fi;
|
fa->fa_info = fi;
|
||||||
@@ -459,20 +483,11 @@ static int fn_hash_insert(struct fib_table *tb, struct fib_config *cfg)
|
|||||||
* uses the same scope, type, and nexthop
|
* uses the same scope, type, and nexthop
|
||||||
* information.
|
* information.
|
||||||
*/
|
*/
|
||||||
fa_orig = fa;
|
if (fa_match)
|
||||||
fa = list_entry(fa->fa_list.prev, struct fib_alias, fa_list);
|
goto out;
|
||||||
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 (!(cfg->fc_nlflags & NLM_F_APPEND))
|
if (!(cfg->fc_nlflags & NLM_F_APPEND))
|
||||||
fa = fa_orig;
|
fa = fa_first;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = -ENOENT;
|
err = -ENOENT;
|
||||||
|
Reference in New Issue
Block a user