[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>
This commit is contained in:
Родитель
52e1635631
Коммит
70ceb4f539
|
@ -23,6 +23,8 @@
|
|||
#define RTF_NONEXTHOP 0x00200000 /* route with no nexthop */
|
||||
#define RTF_EXPIRES 0x00400000
|
||||
|
||||
#define RTF_ROUTEINFO 0x00800000 /* route information - RA */
|
||||
|
||||
#define RTF_CACHE 0x01000000 /* cache entry */
|
||||
#define RTF_FLOW 0x02000000 /* flow significant route */
|
||||
#define RTF_POLICY 0x04000000 /* policy route */
|
||||
|
|
|
@ -7,6 +7,23 @@
|
|||
#define IP6_RT_PRIO_KERN 512
|
||||
#define IP6_RT_FLOW_MASK 0x00ff
|
||||
|
||||
struct route_info {
|
||||
__u8 type;
|
||||
__u8 length;
|
||||
__u8 prefix_len;
|
||||
#if defined(__BIG_ENDIAN_BITFIELD)
|
||||
__u8 reserved_h:3,
|
||||
route_pref:2,
|
||||
reserved_l:3;
|
||||
#elif defined(__LITTLE_ENDIAN_BITFIELD)
|
||||
__u8 reserved_l:3,
|
||||
route_pref:2,
|
||||
reserved_h:3;
|
||||
#endif
|
||||
__u32 lifetime;
|
||||
__u8 prefix[0]; /* 0,8 or 16 */
|
||||
};
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
#include <net/flow.h>
|
||||
|
@ -92,6 +109,10 @@ extern struct rt6_info * rt6_add_dflt_router(struct in6_addr *gwaddr,
|
|||
|
||||
extern void rt6_purge_dflt_routers(void);
|
||||
|
||||
extern int rt6_route_rcv(struct net_device *dev,
|
||||
u8 *opt, int len,
|
||||
struct in6_addr *gwaddr);
|
||||
|
||||
extern void rt6_redirect(struct in6_addr *dest,
|
||||
struct in6_addr *saddr,
|
||||
struct neighbour *neigh,
|
||||
|
|
|
@ -22,6 +22,8 @@ enum {
|
|||
ND_OPT_PREFIX_INFO = 3, /* RFC2461 */
|
||||
ND_OPT_REDIRECT_HDR = 4, /* RFC2461 */
|
||||
ND_OPT_MTU = 5, /* RFC2461 */
|
||||
__ND_OPT_ARRAY_MAX,
|
||||
ND_OPT_ROUTE_INFO = 24, /* RFC4191 */
|
||||
__ND_OPT_MAX
|
||||
};
|
||||
|
||||
|
|
|
@ -49,6 +49,14 @@ config IPV6_ROUTER_PREF
|
|||
|
||||
If unsure, say N.
|
||||
|
||||
config IPV6_ROUTE_INFO
|
||||
bool "IPv6: Route Information (RFC 4191) support (EXPERIMENTAL)"
|
||||
depends on IPV6_ROUTER_PREF && EXPERIMENTAL
|
||||
---help---
|
||||
This is experimental support of Route Information.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config INET6_AH
|
||||
tristate "IPv6: AH transformation"
|
||||
depends on IPV6
|
||||
|
|
|
@ -156,7 +156,11 @@ struct neigh_table nd_tbl = {
|
|||
|
||||
/* ND options */
|
||||
struct ndisc_options {
|
||||
struct nd_opt_hdr *nd_opt_array[__ND_OPT_MAX];
|
||||
struct nd_opt_hdr *nd_opt_array[__ND_OPT_ARRAY_MAX];
|
||||
#ifdef CONFIG_IPV6_ROUTE_INFO
|
||||
struct nd_opt_hdr *nd_opts_ri;
|
||||
struct nd_opt_hdr *nd_opts_ri_end;
|
||||
#endif
|
||||
};
|
||||
|
||||
#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,
|
|||
if (ndopts->nd_opt_array[nd_opt->nd_opt_type] == 0)
|
||||
ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt;
|
||||
break;
|
||||
#ifdef CONFIG_IPV6_ROUTE_INFO
|
||||
case ND_OPT_ROUTE_INFO:
|
||||
ndopts->nd_opts_ri_end = nd_opt;
|
||||
if (!ndopts->nd_opts_ri)
|
||||
ndopts->nd_opts_ri = nd_opt;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
/*
|
||||
* Unknown options must be silently ignored,
|
||||
|
@ -1202,6 +1213,18 @@ skip_defrtr:
|
|||
NEIGH_UPDATE_F_ISROUTER);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IPV6_ROUTE_INFO
|
||||
if (ndopts.nd_opts_ri) {
|
||||
struct nd_opt_hdr *p;
|
||||
for (p = ndopts.nd_opts_ri;
|
||||
p;
|
||||
p = ndisc_next_option(p, ndopts.nd_opts_ri_end)) {
|
||||
rt6_route_rcv(skb->dev, (u8*)p, (p->nd_opt_len) << 3,
|
||||
&skb->nh.ipv6h->saddr);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (in6_dev->cnf.accept_ra_pinfo && ndopts.nd_opts_pi) {
|
||||
struct nd_opt_hdr *p;
|
||||
for (p = ndopts.nd_opts_pi;
|
||||
|
|
134
net/ipv6/route.c
134
net/ipv6/route.c
|
@ -98,6 +98,14 @@ static int ip6_pkt_discard_out(struct sk_buff *skb);
|
|||
static void ip6_link_failure(struct sk_buff *skb);
|
||||
static void ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu);
|
||||
|
||||
#ifdef CONFIG_IPV6_ROUTE_INFO
|
||||
static struct rt6_info *rt6_add_route_info(struct in6_addr *prefix, int prefixlen,
|
||||
struct in6_addr *gwaddr, int ifindex,
|
||||
unsigned pref);
|
||||
static struct rt6_info *rt6_get_route_info(struct in6_addr *prefix, int prefixlen,
|
||||
struct in6_addr *gwaddr, int ifindex);
|
||||
#endif
|
||||
|
||||
static struct dst_ops ip6_dst_ops = {
|
||||
.family = AF_INET6,
|
||||
.protocol = __constant_htons(ETH_P_IPV6),
|
||||
|
@ -346,6 +354,84 @@ static struct rt6_info *rt6_select(struct rt6_info **head, int oif,
|
|||
return (match ? match : &ip6_null_entry);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IPV6_ROUTE_INFO
|
||||
int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
|
||||
struct in6_addr *gwaddr)
|
||||
{
|
||||
struct route_info *rinfo = (struct route_info *) opt;
|
||||
struct in6_addr prefix_buf, *prefix;
|
||||
unsigned int pref;
|
||||
u32 lifetime;
|
||||
struct rt6_info *rt;
|
||||
|
||||
if (len < sizeof(struct route_info)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Sanity check for prefix_len and length */
|
||||
if (rinfo->length > 3) {
|
||||
return -EINVAL;
|
||||
} else if (rinfo->prefix_len > 128) {
|
||||
return -EINVAL;
|
||||
} else if (rinfo->prefix_len > 64) {
|
||||
if (rinfo->length < 2) {
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (rinfo->prefix_len > 0) {
|
||||
if (rinfo->length < 1) {
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
pref = rinfo->route_pref;
|
||||
if (pref == ICMPV6_ROUTER_PREF_INVALID)
|
||||
pref = ICMPV6_ROUTER_PREF_MEDIUM;
|
||||
|
||||
lifetime = htonl(rinfo->lifetime);
|
||||
if (lifetime == 0xffffffff) {
|
||||
/* infinity */
|
||||
} else if (lifetime > 0x7fffffff/HZ) {
|
||||
/* Avoid arithmetic overflow */
|
||||
lifetime = 0x7fffffff/HZ - 1;
|
||||
}
|
||||
|
||||
if (rinfo->length == 3)
|
||||
prefix = (struct in6_addr *)rinfo->prefix;
|
||||
else {
|
||||
/* this function is safe */
|
||||
ipv6_addr_prefix(&prefix_buf,
|
||||
(struct in6_addr *)rinfo->prefix,
|
||||
rinfo->prefix_len);
|
||||
prefix = &prefix_buf;
|
||||
}
|
||||
|
||||
rt = rt6_get_route_info(prefix, rinfo->prefix_len, gwaddr, dev->ifindex);
|
||||
|
||||
if (rt && !lifetime) {
|
||||
ip6_del_rt(rt, NULL, NULL, NULL);
|
||||
rt = NULL;
|
||||
}
|
||||
|
||||
if (!rt && lifetime)
|
||||
rt = rt6_add_route_info(prefix, rinfo->prefix_len, gwaddr, dev->ifindex,
|
||||
pref);
|
||||
else if (rt)
|
||||
rt->rt6i_flags = RTF_ROUTEINFO |
|
||||
(rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
|
||||
|
||||
if (rt) {
|
||||
if (lifetime == 0xffffffff) {
|
||||
rt->rt6i_flags &= ~RTF_EXPIRES;
|
||||
} else {
|
||||
rt->rt6i_expires = jiffies + HZ * lifetime;
|
||||
rt->rt6i_flags |= RTF_EXPIRES;
|
||||
}
|
||||
dst_release(&rt->u.dst);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct rt6_info *rt6_lookup(struct in6_addr *daddr, struct in6_addr *saddr,
|
||||
int oif, int strict)
|
||||
{
|
||||
|
@ -1277,6 +1363,54 @@ static struct rt6_info * ip6_rt_copy(struct rt6_info *ort)
|
|||
return rt;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IPV6_ROUTE_INFO
|
||||
static struct rt6_info *rt6_get_route_info(struct in6_addr *prefix, int prefixlen,
|
||||
struct in6_addr *gwaddr, int ifindex)
|
||||
{
|
||||
struct fib6_node *fn;
|
||||
struct rt6_info *rt = NULL;
|
||||
|
||||
write_lock_bh(&rt6_lock);
|
||||
fn = fib6_locate(&ip6_routing_table, prefix ,prefixlen, NULL, 0);
|
||||
if (!fn)
|
||||
goto out;
|
||||
|
||||
for (rt = fn->leaf; rt; rt = rt->u.next) {
|
||||
if (rt->rt6i_dev->ifindex != ifindex)
|
||||
continue;
|
||||
if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY))
|
||||
continue;
|
||||
if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr))
|
||||
continue;
|
||||
dst_hold(&rt->u.dst);
|
||||
break;
|
||||
}
|
||||
out:
|
||||
write_unlock_bh(&rt6_lock);
|
||||
return rt;
|
||||
}
|
||||
|
||||
static struct rt6_info *rt6_add_route_info(struct in6_addr *prefix, int prefixlen,
|
||||
struct in6_addr *gwaddr, int ifindex,
|
||||
unsigned pref)
|
||||
{
|
||||
struct in6_rtmsg rtmsg;
|
||||
|
||||
memset(&rtmsg, 0, sizeof(rtmsg));
|
||||
rtmsg.rtmsg_type = RTMSG_NEWROUTE;
|
||||
ipv6_addr_copy(&rtmsg.rtmsg_dst, prefix);
|
||||
rtmsg.rtmsg_dst_len = prefixlen;
|
||||
ipv6_addr_copy(&rtmsg.rtmsg_gateway, gwaddr);
|
||||
rtmsg.rtmsg_metric = 1024;
|
||||
rtmsg.rtmsg_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO | RTF_UP | RTF_PREF(pref);
|
||||
rtmsg.rtmsg_ifindex = ifindex;
|
||||
|
||||
ip6_route_add(&rtmsg, NULL, NULL, NULL);
|
||||
|
||||
return rt6_get_route_info(prefix, prefixlen, gwaddr, ifindex);
|
||||
}
|
||||
#endif
|
||||
|
||||
struct rt6_info *rt6_get_dflt_router(struct in6_addr *addr, struct net_device *dev)
|
||||
{
|
||||
struct rt6_info *rt;
|
||||
|
|
Загрузка…
Ссылка в новой задаче