aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/route.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6/route.c')
-rw-r--r--net/ipv6/route.c21
1 files changed, 15 insertions, 6 deletions
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 79078747a646..8a777932786d 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -280,10 +280,13 @@ static int inline rt6_check_neigh(struct rt6_info *rt)
280{ 280{
281 struct neighbour *neigh = rt->rt6i_nexthop; 281 struct neighbour *neigh = rt->rt6i_nexthop;
282 int m = 0; 282 int m = 0;
283 if (neigh) { 283 if (rt->rt6i_flags & RTF_NONEXTHOP ||
284 !(rt->rt6i_flags & RTF_GATEWAY))
285 m = 1;
286 else if (neigh) {
284 read_lock_bh(&neigh->lock); 287 read_lock_bh(&neigh->lock);
285 if (neigh->nud_state & NUD_VALID) 288 if (neigh->nud_state & NUD_VALID)
286 m = 1; 289 m = 2;
287 read_unlock_bh(&neigh->lock); 290 read_unlock_bh(&neigh->lock);
288 } 291 }
289 return m; 292 return m;
@@ -292,15 +295,18 @@ static int inline rt6_check_neigh(struct rt6_info *rt)
292static int rt6_score_route(struct rt6_info *rt, int oif, 295static int rt6_score_route(struct rt6_info *rt, int oif,
293 int strict) 296 int strict)
294{ 297{
295 int m = rt6_check_dev(rt, oif); 298 int m, n;
299
300 m = rt6_check_dev(rt, oif);
296 if (!m && (strict & RT6_SELECT_F_IFACE)) 301 if (!m && (strict & RT6_SELECT_F_IFACE))
297 return -1; 302 return -1;
298#ifdef CONFIG_IPV6_ROUTER_PREF 303#ifdef CONFIG_IPV6_ROUTER_PREF
299 m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2; 304 m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
300#endif 305#endif
301 if (rt6_check_neigh(rt)) 306 n = rt6_check_neigh(rt);
307 if (n > 1)
302 m |= 16; 308 m |= 16;
303 else if (strict & RT6_SELECT_F_REACHABLE) 309 else if (!n && strict & RT6_SELECT_F_REACHABLE)
304 return -1; 310 return -1;
305 return m; 311 return m;
306} 312}
@@ -317,7 +323,7 @@ static struct rt6_info *rt6_select(struct rt6_info **head, int oif,
317 __FUNCTION__, head, head ? *head : NULL, oif); 323 __FUNCTION__, head, head ? *head : NULL, oif);
318 324
319 for (rt = rt0, metric = rt0->rt6i_metric; 325 for (rt = rt0, metric = rt0->rt6i_metric;
320 rt && rt->rt6i_metric == metric; 326 rt && rt->rt6i_metric == metric && (!last || rt != rt0);
321 rt = rt->u.next) { 327 rt = rt->u.next) {
322 int m; 328 int m;
323 329
@@ -343,9 +349,12 @@ static struct rt6_info *rt6_select(struct rt6_info **head, int oif,
343 (strict & RT6_SELECT_F_REACHABLE) && 349 (strict & RT6_SELECT_F_REACHABLE) &&
344 last && last != rt0) { 350 last && last != rt0) {
345 /* no entries matched; do round-robin */ 351 /* no entries matched; do round-robin */
352 static spinlock_t lock = SPIN_LOCK_UNLOCKED;
353 spin_lock(&lock);
346 *head = rt0->u.next; 354 *head = rt0->u.next;
347 rt0->u.next = last->u.next; 355 rt0->u.next = last->u.next;
348 last->u.next = rt0; 356 last->u.next = rt0;
357 spin_unlock(&lock);
349 } 358 }
350 359
351 RT6_TRACE("%s() => %p, score=%d\n", 360 RT6_TRACE("%s() => %p, score=%d\n",