diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index c1caf9ded280..e5b6b0534c5f 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -156,9 +156,12 @@ int fib_unmerge(struct net *net) { struct fib_table *old, *new; + /* attempt to fetch local table if it has been allocated */ old = fib_get_table(net, RT_TABLE_LOCAL); - new = fib_trie_unmerge(old); + if (!old) + return 0; + new = fib_trie_unmerge(old); if (!new) return -ENOMEM; diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index dd488c102d89..e3b4aee4244e 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -950,7 +950,7 @@ static struct key_vector *fib_find_node(struct trie *t, * priority less than or equal to PRIO. */ static struct fib_alias *fib_find_alias(struct hlist_head *fah, u8 slen, - u8 tos, u32 prio) + u8 tos, u32 prio, u32 tb_id) { struct fib_alias *fa; @@ -962,6 +962,10 @@ static struct fib_alias *fib_find_alias(struct hlist_head *fah, u8 slen, continue; if (fa->fa_slen != slen) break; + if (fa->tb_id > tb_id) + continue; + if (fa->tb_id != tb_id) + break; if (fa->fa_tos > tos) continue; if (fa->fa_info->fib_priority >= prio || fa->fa_tos < tos) @@ -1041,6 +1045,9 @@ static int fib_insert_alias(struct trie *t, struct key_vector *tp, hlist_for_each_entry(last, &l->leaf, fa_list) { if (new->fa_slen < last->fa_slen) break; + if ((new->fa_slen == last->fa_slen) && + (new->tb_id > last->tb_id)) + break; fa = last; } @@ -1089,7 +1096,8 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg) } l = fib_find_node(t, &tp, key); - fa = l ? fib_find_alias(&l->leaf, slen, tos, fi->fib_priority) : NULL; + fa = l ? fib_find_alias(&l->leaf, slen, tos, fi->fib_priority, + tb->tb_id) : NULL; /* Now fa, if non-NULL, points to the first fib alias * with the same keys [prefix,tos,priority], if such key already @@ -1116,13 +1124,12 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg) fa_match = NULL; fa_first = fa; hlist_for_each_entry_from(fa, fa_list) { - if ((fa->fa_slen != slen) || (fa->fa_tos != tos)) + if ((fa->fa_slen != slen) || + (fa->tb_id != tb->tb_id) || + (fa->fa_tos != tos)) break; if (fa->fa_info->fib_priority != fi->fib_priority) break; - /* duplicate entry from another table */ - if (WARN_ON(fa->tb_id != tb->tb_id)) - continue; if (fa->fa_type == cfg->fc_type && fa->fa_info == fi) { fa_match = fa; @@ -1474,7 +1481,7 @@ int fib_table_delete(struct fib_table *tb, struct fib_config *cfg) if (!l) return -ESRCH; - fa = fib_find_alias(&l->leaf, slen, tos, 0); + fa = fib_find_alias(&l->leaf, slen, tos, 0, tb->tb_id); if (!fa) return -ESRCH; @@ -1484,12 +1491,11 @@ int fib_table_delete(struct fib_table *tb, struct fib_config *cfg) hlist_for_each_entry_from(fa, fa_list) { struct fib_info *fi = fa->fa_info; - if ((fa->fa_slen != slen) || (fa->fa_tos != tos)) + if ((fa->fa_slen != slen) || + (fa->tb_id != tb->tb_id) || + (fa->fa_tos != tos)) break; - if (fa->tb_id != tb->tb_id) - continue; - if ((!cfg->fc_type || fa->fa_type == cfg->fc_type) && (cfg->fc_scope == RT_SCOPE_NOWHERE || fa->fa_info->fib_scope == cfg->fc_scope) &&