diff options
Diffstat (limited to 'net/ipv6/route.c')
-rw-r--r-- | net/ipv6/route.c | 197 |
1 files changed, 69 insertions, 128 deletions
diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 6a4019a4ca89..f71e2365b43d 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c | |||
@@ -74,6 +74,9 @@ | |||
74 | 74 | ||
75 | #define CLONE_OFFLINK_ROUTE 0 | 75 | #define CLONE_OFFLINK_ROUTE 0 |
76 | 76 | ||
77 | #define RT6_SELECT_F_IFACE 0x1 | ||
78 | #define RT6_SELECT_F_REACHABLE 0x2 | ||
79 | |||
77 | static int ip6_rt_max_size = 4096; | 80 | static int ip6_rt_max_size = 4096; |
78 | static int ip6_rt_gc_min_interval = HZ / 2; | 81 | static int ip6_rt_gc_min_interval = HZ / 2; |
79 | static int ip6_rt_gc_timeout = 60*HZ; | 82 | static int ip6_rt_gc_timeout = 60*HZ; |
@@ -216,148 +219,89 @@ static __inline__ struct rt6_info *rt6_device_match(struct rt6_info *rt, | |||
216 | } | 219 | } |
217 | 220 | ||
218 | /* | 221 | /* |
219 | * pointer to the last default router chosen. BH is disabled locally. | 222 | * Default Router Selection (RFC 2461 6.3.6) |
220 | */ | 223 | */ |
221 | static struct rt6_info *rt6_dflt_pointer; | 224 | static int inline rt6_check_dev(struct rt6_info *rt, int oif) |
222 | static DEFINE_SPINLOCK(rt6_dflt_lock); | 225 | { |
226 | struct net_device *dev = rt->rt6i_dev; | ||
227 | if (!oif || dev->ifindex == oif) | ||
228 | return 2; | ||
229 | if ((dev->flags & IFF_LOOPBACK) && | ||
230 | rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif) | ||
231 | return 1; | ||
232 | return 0; | ||
233 | } | ||
223 | 234 | ||
224 | void rt6_reset_dflt_pointer(struct rt6_info *rt) | 235 | static int inline rt6_check_neigh(struct rt6_info *rt) |
225 | { | 236 | { |
226 | spin_lock_bh(&rt6_dflt_lock); | 237 | struct neighbour *neigh = rt->rt6i_nexthop; |
227 | if (rt == NULL || rt == rt6_dflt_pointer) { | 238 | int m = 0; |
228 | RT6_TRACE("reset default router: %p->NULL\n", rt6_dflt_pointer); | 239 | if (neigh) { |
229 | rt6_dflt_pointer = NULL; | 240 | read_lock_bh(&neigh->lock); |
241 | if (neigh->nud_state & NUD_VALID) | ||
242 | m = 1; | ||
243 | read_unlock_bh(&neigh->lock); | ||
230 | } | 244 | } |
231 | spin_unlock_bh(&rt6_dflt_lock); | 245 | return m; |
232 | } | 246 | } |
233 | 247 | ||
234 | /* Default Router Selection (RFC 2461 6.3.6) */ | 248 | static int rt6_score_route(struct rt6_info *rt, int oif, |
235 | static struct rt6_info *rt6_best_dflt(struct rt6_info *rt, int oif) | 249 | int strict) |
236 | { | 250 | { |
237 | struct rt6_info *match = NULL; | 251 | int m = rt6_check_dev(rt, oif); |
238 | struct rt6_info *sprt; | 252 | if (!m && (strict & RT6_SELECT_F_IFACE)) |
239 | int mpri = 0; | 253 | return -1; |
254 | if (rt6_check_neigh(rt)) | ||
255 | m |= 4; | ||
256 | else if (strict & RT6_SELECT_F_REACHABLE) | ||
257 | return -1; | ||
258 | return m; | ||
259 | } | ||
240 | 260 | ||
241 | for (sprt = rt; sprt; sprt = sprt->u.next) { | 261 | static struct rt6_info *rt6_select(struct rt6_info **head, int oif, |
242 | struct neighbour *neigh; | 262 | int strict) |
243 | int m = 0; | 263 | { |
264 | struct rt6_info *match = NULL, *last = NULL; | ||
265 | struct rt6_info *rt, *rt0 = *head; | ||
266 | u32 metric; | ||
267 | int mpri = -1; | ||
244 | 268 | ||
245 | if (!oif || | 269 | RT6_TRACE("%s(head=%p(*head=%p), oif=%d)\n", |
246 | (sprt->rt6i_dev && | 270 | __FUNCTION__, head, head ? *head : NULL, oif); |
247 | sprt->rt6i_dev->ifindex == oif)) | ||
248 | m += 8; | ||
249 | 271 | ||
250 | if (rt6_check_expired(sprt)) | 272 | for (rt = rt0, metric = rt0->rt6i_metric; |
273 | rt && rt->rt6i_metric == metric; | ||
274 | rt = rt->u.next) { | ||
275 | int m; | ||
276 | |||
277 | if (rt6_check_expired(rt)) | ||
251 | continue; | 278 | continue; |
252 | 279 | ||
253 | if (sprt == rt6_dflt_pointer) | 280 | last = rt; |
254 | m += 4; | 281 | |
255 | 282 | m = rt6_score_route(rt, oif, strict); | |
256 | if ((neigh = sprt->rt6i_nexthop) != NULL) { | 283 | if (m < 0) |
257 | read_lock_bh(&neigh->lock); | ||
258 | switch (neigh->nud_state) { | ||
259 | case NUD_REACHABLE: | ||
260 | m += 3; | ||
261 | break; | ||
262 | |||
263 | case NUD_STALE: | ||
264 | case NUD_DELAY: | ||
265 | case NUD_PROBE: | ||
266 | m += 2; | ||
267 | break; | ||
268 | |||
269 | case NUD_NOARP: | ||
270 | case NUD_PERMANENT: | ||
271 | m += 1; | ||
272 | break; | ||
273 | |||
274 | case NUD_INCOMPLETE: | ||
275 | default: | ||
276 | read_unlock_bh(&neigh->lock); | ||
277 | continue; | ||
278 | } | ||
279 | read_unlock_bh(&neigh->lock); | ||
280 | } else { | ||
281 | continue; | 284 | continue; |
282 | } | ||
283 | 285 | ||
284 | if (m > mpri || m >= 12) { | 286 | if (m > mpri) { |
285 | match = sprt; | 287 | match = rt; |
286 | mpri = m; | 288 | mpri = m; |
287 | if (m >= 12) { | ||
288 | /* we choose the last default router if it | ||
289 | * is in (probably) reachable state. | ||
290 | * If route changed, we should do pmtu | ||
291 | * discovery. --yoshfuji | ||
292 | */ | ||
293 | break; | ||
294 | } | ||
295 | } | 289 | } |
296 | } | 290 | } |
297 | 291 | ||
298 | spin_lock(&rt6_dflt_lock); | 292 | if (!match && |
299 | if (!match) { | 293 | (strict & RT6_SELECT_F_REACHABLE) && |
300 | /* | 294 | last && last != rt0) { |
301 | * No default routers are known to be reachable. | 295 | /* no entries matched; do round-robin */ |
302 | * SHOULD round robin | 296 | *head = rt0->u.next; |
303 | */ | 297 | rt0->u.next = last->u.next; |
304 | if (rt6_dflt_pointer) { | 298 | last->u.next = rt0; |
305 | for (sprt = rt6_dflt_pointer->u.next; | ||
306 | sprt; sprt = sprt->u.next) { | ||
307 | if (sprt->u.dst.obsolete <= 0 && | ||
308 | sprt->u.dst.error == 0 && | ||
309 | !rt6_check_expired(sprt)) { | ||
310 | match = sprt; | ||
311 | break; | ||
312 | } | ||
313 | } | ||
314 | for (sprt = rt; | ||
315 | !match && sprt; | ||
316 | sprt = sprt->u.next) { | ||
317 | if (sprt->u.dst.obsolete <= 0 && | ||
318 | sprt->u.dst.error == 0 && | ||
319 | !rt6_check_expired(sprt)) { | ||
320 | match = sprt; | ||
321 | break; | ||
322 | } | ||
323 | if (sprt == rt6_dflt_pointer) | ||
324 | break; | ||
325 | } | ||
326 | } | ||
327 | } | ||
328 | |||
329 | if (match) { | ||
330 | if (rt6_dflt_pointer != match) | ||
331 | RT6_TRACE("changed default router: %p->%p\n", | ||
332 | rt6_dflt_pointer, match); | ||
333 | rt6_dflt_pointer = match; | ||
334 | } | 299 | } |
335 | spin_unlock(&rt6_dflt_lock); | ||
336 | 300 | ||
337 | if (!match) { | 301 | RT6_TRACE("%s() => %p, score=%d\n", |
338 | /* | 302 | __FUNCTION__, match, mpri); |
339 | * Last Resort: if no default routers found, | ||
340 | * use addrconf default route. | ||
341 | * We don't record this route. | ||
342 | */ | ||
343 | for (sprt = ip6_routing_table.leaf; | ||
344 | sprt; sprt = sprt->u.next) { | ||
345 | if (!rt6_check_expired(sprt) && | ||
346 | (sprt->rt6i_flags & RTF_DEFAULT) && | ||
347 | (!oif || | ||
348 | (sprt->rt6i_dev && | ||
349 | sprt->rt6i_dev->ifindex == oif))) { | ||
350 | match = sprt; | ||
351 | break; | ||
352 | } | ||
353 | } | ||
354 | if (!match) { | ||
355 | /* no default route. give up. */ | ||
356 | match = &ip6_null_entry; | ||
357 | } | ||
358 | } | ||
359 | 303 | ||
360 | return match; | 304 | return (match ? match : &ip6_null_entry); |
361 | } | 305 | } |
362 | 306 | ||
363 | struct rt6_info *rt6_lookup(struct in6_addr *daddr, struct in6_addr *saddr, | 307 | struct rt6_info *rt6_lookup(struct in6_addr *daddr, struct in6_addr *saddr, |
@@ -542,7 +486,7 @@ struct dst_entry * ip6_route_output(struct sock *sk, struct flowi *fl) | |||
542 | int attempts = 3; | 486 | int attempts = 3; |
543 | int err; | 487 | int err; |
544 | 488 | ||
545 | strict = ipv6_addr_type(&fl->fl6_dst) & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL); | 489 | strict = ipv6_addr_type(&fl->fl6_dst) & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL) ? RT6_SELECT_F_IFACE : 0; |
546 | 490 | ||
547 | relookup: | 491 | relookup: |
548 | read_lock_bh(&rt6_lock); | 492 | read_lock_bh(&rt6_lock); |
@@ -558,8 +502,9 @@ restart: | |||
558 | goto out; | 502 | goto out; |
559 | } | 503 | } |
560 | if (rt->rt6i_flags & RTF_DEFAULT) { | 504 | if (rt->rt6i_flags & RTF_DEFAULT) { |
561 | if (rt->rt6i_metric >= IP6_RT_PRIO_ADDRCONF) | 505 | rt = rt6_select(&fn->leaf, fl->oif, strict | RT6_SELECT_F_REACHABLE); |
562 | rt = rt6_best_dflt(rt, fl->oif); | 506 | if (rt == &ip6_null_entry) |
507 | rt = rt6_select(&fn->leaf, fl->oif, strict); | ||
563 | } else { | 508 | } else { |
564 | rt = rt6_device_match(rt, fl->oif, strict); | 509 | rt = rt6_device_match(rt, fl->oif, strict); |
565 | BACKTRACK(); | 510 | BACKTRACK(); |
@@ -1025,8 +970,6 @@ int ip6_del_rt(struct rt6_info *rt, struct nlmsghdr *nlh, void *_rtattr, struct | |||
1025 | 970 | ||
1026 | write_lock_bh(&rt6_lock); | 971 | write_lock_bh(&rt6_lock); |
1027 | 972 | ||
1028 | rt6_reset_dflt_pointer(NULL); | ||
1029 | |||
1030 | err = fib6_del(rt, nlh, _rtattr, req); | 973 | err = fib6_del(rt, nlh, _rtattr, req); |
1031 | dst_release(&rt->u.dst); | 974 | dst_release(&rt->u.dst); |
1032 | 975 | ||
@@ -1341,8 +1284,6 @@ restart: | |||
1341 | if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) { | 1284 | if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) { |
1342 | dst_hold(&rt->u.dst); | 1285 | dst_hold(&rt->u.dst); |
1343 | 1286 | ||
1344 | rt6_reset_dflt_pointer(NULL); | ||
1345 | |||
1346 | read_unlock_bh(&rt6_lock); | 1287 | read_unlock_bh(&rt6_lock); |
1347 | 1288 | ||
1348 | ip6_del_rt(rt, NULL, NULL, NULL); | 1289 | ip6_del_rt(rt, NULL, NULL, NULL); |