Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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

authored by

YOSHIFUJI Hideaki and committed by
David S. Miller
70ceb4f5 52e16356

+191 -1
+2
include/linux/ipv6_route.h
··· 23 23 #define RTF_NONEXTHOP 0x00200000 /* route with no nexthop */ 24 24 #define RTF_EXPIRES 0x00400000 25 25 26 + #define RTF_ROUTEINFO 0x00800000 /* route information - RA */ 27 + 26 28 #define RTF_CACHE 0x01000000 /* cache entry */ 27 29 #define RTF_FLOW 0x02000000 /* flow significant route */ 28 30 #define RTF_POLICY 0x04000000 /* policy route */
+21
include/net/ip6_route.h
··· 7 7 #define IP6_RT_PRIO_KERN 512 8 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 27 #ifdef __KERNEL__ 11 28 12 29 #include <net/flow.h> ··· 108 91 unsigned int pref); 109 92 110 93 extern void rt6_purge_dflt_routers(void); 94 + 95 + extern int rt6_route_rcv(struct net_device *dev, 96 + u8 *opt, int len, 97 + struct in6_addr *gwaddr); 111 98 112 99 extern void rt6_redirect(struct in6_addr *dest, 113 100 struct in6_addr *saddr,
+2
include/net/ndisc.h
··· 22 22 ND_OPT_PREFIX_INFO = 3, /* RFC2461 */ 23 23 ND_OPT_REDIRECT_HDR = 4, /* RFC2461 */ 24 24 ND_OPT_MTU = 5, /* RFC2461 */ 25 + __ND_OPT_ARRAY_MAX, 26 + ND_OPT_ROUTE_INFO = 24, /* RFC4191 */ 25 27 __ND_OPT_MAX 26 28 }; 27 29
+8
net/ipv6/Kconfig
··· 49 49 50 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 60 config INET6_AH 53 61 tristate "IPv6: AH transformation" 54 62 depends on IPV6
+24 -1
net/ipv6/ndisc.c
··· 156 156 157 157 /* ND options */ 158 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 166 #define nd_opts_src_lladdr nd_opt_array[ND_OPT_SOURCE_LL_ADDR] ··· 259 255 if (ndopts->nd_opt_array[nd_opt->nd_opt_type] == 0) 260 256 ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt; 261 257 break; 258 + #ifdef CONFIG_IPV6_ROUTE_INFO 259 + case ND_OPT_ROUTE_INFO: 260 + ndopts->nd_opts_ri_end = nd_opt; 261 + if (!ndopts->nd_opts_ri) 262 + ndopts->nd_opts_ri = nd_opt; 263 + break; 264 + #endif 262 265 default: 263 266 /* 264 267 * Unknown options must be silently ignored, ··· 1212 1201 NEIGH_UPDATE_F_OVERRIDE_ISROUTER| 1213 1202 NEIGH_UPDATE_F_ISROUTER); 1214 1203 } 1204 + 1205 + #ifdef CONFIG_IPV6_ROUTE_INFO 1206 + if (ndopts.nd_opts_ri) { 1207 + struct nd_opt_hdr *p; 1208 + for (p = ndopts.nd_opts_ri; 1209 + p; 1210 + p = ndisc_next_option(p, ndopts.nd_opts_ri_end)) { 1211 + rt6_route_rcv(skb->dev, (u8*)p, (p->nd_opt_len) << 3, 1212 + &skb->nh.ipv6h->saddr); 1213 + } 1214 + } 1215 + #endif 1215 1216 1216 1217 if (in6_dev->cnf.accept_ra_pinfo && ndopts.nd_opts_pi) { 1217 1218 struct nd_opt_hdr *p;
+134
net/ipv6/route.c
··· 98 98 static void ip6_link_failure(struct sk_buff *skb); 99 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 109 static struct dst_ops ip6_dst_ops = { 102 110 .family = AF_INET6, 103 111 .protocol = __constant_htons(ETH_P_IPV6), ··· 353 345 354 346 return (match ? match : &ip6_null_entry); 355 347 } 348 + 349 + #ifdef CONFIG_IPV6_ROUTE_INFO 350 + int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, 351 + struct in6_addr *gwaddr) 352 + { 353 + struct route_info *rinfo = (struct route_info *) opt; 354 + struct in6_addr prefix_buf, *prefix; 355 + unsigned int pref; 356 + u32 lifetime; 357 + struct rt6_info *rt; 358 + 359 + if (len < sizeof(struct route_info)) { 360 + return -EINVAL; 361 + } 362 + 363 + /* Sanity check for prefix_len and length */ 364 + if (rinfo->length > 3) { 365 + return -EINVAL; 366 + } else if (rinfo->prefix_len > 128) { 367 + return -EINVAL; 368 + } else if (rinfo->prefix_len > 64) { 369 + if (rinfo->length < 2) { 370 + return -EINVAL; 371 + } 372 + } else if (rinfo->prefix_len > 0) { 373 + if (rinfo->length < 1) { 374 + return -EINVAL; 375 + } 376 + } 377 + 378 + pref = rinfo->route_pref; 379 + if (pref == ICMPV6_ROUTER_PREF_INVALID) 380 + pref = ICMPV6_ROUTER_PREF_MEDIUM; 381 + 382 + lifetime = htonl(rinfo->lifetime); 383 + if (lifetime == 0xffffffff) { 384 + /* infinity */ 385 + } else if (lifetime > 0x7fffffff/HZ) { 386 + /* Avoid arithmetic overflow */ 387 + lifetime = 0x7fffffff/HZ - 1; 388 + } 389 + 390 + if (rinfo->length == 3) 391 + prefix = (struct in6_addr *)rinfo->prefix; 392 + else { 393 + /* this function is safe */ 394 + ipv6_addr_prefix(&prefix_buf, 395 + (struct in6_addr *)rinfo->prefix, 396 + rinfo->prefix_len); 397 + prefix = &prefix_buf; 398 + } 399 + 400 + rt = rt6_get_route_info(prefix, rinfo->prefix_len, gwaddr, dev->ifindex); 401 + 402 + if (rt && !lifetime) { 403 + ip6_del_rt(rt, NULL, NULL, NULL); 404 + rt = NULL; 405 + } 406 + 407 + if (!rt && lifetime) 408 + rt = rt6_add_route_info(prefix, rinfo->prefix_len, gwaddr, dev->ifindex, 409 + pref); 410 + else if (rt) 411 + rt->rt6i_flags = RTF_ROUTEINFO | 412 + (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref); 413 + 414 + if (rt) { 415 + if (lifetime == 0xffffffff) { 416 + rt->rt6i_flags &= ~RTF_EXPIRES; 417 + } else { 418 + rt->rt6i_expires = jiffies + HZ * lifetime; 419 + rt->rt6i_flags |= RTF_EXPIRES; 420 + } 421 + dst_release(&rt->u.dst); 422 + } 423 + return 0; 424 + } 425 + #endif 356 426 357 427 struct rt6_info *rt6_lookup(struct in6_addr *daddr, struct in6_addr *saddr, 358 428 int oif, int strict) ··· 1362 1276 } 1363 1277 return rt; 1364 1278 } 1279 + 1280 + #ifdef CONFIG_IPV6_ROUTE_INFO 1281 + static struct rt6_info *rt6_get_route_info(struct in6_addr *prefix, int prefixlen, 1282 + struct in6_addr *gwaddr, int ifindex) 1283 + { 1284 + struct fib6_node *fn; 1285 + struct rt6_info *rt = NULL; 1286 + 1287 + write_lock_bh(&rt6_lock); 1288 + fn = fib6_locate(&ip6_routing_table, prefix ,prefixlen, NULL, 0); 1289 + if (!fn) 1290 + goto out; 1291 + 1292 + for (rt = fn->leaf; rt; rt = rt->u.next) { 1293 + if (rt->rt6i_dev->ifindex != ifindex) 1294 + continue; 1295 + if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY)) 1296 + continue; 1297 + if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr)) 1298 + continue; 1299 + dst_hold(&rt->u.dst); 1300 + break; 1301 + } 1302 + out: 1303 + write_unlock_bh(&rt6_lock); 1304 + return rt; 1305 + } 1306 + 1307 + static struct rt6_info *rt6_add_route_info(struct in6_addr *prefix, int prefixlen, 1308 + struct in6_addr *gwaddr, int ifindex, 1309 + unsigned pref) 1310 + { 1311 + struct in6_rtmsg rtmsg; 1312 + 1313 + memset(&rtmsg, 0, sizeof(rtmsg)); 1314 + rtmsg.rtmsg_type = RTMSG_NEWROUTE; 1315 + ipv6_addr_copy(&rtmsg.rtmsg_dst, prefix); 1316 + rtmsg.rtmsg_dst_len = prefixlen; 1317 + ipv6_addr_copy(&rtmsg.rtmsg_gateway, gwaddr); 1318 + rtmsg.rtmsg_metric = 1024; 1319 + rtmsg.rtmsg_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO | RTF_UP | RTF_PREF(pref); 1320 + rtmsg.rtmsg_ifindex = ifindex; 1321 + 1322 + ip6_route_add(&rtmsg, NULL, NULL, NULL); 1323 + 1324 + return rt6_get_route_info(prefix, prefixlen, gwaddr, ifindex); 1325 + } 1326 + #endif 1365 1327 1366 1328 struct rt6_info *rt6_get_dflt_router(struct in6_addr *addr, struct net_device *dev) 1367 1329 {