diff options
-rw-r--r-- | include/linux/ipv6_route.h | 2 | ||||
-rw-r--r-- | include/net/ip6_route.h | 21 | ||||
-rw-r--r-- | include/net/ndisc.h | 2 | ||||
-rw-r--r-- | net/ipv6/Kconfig | 8 | ||||
-rw-r--r-- | net/ipv6/ndisc.c | 25 | ||||
-rw-r--r-- | net/ipv6/route.c | 134 |
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 | ||
10 | struct 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 | ||
93 | extern void rt6_purge_dflt_routers(void); | 110 | extern void rt6_purge_dflt_routers(void); |
94 | 111 | ||
112 | extern int rt6_route_rcv(struct net_device *dev, | ||
113 | u8 *opt, int len, | ||
114 | struct in6_addr *gwaddr); | ||
115 | |||
95 | extern void rt6_redirect(struct in6_addr *dest, | 116 | extern 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 | ||
52 | config 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 | |||
52 | config INET6_AH | 60 | config 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 */ |
158 | struct ndisc_options { | 158 | struct 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); | |||
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; |