diff options
Diffstat (limited to 'net/ipv6/route.c')
-rw-r--r-- | net/ipv6/route.c | 134 |
1 files changed, 134 insertions, 0 deletions
diff --git a/net/ipv6/route.c b/net/ipv6/route.c index c797b9bbb7d1..0f30ee3d94ea 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c | |||
@@ -98,6 +98,14 @@ static int ip6_pkt_discard_out(struct sk_buff *skb); | |||
98 | static void ip6_link_failure(struct sk_buff *skb); | 98 | static void ip6_link_failure(struct sk_buff *skb); |
99 | static void ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu); | 99 | static void ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu); |
100 | 100 | ||
101 | #ifdef CONFIG_IPV6_ROUTE_INFO | ||
102 | static struct rt6_info *rt6_add_route_info(struct in6_addr *prefix, int prefixlen, | ||
103 | struct in6_addr *gwaddr, int ifindex, | ||
104 | unsigned pref); | ||
105 | static struct rt6_info *rt6_get_route_info(struct in6_addr *prefix, int prefixlen, | ||
106 | struct in6_addr *gwaddr, int ifindex); | ||
107 | #endif | ||
108 | |||
101 | static struct dst_ops ip6_dst_ops = { | 109 | static struct dst_ops ip6_dst_ops = { |
102 | .family = AF_INET6, | 110 | .family = AF_INET6, |
103 | .protocol = __constant_htons(ETH_P_IPV6), | 111 | .protocol = __constant_htons(ETH_P_IPV6), |
@@ -346,6 +354,84 @@ static struct rt6_info *rt6_select(struct rt6_info **head, int oif, | |||
346 | return (match ? match : &ip6_null_entry); | 354 | return (match ? match : &ip6_null_entry); |
347 | } | 355 | } |
348 | 356 | ||
357 | #ifdef CONFIG_IPV6_ROUTE_INFO | ||
358 | int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, | ||
359 | struct in6_addr *gwaddr) | ||
360 | { | ||
361 | struct route_info *rinfo = (struct route_info *) opt; | ||
362 | struct in6_addr prefix_buf, *prefix; | ||
363 | unsigned int pref; | ||
364 | u32 lifetime; | ||
365 | struct rt6_info *rt; | ||
366 | |||
367 | if (len < sizeof(struct route_info)) { | ||
368 | return -EINVAL; | ||
369 | } | ||
370 | |||
371 | /* Sanity check for prefix_len and length */ | ||
372 | if (rinfo->length > 3) { | ||
373 | return -EINVAL; | ||
374 | } else if (rinfo->prefix_len > 128) { | ||
375 | return -EINVAL; | ||
376 | } else if (rinfo->prefix_len > 64) { | ||
377 | if (rinfo->length < 2) { | ||
378 | return -EINVAL; | ||
379 | } | ||
380 | } else if (rinfo->prefix_len > 0) { | ||
381 | if (rinfo->length < 1) { | ||
382 | return -EINVAL; | ||
383 | } | ||
384 | } | ||
385 | |||
386 | pref = rinfo->route_pref; | ||
387 | if (pref == ICMPV6_ROUTER_PREF_INVALID) | ||
388 | pref = ICMPV6_ROUTER_PREF_MEDIUM; | ||
389 | |||
390 | lifetime = htonl(rinfo->lifetime); | ||
391 | if (lifetime == 0xffffffff) { | ||
392 | /* infinity */ | ||
393 | } else if (lifetime > 0x7fffffff/HZ) { | ||
394 | /* Avoid arithmetic overflow */ | ||
395 | lifetime = 0x7fffffff/HZ - 1; | ||
396 | } | ||
397 | |||
398 | if (rinfo->length == 3) | ||
399 | prefix = (struct in6_addr *)rinfo->prefix; | ||
400 | else { | ||
401 | /* this function is safe */ | ||
402 | ipv6_addr_prefix(&prefix_buf, | ||
403 | (struct in6_addr *)rinfo->prefix, | ||
404 | rinfo->prefix_len); | ||
405 | prefix = &prefix_buf; | ||
406 | } | ||
407 | |||
408 | rt = rt6_get_route_info(prefix, rinfo->prefix_len, gwaddr, dev->ifindex); | ||
409 | |||
410 | if (rt && !lifetime) { | ||
411 | ip6_del_rt(rt, NULL, NULL, NULL); | ||
412 | rt = NULL; | ||
413 | } | ||
414 | |||
415 | if (!rt && lifetime) | ||
416 | rt = rt6_add_route_info(prefix, rinfo->prefix_len, gwaddr, dev->ifindex, | ||
417 | pref); | ||
418 | else if (rt) | ||
419 | rt->rt6i_flags = RTF_ROUTEINFO | | ||
420 | (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref); | ||
421 | |||
422 | if (rt) { | ||
423 | if (lifetime == 0xffffffff) { | ||
424 | rt->rt6i_flags &= ~RTF_EXPIRES; | ||
425 | } else { | ||
426 | rt->rt6i_expires = jiffies + HZ * lifetime; | ||
427 | rt->rt6i_flags |= RTF_EXPIRES; | ||
428 | } | ||
429 | dst_release(&rt->u.dst); | ||
430 | } | ||
431 | return 0; | ||
432 | } | ||
433 | #endif | ||
434 | |||
349 | struct rt6_info *rt6_lookup(struct in6_addr *daddr, struct in6_addr *saddr, | 435 | struct rt6_info *rt6_lookup(struct in6_addr *daddr, struct in6_addr *saddr, |
350 | int oif, int strict) | 436 | int oif, int strict) |
351 | { | 437 | { |
@@ -1277,6 +1363,54 @@ static struct rt6_info * ip6_rt_copy(struct rt6_info *ort) | |||
1277 | return rt; | 1363 | return rt; |
1278 | } | 1364 | } |
1279 | 1365 | ||
1366 | #ifdef CONFIG_IPV6_ROUTE_INFO | ||
1367 | static struct rt6_info *rt6_get_route_info(struct in6_addr *prefix, int prefixlen, | ||
1368 | struct in6_addr *gwaddr, int ifindex) | ||
1369 | { | ||
1370 | struct fib6_node *fn; | ||
1371 | struct rt6_info *rt = NULL; | ||
1372 | |||
1373 | write_lock_bh(&rt6_lock); | ||
1374 | fn = fib6_locate(&ip6_routing_table, prefix ,prefixlen, NULL, 0); | ||
1375 | if (!fn) | ||
1376 | goto out; | ||
1377 | |||
1378 | for (rt = fn->leaf; rt; rt = rt->u.next) { | ||
1379 | if (rt->rt6i_dev->ifindex != ifindex) | ||
1380 | continue; | ||
1381 | if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY)) | ||
1382 | continue; | ||
1383 | if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr)) | ||
1384 | continue; | ||
1385 | dst_hold(&rt->u.dst); | ||
1386 | break; | ||
1387 | } | ||
1388 | out: | ||
1389 | write_unlock_bh(&rt6_lock); | ||
1390 | return rt; | ||
1391 | } | ||
1392 | |||
1393 | static struct rt6_info *rt6_add_route_info(struct in6_addr *prefix, int prefixlen, | ||
1394 | struct in6_addr *gwaddr, int ifindex, | ||
1395 | unsigned pref) | ||
1396 | { | ||
1397 | struct in6_rtmsg rtmsg; | ||
1398 | |||
1399 | memset(&rtmsg, 0, sizeof(rtmsg)); | ||
1400 | rtmsg.rtmsg_type = RTMSG_NEWROUTE; | ||
1401 | ipv6_addr_copy(&rtmsg.rtmsg_dst, prefix); | ||
1402 | rtmsg.rtmsg_dst_len = prefixlen; | ||
1403 | ipv6_addr_copy(&rtmsg.rtmsg_gateway, gwaddr); | ||
1404 | rtmsg.rtmsg_metric = 1024; | ||
1405 | rtmsg.rtmsg_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO | RTF_UP | RTF_PREF(pref); | ||
1406 | rtmsg.rtmsg_ifindex = ifindex; | ||
1407 | |||
1408 | ip6_route_add(&rtmsg, NULL, NULL, NULL); | ||
1409 | |||
1410 | return rt6_get_route_info(prefix, prefixlen, gwaddr, ifindex); | ||
1411 | } | ||
1412 | #endif | ||
1413 | |||
1280 | struct rt6_info *rt6_get_dflt_router(struct in6_addr *addr, struct net_device *dev) | 1414 | struct rt6_info *rt6_get_dflt_router(struct in6_addr *addr, struct net_device *dev) |
1281 | { | 1415 | { |
1282 | struct rt6_info *rt; | 1416 | struct rt6_info *rt; |