aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Walter <sahne@0x90.at>2011-04-13 17:10:57 -0400
committerDavid S. Miller <davem@davemloft.net>2011-04-15 18:44:37 -0400
commitc3968a857a6b6c3d2ef4ead35776b055fb664d74 (patch)
treef82be0953ff4454218f38ffb803fdcfb93a6f2a3
parentbd015928bb1713691068c4d0d159afccbaf0f8c0 (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.h2
-rw-r--r--include/net/ip6_route.h7
-rw-r--r--net/ipv6/addrconf.c2
-rw-r--r--net/ipv6/ip6_output.c8
-rw-r--r--net/ipv6/route.c72
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);
84extern int ip6_ins_rt(struct rt6_info *); 84extern int ip6_ins_rt(struct rt6_info *);
85extern int ip6_del_rt(struct rt6_info *); 85extern int ip6_del_rt(struct rt6_info *);
86 86
87extern 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
87extern struct rt6_info *rt6_lookup(struct net *net, 93extern 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 {
141extern int rt6_dump_route(struct rt6_info *rt, void *p_arg); 147extern int rt6_dump_route(struct rt6_info *rt, void *p_arg);
142extern void rt6_ifdown(struct net *net, struct net_device *dev); 148extern void rt6_ifdown(struct net *net, struct net_device *dev);
143extern void rt6_mtu_change(struct net_device *dev, unsigned mtu); 149extern void rt6_mtu_change(struct net_device *dev, unsigned mtu);
150extern 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);
828out: 830out:
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
2050int 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 */
2067struct arg_dev_net_ip {
2068 struct net_device *dev;
2069 struct net *net;
2070 struct in6_addr *addr;
2071};
2072
2073static 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
2088void 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
2040struct arg_dev_net { 2099struct 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