diff options
Diffstat (limited to 'net/ipv4')
-rw-r--r-- | net/ipv4/fib_trie.c | 55 |
1 files changed, 35 insertions, 20 deletions
diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index f2f47033f31f..cbccafde8238 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c | |||
@@ -1205,20 +1205,45 @@ static int fn_trie_insert(struct fib_table *tb, struct fib_config *cfg) | |||
1205 | * and we need to allocate a new one of those as well. | 1205 | * and we need to allocate a new one of those as well. |
1206 | */ | 1206 | */ |
1207 | 1207 | ||
1208 | if (fa && fa->fa_info->fib_priority == fi->fib_priority) { | 1208 | if (fa && fa->fa_tos == tos && |
1209 | struct fib_alias *fa_orig; | 1209 | fa->fa_info->fib_priority == fi->fib_priority) { |
1210 | struct fib_alias *fa_first, *fa_match; | ||
1210 | 1211 | ||
1211 | err = -EEXIST; | 1212 | err = -EEXIST; |
1212 | if (cfg->fc_nlflags & NLM_F_EXCL) | 1213 | if (cfg->fc_nlflags & NLM_F_EXCL) |
1213 | goto out; | 1214 | goto out; |
1214 | 1215 | ||
1216 | /* We have 2 goals: | ||
1217 | * 1. Find exact match for type, scope, fib_info to avoid | ||
1218 | * duplicate routes | ||
1219 | * 2. Find next 'fa' (or head), NLM_F_APPEND inserts before it | ||
1220 | */ | ||
1221 | fa_match = NULL; | ||
1222 | fa_first = fa; | ||
1223 | fa = list_entry(fa->fa_list.prev, struct fib_alias, fa_list); | ||
1224 | list_for_each_entry_continue(fa, fa_head, fa_list) { | ||
1225 | if (fa->fa_tos != tos) | ||
1226 | break; | ||
1227 | if (fa->fa_info->fib_priority != fi->fib_priority) | ||
1228 | break; | ||
1229 | if (fa->fa_type == cfg->fc_type && | ||
1230 | fa->fa_scope == cfg->fc_scope && | ||
1231 | fa->fa_info == fi) { | ||
1232 | fa_match = fa; | ||
1233 | break; | ||
1234 | } | ||
1235 | } | ||
1236 | |||
1215 | if (cfg->fc_nlflags & NLM_F_REPLACE) { | 1237 | if (cfg->fc_nlflags & NLM_F_REPLACE) { |
1216 | struct fib_info *fi_drop; | 1238 | struct fib_info *fi_drop; |
1217 | u8 state; | 1239 | u8 state; |
1218 | 1240 | ||
1219 | if (fi->fib_treeref > 1) | 1241 | fa = fa_first; |
1242 | if (fa_match) { | ||
1243 | if (fa == fa_match) | ||
1244 | err = 0; | ||
1220 | goto out; | 1245 | goto out; |
1221 | 1246 | } | |
1222 | err = -ENOBUFS; | 1247 | err = -ENOBUFS; |
1223 | new_fa = kmem_cache_alloc(fn_alias_kmem, GFP_KERNEL); | 1248 | new_fa = kmem_cache_alloc(fn_alias_kmem, GFP_KERNEL); |
1224 | if (new_fa == NULL) | 1249 | if (new_fa == NULL) |
@@ -1230,7 +1255,7 @@ static int fn_trie_insert(struct fib_table *tb, struct fib_config *cfg) | |||
1230 | new_fa->fa_type = cfg->fc_type; | 1255 | new_fa->fa_type = cfg->fc_type; |
1231 | new_fa->fa_scope = cfg->fc_scope; | 1256 | new_fa->fa_scope = cfg->fc_scope; |
1232 | state = fa->fa_state; | 1257 | state = fa->fa_state; |
1233 | new_fa->fa_state &= ~FA_S_ACCESSED; | 1258 | new_fa->fa_state = state & ~FA_S_ACCESSED; |
1234 | 1259 | ||
1235 | list_replace_rcu(&fa->fa_list, &new_fa->fa_list); | 1260 | list_replace_rcu(&fa->fa_list, &new_fa->fa_list); |
1236 | alias_free_mem_rcu(fa); | 1261 | alias_free_mem_rcu(fa); |
@@ -1247,20 +1272,11 @@ static int fn_trie_insert(struct fib_table *tb, struct fib_config *cfg) | |||
1247 | * uses the same scope, type, and nexthop | 1272 | * uses the same scope, type, and nexthop |
1248 | * information. | 1273 | * information. |
1249 | */ | 1274 | */ |
1250 | fa_orig = fa; | 1275 | if (fa_match) |
1251 | list_for_each_entry(fa, fa_orig->fa_list.prev, fa_list) { | 1276 | goto out; |
1252 | if (fa->fa_tos != tos) | ||
1253 | break; | ||
1254 | if (fa->fa_info->fib_priority != fi->fib_priority) | ||
1255 | break; | ||
1256 | if (fa->fa_type == cfg->fc_type && | ||
1257 | fa->fa_scope == cfg->fc_scope && | ||
1258 | fa->fa_info == fi) | ||
1259 | goto out; | ||
1260 | } | ||
1261 | 1277 | ||
1262 | if (!(cfg->fc_nlflags & NLM_F_APPEND)) | 1278 | if (!(cfg->fc_nlflags & NLM_F_APPEND)) |
1263 | fa = fa_orig; | 1279 | fa = fa_first; |
1264 | } | 1280 | } |
1265 | err = -ENOENT; | 1281 | err = -ENOENT; |
1266 | if (!(cfg->fc_nlflags & NLM_F_CREATE)) | 1282 | if (!(cfg->fc_nlflags & NLM_F_CREATE)) |
@@ -1600,9 +1616,8 @@ static int fn_trie_delete(struct fib_table *tb, struct fib_config *cfg) | |||
1600 | pr_debug("Deleting %08x/%d tos=%d t=%p\n", key, plen, tos, t); | 1616 | pr_debug("Deleting %08x/%d tos=%d t=%p\n", key, plen, tos, t); |
1601 | 1617 | ||
1602 | fa_to_delete = NULL; | 1618 | fa_to_delete = NULL; |
1603 | fa_head = fa->fa_list.prev; | 1619 | fa = list_entry(fa->fa_list.prev, struct fib_alias, fa_list); |
1604 | 1620 | list_for_each_entry_continue(fa, fa_head, fa_list) { | |
1605 | list_for_each_entry(fa, fa_head, fa_list) { | ||
1606 | struct fib_info *fi = fa->fa_info; | 1621 | struct fib_info *fi = fa->fa_info; |
1607 | 1622 | ||
1608 | if (fa->fa_tos != tos) | 1623 | if (fa->fa_tos != tos) |