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 | |
| 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>
| -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; |
