diff options
Diffstat (limited to 'net/ipv6/route.c')
-rw-r--r-- | net/ipv6/route.c | 21 |
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) | |||
292 | static int rt6_score_route(struct rt6_info *rt, int oif, | 295 | static 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", |