diff options
author | YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org> | 2006-03-20 20:06:24 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2006-03-20 20:06:24 -0500 |
commit | 70ceb4f53929f73746be72f73707cd9f8753e2fc (patch) | |
tree | e562929b0c800b0243e203587065e05323baba59 /net | |
parent | 52e1635631b342803aecaf81a362c1464e3da2e5 (diff) |
[IPV6]: ROUTE: Add experimental support for Route Information Option in RA (RFC4191).
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv6/Kconfig | 8 | ||||
-rw-r--r-- | net/ipv6/ndisc.c | 25 | ||||
-rw-r--r-- | net/ipv6/route.c | 134 |
3 files changed, 166 insertions, 1 deletions
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; |