diff options
author | Daniel Walter <sahne@0x90.at> | 2011-04-13 17:10:57 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-04-15 18:44:37 -0400 |
commit | c3968a857a6b6c3d2ef4ead35776b055fb664d74 (patch) | |
tree | f82be0953ff4454218f38ffb803fdcfb93a6f2a3 | |
parent | bd015928bb1713691068c4d0d159afccbaf0f8c0 (diff) |
ipv6: RTA_PREFSRC support for ipv6 route source address selection
[ipv6] Add support for RTA_PREFSRC
This patch allows a user to select the preferred source address
for a specific IPv6-Route. It can be set via a netlink message
setting RTA_PREFSRC to a valid IPv6 address which must be
up on the device the route will be bound to.
Signed-off-by: Daniel Walter <dwalter@barracuda.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/net/ip6_fib.h | 2 | ||||
-rw-r--r-- | include/net/ip6_route.h | 7 | ||||
-rw-r--r-- | net/ipv6/addrconf.c | 2 | ||||
-rw-r--r-- | net/ipv6/ip6_output.c | 8 | ||||
-rw-r--r-- | net/ipv6/route.c | 72 |
5 files changed, 84 insertions, 7 deletions
diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index bc3cde0a810c..98348d53b2b6 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h | |||
@@ -42,6 +42,7 @@ struct fib6_config { | |||
42 | 42 | ||
43 | struct in6_addr fc_dst; | 43 | struct in6_addr fc_dst; |
44 | struct in6_addr fc_src; | 44 | struct in6_addr fc_src; |
45 | struct in6_addr fc_prefsrc; | ||
45 | struct in6_addr fc_gateway; | 46 | struct in6_addr fc_gateway; |
46 | 47 | ||
47 | unsigned long fc_expires; | 48 | unsigned long fc_expires; |
@@ -107,6 +108,7 @@ struct rt6_info { | |||
107 | struct rt6key rt6i_dst ____cacheline_aligned_in_smp; | 108 | struct rt6key rt6i_dst ____cacheline_aligned_in_smp; |
108 | u32 rt6i_flags; | 109 | u32 rt6i_flags; |
109 | struct rt6key rt6i_src; | 110 | struct rt6key rt6i_src; |
111 | struct rt6key rt6i_prefsrc; | ||
110 | u32 rt6i_metric; | 112 | u32 rt6i_metric; |
111 | u32 rt6i_peer_genid; | 113 | u32 rt6i_peer_genid; |
112 | 114 | ||
diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index c850e5fb967c..86b1cb486903 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h | |||
@@ -84,6 +84,12 @@ extern int ip6_route_add(struct fib6_config *cfg); | |||
84 | extern int ip6_ins_rt(struct rt6_info *); | 84 | extern int ip6_ins_rt(struct rt6_info *); |
85 | extern int ip6_del_rt(struct rt6_info *); | 85 | extern int ip6_del_rt(struct rt6_info *); |
86 | 86 | ||
87 | extern int ip6_route_get_saddr(struct net *net, | ||
88 | struct rt6_info *rt, | ||
89 | struct in6_addr *daddr, | ||
90 | unsigned int prefs, | ||
91 | struct in6_addr *saddr); | ||
92 | |||
87 | extern struct rt6_info *rt6_lookup(struct net *net, | 93 | extern struct rt6_info *rt6_lookup(struct net *net, |
88 | const struct in6_addr *daddr, | 94 | const struct in6_addr *daddr, |
89 | const struct in6_addr *saddr, | 95 | const struct in6_addr *saddr, |
@@ -141,6 +147,7 @@ struct rt6_rtnl_dump_arg { | |||
141 | extern int rt6_dump_route(struct rt6_info *rt, void *p_arg); | 147 | extern int rt6_dump_route(struct rt6_info *rt, void *p_arg); |
142 | extern void rt6_ifdown(struct net *net, struct net_device *dev); | 148 | extern void rt6_ifdown(struct net *net, struct net_device *dev); |
143 | extern void rt6_mtu_change(struct net_device *dev, unsigned mtu); | 149 | extern void rt6_mtu_change(struct net_device *dev, unsigned mtu); |
150 | extern void rt6_remove_prefsrc(struct inet6_ifaddr *ifp); | ||
144 | 151 | ||
145 | 152 | ||
146 | /* | 153 | /* |
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 1493534116df..129d7e1f311c 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c | |||
@@ -825,6 +825,8 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp) | |||
825 | dst_release(&rt->dst); | 825 | dst_release(&rt->dst); |
826 | } | 826 | } |
827 | 827 | ||
828 | /* clean up prefsrc entries */ | ||
829 | rt6_remove_prefsrc(ifp); | ||
828 | out: | 830 | out: |
829 | in6_ifa_put(ifp); | 831 | in6_ifa_put(ifp); |
830 | } | 832 | } |
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 46cf7bea6769..c614d02bf429 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c | |||
@@ -930,10 +930,10 @@ static int ip6_dst_lookup_tail(struct sock *sk, | |||
930 | goto out_err_release; | 930 | goto out_err_release; |
931 | 931 | ||
932 | if (ipv6_addr_any(&fl6->saddr)) { | 932 | if (ipv6_addr_any(&fl6->saddr)) { |
933 | err = ipv6_dev_get_saddr(net, ip6_dst_idev(*dst)->dev, | 933 | struct rt6_info *rt = (struct rt6_info *) *dst; |
934 | &fl6->daddr, | 934 | err = ip6_route_get_saddr(net, rt, &fl6->daddr, |
935 | sk ? inet6_sk(sk)->srcprefs : 0, | 935 | sk ? inet6_sk(sk)->srcprefs : 0, |
936 | &fl6->saddr); | 936 | &fl6->saddr); |
937 | if (err) | 937 | if (err) |
938 | goto out_err_release; | 938 | goto out_err_release; |
939 | } | 939 | } |
diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 843406f14d7b..af26cc1073cb 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c | |||
@@ -1325,6 +1325,16 @@ int ip6_route_add(struct fib6_config *cfg) | |||
1325 | if (dev == NULL) | 1325 | if (dev == NULL) |
1326 | goto out; | 1326 | goto out; |
1327 | 1327 | ||
1328 | if (!ipv6_addr_any(&cfg->fc_prefsrc)) { | ||
1329 | if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) { | ||
1330 | err = -EINVAL; | ||
1331 | goto out; | ||
1332 | } | ||
1333 | ipv6_addr_copy(&rt->rt6i_prefsrc.addr, &cfg->fc_prefsrc); | ||
1334 | rt->rt6i_prefsrc.plen = 128; | ||
1335 | } else | ||
1336 | rt->rt6i_prefsrc.plen = 0; | ||
1337 | |||
1328 | if (cfg->fc_flags & (RTF_GATEWAY | RTF_NONEXTHOP)) { | 1338 | if (cfg->fc_flags & (RTF_GATEWAY | RTF_NONEXTHOP)) { |
1329 | rt->rt6i_nexthop = __neigh_lookup_errno(&nd_tbl, &rt->rt6i_gateway, dev); | 1339 | rt->rt6i_nexthop = __neigh_lookup_errno(&nd_tbl, &rt->rt6i_gateway, dev); |
1330 | if (IS_ERR(rt->rt6i_nexthop)) { | 1340 | if (IS_ERR(rt->rt6i_nexthop)) { |
@@ -2037,6 +2047,55 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, | |||
2037 | return rt; | 2047 | return rt; |
2038 | } | 2048 | } |
2039 | 2049 | ||
2050 | int ip6_route_get_saddr(struct net *net, | ||
2051 | struct rt6_info *rt, | ||
2052 | struct in6_addr *daddr, | ||
2053 | unsigned int prefs, | ||
2054 | struct in6_addr *saddr) | ||
2055 | { | ||
2056 | struct inet6_dev *idev = ip6_dst_idev((struct dst_entry*)rt); | ||
2057 | int err = 0; | ||
2058 | if (rt->rt6i_prefsrc.plen) | ||
2059 | ipv6_addr_copy(saddr, &rt->rt6i_prefsrc.addr); | ||
2060 | else | ||
2061 | err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL, | ||
2062 | daddr, prefs, saddr); | ||
2063 | return err; | ||
2064 | } | ||
2065 | |||
2066 | /* remove deleted ip from prefsrc entries */ | ||
2067 | struct arg_dev_net_ip { | ||
2068 | struct net_device *dev; | ||
2069 | struct net *net; | ||
2070 | struct in6_addr *addr; | ||
2071 | }; | ||
2072 | |||
2073 | static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg) | ||
2074 | { | ||
2075 | struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev; | ||
2076 | struct net *net = ((struct arg_dev_net_ip *)arg)->net; | ||
2077 | struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr; | ||
2078 | |||
2079 | if (((void *)rt->rt6i_dev == dev || dev == NULL) && | ||
2080 | rt != net->ipv6.ip6_null_entry && | ||
2081 | ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) { | ||
2082 | /* remove prefsrc entry */ | ||
2083 | rt->rt6i_prefsrc.plen = 0; | ||
2084 | } | ||
2085 | return 0; | ||
2086 | } | ||
2087 | |||
2088 | void rt6_remove_prefsrc(struct inet6_ifaddr *ifp) | ||
2089 | { | ||
2090 | struct net *net = dev_net(ifp->idev->dev); | ||
2091 | struct arg_dev_net_ip adni = { | ||
2092 | .dev = ifp->idev->dev, | ||
2093 | .net = net, | ||
2094 | .addr = &ifp->addr, | ||
2095 | }; | ||
2096 | fib6_clean_all(net, fib6_remove_prefsrc, 0, &adni); | ||
2097 | } | ||
2098 | |||
2040 | struct arg_dev_net { | 2099 | struct arg_dev_net { |
2041 | struct net_device *dev; | 2100 | struct net_device *dev; |
2042 | struct net *net; | 2101 | struct net *net; |
@@ -2183,6 +2242,9 @@ static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, | |||
2183 | nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen); | 2242 | nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen); |
2184 | } | 2243 | } |
2185 | 2244 | ||
2245 | if (tb[RTA_PREFSRC]) | ||
2246 | nla_memcpy(&cfg->fc_prefsrc, tb[RTA_PREFSRC], 16); | ||
2247 | |||
2186 | if (tb[RTA_OIF]) | 2248 | if (tb[RTA_OIF]) |
2187 | cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]); | 2249 | cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]); |
2188 | 2250 | ||
@@ -2325,13 +2387,17 @@ static int rt6_fill_node(struct net *net, | |||
2325 | #endif | 2387 | #endif |
2326 | NLA_PUT_U32(skb, RTA_IIF, iif); | 2388 | NLA_PUT_U32(skb, RTA_IIF, iif); |
2327 | } else if (dst) { | 2389 | } else if (dst) { |
2328 | struct inet6_dev *idev = ip6_dst_idev(&rt->dst); | ||
2329 | struct in6_addr saddr_buf; | 2390 | struct in6_addr saddr_buf; |
2330 | if (ipv6_dev_get_saddr(net, idev ? idev->dev : NULL, | 2391 | if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0) |
2331 | dst, 0, &saddr_buf) == 0) | ||
2332 | NLA_PUT(skb, RTA_PREFSRC, 16, &saddr_buf); | 2392 | NLA_PUT(skb, RTA_PREFSRC, 16, &saddr_buf); |
2333 | } | 2393 | } |
2334 | 2394 | ||
2395 | if (rt->rt6i_prefsrc.plen) { | ||
2396 | struct in6_addr saddr_buf; | ||
2397 | ipv6_addr_copy(&saddr_buf, &rt->rt6i_prefsrc.addr); | ||
2398 | NLA_PUT(skb, RTA_PREFSRC, 16, &saddr_buf); | ||
2399 | } | ||
2400 | |||
2335 | if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0) | 2401 | if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0) |
2336 | goto nla_put_failure; | 2402 | goto nla_put_failure; |
2337 | 2403 | ||