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.c134
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);
98static void ip6_link_failure(struct sk_buff *skb); 98static void ip6_link_failure(struct sk_buff *skb);
99static void ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu); 99static void ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu);
100 100
101#ifdef CONFIG_IPV6_ROUTE_INFO
102static struct rt6_info *rt6_add_route_info(struct in6_addr *prefix, int prefixlen,
103 struct in6_addr *gwaddr, int ifindex,
104 unsigned pref);
105static struct rt6_info *rt6_get_route_info(struct in6_addr *prefix, int prefixlen,
106 struct in6_addr *gwaddr, int ifindex);
107#endif
108
101static struct dst_ops ip6_dst_ops = { 109static 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
358int 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
349struct rt6_info *rt6_lookup(struct in6_addr *daddr, struct in6_addr *saddr, 435struct 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
1367static 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 }
1388out:
1389 write_unlock_bh(&rt6_lock);
1390 return rt;
1391}
1392
1393static 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
1280struct rt6_info *rt6_get_dflt_router(struct in6_addr *addr, struct net_device *dev) 1414struct 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;