aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/ipv6_route.h2
-rw-r--r--include/net/ip6_route.h21
-rw-r--r--include/net/ndisc.h2
-rw-r--r--net/ipv6/Kconfig8
-rw-r--r--net/ipv6/ndisc.c25
-rw-r--r--net/ipv6/route.c134
6 files changed, 191 insertions, 1 deletions
diff --git a/include/linux/ipv6_route.h b/include/linux/ipv6_route.h
index f4b085c91608..b323ff577967 100644
--- a/include/linux/ipv6_route.h
+++ b/include/linux/ipv6_route.h
@@ -23,6 +23,8 @@
23#define RTF_NONEXTHOP 0x00200000 /* route with no nexthop */ 23#define RTF_NONEXTHOP 0x00200000 /* route with no nexthop */
24#define RTF_EXPIRES 0x00400000 24#define RTF_EXPIRES 0x00400000
25 25
26#define RTF_ROUTEINFO 0x00800000 /* route information - RA */
27
26#define RTF_CACHE 0x01000000 /* cache entry */ 28#define RTF_CACHE 0x01000000 /* cache entry */
27#define RTF_FLOW 0x02000000 /* flow significant route */ 29#define RTF_FLOW 0x02000000 /* flow significant route */
28#define RTF_POLICY 0x04000000 /* policy route */ 30#define RTF_POLICY 0x04000000 /* policy route */
diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h
index 50161322b828..a398ae5e30f9 100644
--- a/include/net/ip6_route.h
+++ b/include/net/ip6_route.h
@@ -7,6 +7,23 @@
7#define IP6_RT_PRIO_KERN 512 7#define IP6_RT_PRIO_KERN 512
8#define IP6_RT_FLOW_MASK 0x00ff 8#define IP6_RT_FLOW_MASK 0x00ff
9 9
10struct route_info {
11 __u8 type;
12 __u8 length;
13 __u8 prefix_len;
14#if defined(__BIG_ENDIAN_BITFIELD)
15 __u8 reserved_h:3,
16 route_pref:2,
17 reserved_l:3;
18#elif defined(__LITTLE_ENDIAN_BITFIELD)
19 __u8 reserved_l:3,
20 route_pref:2,
21 reserved_h:3;
22#endif
23 __u32 lifetime;
24 __u8 prefix[0]; /* 0,8 or 16 */
25};
26
10#ifdef __KERNEL__ 27#ifdef __KERNEL__
11 28
12#include <net/flow.h> 29#include <net/flow.h>
@@ -92,6 +109,10 @@ extern struct rt6_info * rt6_add_dflt_router(struct in6_addr *gwaddr,
92 109
93extern void rt6_purge_dflt_routers(void); 110extern void rt6_purge_dflt_routers(void);
94 111
112extern int rt6_route_rcv(struct net_device *dev,
113 u8 *opt, int len,
114 struct in6_addr *gwaddr);
115
95extern void rt6_redirect(struct in6_addr *dest, 116extern void rt6_redirect(struct in6_addr *dest,
96 struct in6_addr *saddr, 117 struct in6_addr *saddr,
97 struct neighbour *neigh, 118 struct neighbour *neigh,
diff --git a/include/net/ndisc.h b/include/net/ndisc.h
index bbac87eeb422..91fa271a0064 100644
--- a/include/net/ndisc.h
+++ b/include/net/ndisc.h
@@ -22,6 +22,8 @@ enum {
22 ND_OPT_PREFIX_INFO = 3, /* RFC2461 */ 22 ND_OPT_PREFIX_INFO = 3, /* RFC2461 */
23 ND_OPT_REDIRECT_HDR = 4, /* RFC2461 */ 23 ND_OPT_REDIRECT_HDR = 4, /* RFC2461 */
24 ND_OPT_MTU = 5, /* RFC2461 */ 24 ND_OPT_MTU = 5, /* RFC2461 */
25 __ND_OPT_ARRAY_MAX,
26 ND_OPT_ROUTE_INFO = 24, /* RFC4191 */
25 __ND_OPT_MAX 27 __ND_OPT_MAX
26}; 28};
27 29
diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig
index c456ead8a4a3..e6f83b6a2b76 100644
--- a/net/ipv6/Kconfig
+++ b/net/ipv6/Kconfig
@@ -49,6 +49,14 @@ config IPV6_ROUTER_PREF
49 49
50 If unsure, say N. 50 If unsure, say N.
51 51
52config IPV6_ROUTE_INFO
53 bool "IPv6: Route Information (RFC 4191) support (EXPERIMENTAL)"
54 depends on IPV6_ROUTER_PREF && EXPERIMENTAL
55 ---help---
56 This is experimental support of Route Information.
57
58 If unsure, say N.
59
52config INET6_AH 60config INET6_AH
53 tristate "IPv6: AH transformation" 61 tristate "IPv6: AH transformation"
54 depends on IPV6 62 depends on IPV6
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index f4462ee33024..1f6256909674 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -156,7 +156,11 @@ struct neigh_table nd_tbl = {
156 156
157/* ND options */ 157/* ND options */
158struct ndisc_options { 158struct ndisc_options {
159 struct nd_opt_hdr *nd_opt_array[__ND_OPT_MAX]; 159 struct nd_opt_hdr *nd_opt_array[__ND_OPT_ARRAY_MAX];
160#ifdef CONFIG_IPV6_ROUTE_INFO
161 struct nd_opt_hdr *nd_opts_ri;
162 struct nd_opt_hdr *nd_opts_ri_end;
163#endif
160}; 164};
161 165
162#define nd_opts_src_lladdr nd_opt_array[ND_OPT_SOURCE_LL_ADDR] 166#define nd_opts_src_lladdr nd_opt_array[ND_OPT_SOURCE_LL_ADDR]
@@ -255,6 +259,13 @@ static struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len,
255 if (ndopts->nd_opt_array[nd_opt->nd_opt_type] == 0) 259 if (ndopts->nd_opt_array[nd_opt->nd_opt_type] == 0)
256 ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt; 260 ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt;
257 break; 261 break;
262#ifdef CONFIG_IPV6_ROUTE_INFO
263 case ND_OPT_ROUTE_INFO:
264 ndopts->nd_opts_ri_end = nd_opt;
265 if (!ndopts->nd_opts_ri)
266 ndopts->nd_opts_ri = nd_opt;
267 break;
268#endif
258 default: 269 default:
259 /* 270 /*
260 * Unknown options must be silently ignored, 271 * Unknown options must be silently ignored,
@@ -1202,6 +1213,18 @@ skip_defrtr:
1202 NEIGH_UPDATE_F_ISROUTER); 1213 NEIGH_UPDATE_F_ISROUTER);
1203 } 1214 }
1204 1215
1216#ifdef CONFIG_IPV6_ROUTE_INFO
1217 if (ndopts.nd_opts_ri) {
1218 struct nd_opt_hdr *p;
1219 for (p = ndopts.nd_opts_ri;
1220 p;
1221 p = ndisc_next_option(p, ndopts.nd_opts_ri_end)) {
1222 rt6_route_rcv(skb->dev, (u8*)p, (p->nd_opt_len) << 3,
1223 &skb->nh.ipv6h->saddr);
1224 }
1225 }
1226#endif
1227
1205 if (in6_dev->cnf.accept_ra_pinfo && ndopts.nd_opts_pi) { 1228 if (in6_dev->cnf.accept_ra_pinfo && ndopts.nd_opts_pi) {
1206 struct nd_opt_hdr *p; 1229 struct nd_opt_hdr *p;
1207 for (p = ndopts.nd_opts_pi; 1230 for (p = ndopts.nd_opts_pi;
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;