aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/fib_semantics.c
diff options
context:
space:
mode:
authorJulian Anastasov <ja@ssi.bg>2015-07-22 03:43:23 -0400
committerDavid S. Miller <davem@davemloft.net>2015-07-25 01:46:11 -0400
commit2392debc2be721a7d5b907cbcbc0ebb858dead01 (patch)
treedbcfe5697a5f9bab9752efe6c76aa74fbe8d493d /net/ipv4/fib_semantics.c
parent18a912e9a832dcfc7db9e055c7e41701ff5f9e69 (diff)
ipv4: consider TOS in fib_select_default
fib_select_default considers alternative routes only when res->fi is for the first alias in res->fa_head. In the common case this can happen only when the initial lookup matches the first alias with highest TOS value. This prevents the alternative routes to require specific TOS. This patch solves the problem as follows: - routes that require specific TOS should be returned by fib_select_default only when TOS matches, as already done in fib_table_lookup. This rule implies that depending on the TOS we can have many different lists of alternative gateways and we have to keep the last used gateway (fa_default) in first alias for the TOS instead of using single tb_default value. - as the aliases are ordered by many keys (TOS desc, fib_priority asc), we restrict the possible results to routes with matching TOS and lowest metric (fib_priority) and routes that match any TOS, again with lowest metric. For example, packet with TOS 8 can not use gw3 (not lowest metric), gw4 (different TOS) and gw6 (not lowest metric), all other gateways can be used: tos 8 via gw1 metric 2 <--- res->fa_head and res->fi tos 8 via gw2 metric 2 tos 8 via gw3 metric 3 tos 4 via gw4 tos 0 via gw5 tos 0 via gw6 metric 1 Reported-by: Hagen Paul Pfeifer <hagen@jauu.net> Signed-off-by: Julian Anastasov <ja@ssi.bg> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/fib_semantics.c')
-rw-r--r--net/ipv4/fib_semantics.c36
1 files changed, 25 insertions, 11 deletions
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c
index e1079583b8b7..3a06586b170c 100644
--- a/net/ipv4/fib_semantics.c
+++ b/net/ipv4/fib_semantics.c
@@ -1202,28 +1202,40 @@ int fib_sync_down_dev(struct net_device *dev, unsigned long event)
1202} 1202}
1203 1203
1204/* Must be invoked inside of an RCU protected region. */ 1204/* Must be invoked inside of an RCU protected region. */
1205void fib_select_default(struct fib_result *res) 1205void fib_select_default(const struct flowi4 *flp, struct fib_result *res)
1206{ 1206{
1207 struct fib_info *fi = NULL, *last_resort = NULL; 1207 struct fib_info *fi = NULL, *last_resort = NULL;
1208 struct hlist_head *fa_head = res->fa_head; 1208 struct hlist_head *fa_head = res->fa_head;
1209 struct fib_table *tb = res->table; 1209 struct fib_table *tb = res->table;
1210 u8 slen = 32 - res->prefixlen; 1210 u8 slen = 32 - res->prefixlen;
1211 int order = -1, last_idx = -1; 1211 int order = -1, last_idx = -1;
1212 struct fib_alias *fa; 1212 struct fib_alias *fa, *fa1 = NULL;
1213 u32 last_prio = res->fi->fib_priority;
1214 u8 last_tos = 0;
1213 1215
1214 hlist_for_each_entry_rcu(fa, fa_head, fa_list) { 1216 hlist_for_each_entry_rcu(fa, fa_head, fa_list) {
1215 struct fib_info *next_fi = fa->fa_info; 1217 struct fib_info *next_fi = fa->fa_info;
1216 1218
1217 if (fa->fa_slen != slen) 1219 if (fa->fa_slen != slen)
1218 continue; 1220 continue;
1221 if (fa->fa_tos && fa->fa_tos != flp->flowi4_tos)
1222 continue;
1219 if (fa->tb_id != tb->tb_id) 1223 if (fa->tb_id != tb->tb_id)
1220 continue; 1224 continue;
1225 if (next_fi->fib_priority > last_prio &&
1226 fa->fa_tos == last_tos) {
1227 if (last_tos)
1228 continue;
1229 break;
1230 }
1231 if (next_fi->fib_flags & RTNH_F_DEAD)
1232 continue;
1233 last_tos = fa->fa_tos;
1234 last_prio = next_fi->fib_priority;
1235
1221 if (next_fi->fib_scope != res->scope || 1236 if (next_fi->fib_scope != res->scope ||
1222 fa->fa_type != RTN_UNICAST) 1237 fa->fa_type != RTN_UNICAST)
1223 continue; 1238 continue;
1224
1225 if (next_fi->fib_priority > res->fi->fib_priority)
1226 break;
1227 if (!next_fi->fib_nh[0].nh_gw || 1239 if (!next_fi->fib_nh[0].nh_gw ||
1228 next_fi->fib_nh[0].nh_scope != RT_SCOPE_LINK) 1240 next_fi->fib_nh[0].nh_scope != RT_SCOPE_LINK)
1229 continue; 1241 continue;
@@ -1233,10 +1245,11 @@ void fib_select_default(struct fib_result *res)
1233 if (!fi) { 1245 if (!fi) {
1234 if (next_fi != res->fi) 1246 if (next_fi != res->fi)
1235 break; 1247 break;
1248 fa1 = fa;
1236 } else if (!fib_detect_death(fi, order, &last_resort, 1249 } else if (!fib_detect_death(fi, order, &last_resort,
1237 &last_idx, tb->tb_default)) { 1250 &last_idx, fa1->fa_default)) {
1238 fib_result_assign(res, fi); 1251 fib_result_assign(res, fi);
1239 tb->tb_default = order; 1252 fa1->fa_default = order;
1240 goto out; 1253 goto out;
1241 } 1254 }
1242 fi = next_fi; 1255 fi = next_fi;
@@ -1244,20 +1257,21 @@ void fib_select_default(struct fib_result *res)
1244 } 1257 }
1245 1258
1246 if (order <= 0 || !fi) { 1259 if (order <= 0 || !fi) {
1247 tb->tb_default = -1; 1260 if (fa1)
1261 fa1->fa_default = -1;
1248 goto out; 1262 goto out;
1249 } 1263 }
1250 1264
1251 if (!fib_detect_death(fi, order, &last_resort, &last_idx, 1265 if (!fib_detect_death(fi, order, &last_resort, &last_idx,
1252 tb->tb_default)) { 1266 fa1->fa_default)) {
1253 fib_result_assign(res, fi); 1267 fib_result_assign(res, fi);
1254 tb->tb_default = order; 1268 fa1->fa_default = order;
1255 goto out; 1269 goto out;
1256 } 1270 }
1257 1271
1258 if (last_idx >= 0) 1272 if (last_idx >= 0)
1259 fib_result_assign(res, last_resort); 1273 fib_result_assign(res, last_resort);
1260 tb->tb_default = last_idx; 1274 fa1->fa_default = last_idx;
1261out: 1275out:
1262 return; 1276 return;
1263} 1277}