|
@@ -1205,20 +1205,45 @@ static int fn_trie_insert(struct fib_table *tb, struct fib_config *cfg)
|
|
|
* and we need to allocate a new one of those as well.
|
|
|
*/
|
|
|
|
|
|
- if (fa && fa->fa_info->fib_priority == fi->fib_priority) {
|
|
|
- struct fib_alias *fa_orig;
|
|
|
+ if (fa && fa->fa_tos == tos &&
|
|
|
+ fa->fa_info->fib_priority == fi->fib_priority) {
|
|
|
+ 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, fa_head, 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) {
|
|
|
+ if (fa == fa_match)
|
|
|
+ err = 0;
|
|
|
goto out;
|
|
|
-
|
|
|
+ }
|
|
|
err = -ENOBUFS;
|
|
|
new_fa = kmem_cache_alloc(fn_alias_kmem, GFP_KERNEL);
|
|
|
if (new_fa == NULL)
|
|
@@ -1230,7 +1255,7 @@ static int fn_trie_insert(struct fib_table *tb, struct fib_config *cfg)
|
|
|
new_fa->fa_type = cfg->fc_type;
|
|
|
new_fa->fa_scope = cfg->fc_scope;
|
|
|
state = fa->fa_state;
|
|
|
- new_fa->fa_state &= ~FA_S_ACCESSED;
|
|
|
+ new_fa->fa_state = state & ~FA_S_ACCESSED;
|
|
|
|
|
|
list_replace_rcu(&fa->fa_list, &new_fa->fa_list);
|
|
|
alias_free_mem_rcu(fa);
|
|
@@ -1247,20 +1272,11 @@ static int fn_trie_insert(struct fib_table *tb, struct fib_config *cfg)
|
|
|
* uses the same scope, type, and nexthop
|
|
|
* information.
|
|
|
*/
|
|
|
- fa_orig = fa;
|
|
|
- list_for_each_entry(fa, fa_orig->fa_list.prev, 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;
|
|
|
if (!(cfg->fc_nlflags & NLM_F_CREATE))
|
|
@@ -1600,9 +1616,8 @@ static int fn_trie_delete(struct fib_table *tb, struct fib_config *cfg)
|
|
|
pr_debug("Deleting %08x/%d tos=%d t=%p\n", key, plen, tos, t);
|
|
|
|
|
|
fa_to_delete = NULL;
|
|
|
- fa_head = fa->fa_list.prev;
|
|
|
-
|
|
|
- list_for_each_entry(fa, fa_head, fa_list) {
|
|
|
+ fa = list_entry(fa->fa_list.prev, struct fib_alias, fa_list);
|
|
|
+ list_for_each_entry_continue(fa, fa_head, fa_list) {
|
|
|
struct fib_info *fi = fa->fa_info;
|
|
|
|
|
|
if (fa->fa_tos != tos)
|