aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/in6.h11
-rw-r--r--include/linux/ipv6.h6
-rw-r--r--include/net/addrconf.h1
-rw-r--r--include/net/ip6_route.h9
-rw-r--r--net/ipv6/addrconf.c17
-rw-r--r--net/ipv6/fib6_rules.c12
-rw-r--r--net/ipv6/ip6_output.c4
-rw-r--r--net/ipv6/ipv6_sockglue.c78
-rw-r--r--net/ipv6/ndisc.c4
-rw-r--r--net/ipv6/route.c11
-rw-r--r--net/ipv6/xfrm6_policy.c2
-rw-r--r--net/sctp/ipv6.c4
12 files changed, 146 insertions, 13 deletions
diff --git a/include/linux/in6.h b/include/linux/in6.h
index 2a61c82af115..f674000c6c99 100644
--- a/include/linux/in6.h
+++ b/include/linux/in6.h
@@ -249,4 +249,15 @@ struct in6_flowlabel_req
249 * IP6T_SO_GET_REVISION_TARGET 69 249 * IP6T_SO_GET_REVISION_TARGET 69
250 */ 250 */
251 251
252/* RFC5014: Source address selection */
253#define IPV6_ADDR_PREFERENCES 72
254
255#define IPV6_PREFER_SRC_TMP 0x0001
256#define IPV6_PREFER_SRC_PUBLIC 0x0002
257#define IPV6_PREFER_SRC_PUBTMP_DEFAULT 0x0100
258#define IPV6_PREFER_SRC_COA 0x0004
259#define IPV6_PREFER_SRC_HOME 0x0400
260#define IPV6_PREFER_SRC_CGA 0x0008
261#define IPV6_PREFER_SRC_NONCGA 0x0800
262
252#endif 263#endif
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index 87ae4e389ce1..c9ba0da16ce9 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -322,7 +322,11 @@ struct ipv6_pinfo {
322 __u8 recverr:1, 322 __u8 recverr:1,
323 sndflow:1, 323 sndflow:1,
324 pmtudisc:2, 324 pmtudisc:2,
325 ipv6only:1; 325 ipv6only:1,
326 srcprefs:3; /* 001: prefer temporary address
327 * 010: prefer public address
328 * 100: prefer care-of address
329 */
326 __u8 tclass; 330 __u8 tclass;
327 331
328 __u32 dst_cookie; 332 __u32 dst_cookie;
diff --git a/include/net/addrconf.h b/include/net/addrconf.h
index edcb4bbaab7d..c9276c72764d 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -78,6 +78,7 @@ extern struct inet6_ifaddr *ipv6_get_ifaddr(struct net *net,
78 78
79extern int ipv6_dev_get_saddr(struct net_device *dev, 79extern int ipv6_dev_get_saddr(struct net_device *dev,
80 struct in6_addr *daddr, 80 struct in6_addr *daddr,
81 unsigned int srcprefs,
81 struct in6_addr *saddr); 82 struct in6_addr *saddr);
82extern int ipv6_get_lladdr(struct net_device *dev, 83extern int ipv6_get_lladdr(struct net_device *dev,
83 struct in6_addr *addr, 84 struct in6_addr *addr,
diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h
index 5c3b67c86aef..3ae6799c2b14 100644
--- a/include/net/ip6_route.h
+++ b/include/net/ip6_route.h
@@ -30,9 +30,12 @@ struct route_info {
30#include <linux/ip.h> 30#include <linux/ip.h>
31#include <linux/ipv6.h> 31#include <linux/ipv6.h>
32 32
33#define RT6_LOOKUP_F_IFACE 0x1 33#define RT6_LOOKUP_F_IFACE 0x00000001
34#define RT6_LOOKUP_F_REACHABLE 0x2 34#define RT6_LOOKUP_F_REACHABLE 0x00000002
35#define RT6_LOOKUP_F_HAS_SADDR 0x4 35#define RT6_LOOKUP_F_HAS_SADDR 0x00000004
36#define RT6_LOOKUP_F_SRCPREF_TMP 0x00000008
37#define RT6_LOOKUP_F_SRCPREF_PUBLIC 0x00000010
38#define RT6_LOOKUP_F_SRCPREF_COA 0x00000020
36 39
37extern struct rt6_info *ip6_null_entry; 40extern struct rt6_info *ip6_null_entry;
38 41
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 787e90af166c..89954885dee1 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -909,6 +909,7 @@ struct ipv6_saddr_dst {
909 int ifindex; 909 int ifindex;
910 int scope; 910 int scope;
911 int label; 911 int label;
912 unsigned int prefs;
912}; 913};
913 914
914static inline int ipv6_saddr_preferred(int type) 915static inline int ipv6_saddr_preferred(int type)
@@ -984,9 +985,12 @@ static int ipv6_get_saddr_eval(struct ipv6_saddr_score *score,
984 break; 985 break;
985#ifdef CONFIG_IPV6_MIP6 986#ifdef CONFIG_IPV6_MIP6
986 case IPV6_SADDR_RULE_HOA: 987 case IPV6_SADDR_RULE_HOA:
988 {
987 /* Rule 4: Prefer home address */ 989 /* Rule 4: Prefer home address */
988 ret = !!(score->ifa->flags & IFA_F_HOMEADDRESS); 990 int prefhome = !(dst->prefs & IPV6_PREFER_SRC_COA);
991 ret = !(score->ifa->flags & IFA_F_HOMEADDRESS) ^ prefhome;
989 break; 992 break;
993 }
990#endif 994#endif
991 case IPV6_SADDR_RULE_OIF: 995 case IPV6_SADDR_RULE_OIF:
992 /* Rule 5: Prefer outgoing interface */ 996 /* Rule 5: Prefer outgoing interface */
@@ -1000,11 +1004,16 @@ static int ipv6_get_saddr_eval(struct ipv6_saddr_score *score,
1000 break; 1004 break;
1001#ifdef CONFIG_IPV6_PRIVACY 1005#ifdef CONFIG_IPV6_PRIVACY
1002 case IPV6_SADDR_RULE_PRIVACY: 1006 case IPV6_SADDR_RULE_PRIVACY:
1007 {
1003 /* Rule 7: Prefer public address 1008 /* Rule 7: Prefer public address
1004 * Note: prefer temprary address if use_tempaddr >= 2 1009 * Note: prefer temprary address if use_tempaddr >= 2
1005 */ 1010 */
1006 ret = (!(score->ifa->flags & IFA_F_TEMPORARY)) ^ (score->ifa->idev->cnf.use_tempaddr >= 2); 1011 int preftmp = dst->prefs & (IPV6_PREFER_SRC_PUBLIC|IPV6_PREFER_SRC_TMP) ?
1012 !!(dst->prefs & IPV6_PREFER_SRC_TMP) :
1013 score->ifa->idev->cnf.use_tempaddr >= 2;
1014 ret = (!(score->ifa->flags & IFA_F_TEMPORARY)) ^ preftmp;
1007 break; 1015 break;
1016 }
1008#endif 1017#endif
1009 case IPV6_SADDR_RULE_ORCHID: 1018 case IPV6_SADDR_RULE_ORCHID:
1010 /* Rule 8-: Prefer ORCHID vs ORCHID or 1019 /* Rule 8-: Prefer ORCHID vs ORCHID or
@@ -1030,7 +1039,8 @@ out:
1030} 1039}
1031 1040
1032int ipv6_dev_get_saddr(struct net_device *dst_dev, 1041int ipv6_dev_get_saddr(struct net_device *dst_dev,
1033 struct in6_addr *daddr, struct in6_addr *saddr) 1042 struct in6_addr *daddr, unsigned int prefs,
1043 struct in6_addr *saddr)
1034{ 1044{
1035 struct ipv6_saddr_score scores[2], 1045 struct ipv6_saddr_score scores[2],
1036 *score = &scores[0], *hiscore = &scores[1]; 1046 *score = &scores[0], *hiscore = &scores[1];
@@ -1044,6 +1054,7 @@ int ipv6_dev_get_saddr(struct net_device *dst_dev,
1044 dst.ifindex = dst_dev ? dst_dev->ifindex : 0; 1054 dst.ifindex = dst_dev ? dst_dev->ifindex : 0;
1045 dst.scope = __ipv6_addr_src_scope(dst_type); 1055 dst.scope = __ipv6_addr_src_scope(dst_type);
1046 dst.label = ipv6_addr_label(daddr, dst_type, dst.ifindex); 1056 dst.label = ipv6_addr_label(daddr, dst_type, dst.ifindex);
1057 dst.prefs = prefs;
1047 1058
1048 hiscore->rule = -1; 1059 hiscore->rule = -1;
1049 hiscore->ifa = NULL; 1060 hiscore->ifa = NULL;
diff --git a/net/ipv6/fib6_rules.c b/net/ipv6/fib6_rules.c
index 55137408f054..e7a7fe26cebf 100644
--- a/net/ipv6/fib6_rules.c
+++ b/net/ipv6/fib6_rules.c
@@ -84,8 +84,18 @@ static int fib6_rule_action(struct fib_rule *rule, struct flowi *flp,
84 if ((rule->flags & FIB_RULE_FIND_SADDR) && 84 if ((rule->flags & FIB_RULE_FIND_SADDR) &&
85 r->src.plen && !(flags & RT6_LOOKUP_F_HAS_SADDR)) { 85 r->src.plen && !(flags & RT6_LOOKUP_F_HAS_SADDR)) {
86 struct in6_addr saddr; 86 struct in6_addr saddr;
87 unsigned int srcprefs = 0;
88
89 if (flags & RT6_LOOKUP_F_SRCPREF_TMP)
90 srcprefs |= IPV6_PREFER_SRC_TMP;
91 if (flags & RT6_LOOKUP_F_SRCPREF_PUBLIC)
92 srcprefs |= IPV6_PREFER_SRC_PUBLIC;
93 if (flags & RT6_LOOKUP_F_SRCPREF_COA)
94 srcprefs |= IPV6_PREFER_SRC_COA;
95
87 if (ipv6_dev_get_saddr(ip6_dst_idev(&rt->u.dst)->dev, 96 if (ipv6_dev_get_saddr(ip6_dst_idev(&rt->u.dst)->dev,
88 &flp->fl6_dst, &saddr)) 97 &flp->fl6_dst, srcprefs,
98 &saddr))
89 goto again; 99 goto again;
90 if (!ipv6_prefix_equal(&saddr, &r->src.addr, 100 if (!ipv6_prefix_equal(&saddr, &r->src.addr,
91 r->src.plen)) 101 r->src.plen))
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 2a4f08c8a02d..d34aa61353bb 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -920,7 +920,9 @@ static int ip6_dst_lookup_tail(struct sock *sk,
920 920
921 if (ipv6_addr_any(&fl->fl6_src)) { 921 if (ipv6_addr_any(&fl->fl6_src)) {
922 err = ipv6_dev_get_saddr(ip6_dst_idev(*dst)->dev, 922 err = ipv6_dev_get_saddr(ip6_dst_idev(*dst)->dev,
923 &fl->fl6_dst, &fl->fl6_src); 923 &fl->fl6_dst,
924 sk ? inet6_sk(sk)->srcprefs : 0,
925 &fl->fl6_src);
924 if (err) 926 if (err)
925 goto out_err_release; 927 goto out_err_release;
926 } 928 }
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 8e29fb1d1df6..dc6695cc5767 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -617,7 +617,67 @@ done:
617 retv = xfrm_user_policy(sk, optname, optval, optlen); 617 retv = xfrm_user_policy(sk, optname, optval, optlen);
618 break; 618 break;
619 619
620 case IPV6_ADDR_PREFERENCES:
621 {
622 unsigned int pref = 0;
623 unsigned int prefmask = ~0;
624
625 retv = -EINVAL;
626
627 /* check PUBLIC/TMP/PUBTMP_DEFAULT conflicts */
628 switch (val & (IPV6_PREFER_SRC_PUBLIC|
629 IPV6_PREFER_SRC_TMP|
630 IPV6_PREFER_SRC_PUBTMP_DEFAULT)) {
631 case IPV6_PREFER_SRC_PUBLIC:
632 pref |= IPV6_PREFER_SRC_PUBLIC;
633 break;
634 case IPV6_PREFER_SRC_TMP:
635 pref |= IPV6_PREFER_SRC_TMP;
636 break;
637 case IPV6_PREFER_SRC_PUBTMP_DEFAULT:
638 break;
639 case 0:
640 goto pref_skip_pubtmp;
641 default:
642 goto e_inval;
643 }
644
645 prefmask &= ~(IPV6_PREFER_SRC_PUBLIC|
646 IPV6_PREFER_SRC_TMP);
647pref_skip_pubtmp:
648
649 /* check HOME/COA conflicts */
650 switch (val & (IPV6_PREFER_SRC_HOME|IPV6_PREFER_SRC_COA)) {
651 case IPV6_PREFER_SRC_HOME:
652 break;
653 case IPV6_PREFER_SRC_COA:
654 pref |= IPV6_PREFER_SRC_COA;
655 case 0:
656 goto pref_skip_coa;
657 default:
658 goto e_inval;
659 }
660
661 prefmask &= ~IPV6_PREFER_SRC_COA;
662pref_skip_coa:
663
664 /* check CGA/NONCGA conflicts */
665 switch (val & (IPV6_PREFER_SRC_CGA|IPV6_PREFER_SRC_NONCGA)) {
666 case IPV6_PREFER_SRC_CGA:
667 case IPV6_PREFER_SRC_NONCGA:
668 case 0:
669 break;
670 default:
671 goto e_inval;
672 }
673
674 np->srcprefs = (np->srcprefs & prefmask) | pref;
675 retv = 0;
676
677 break;
678 }
620 } 679 }
680
621 release_sock(sk); 681 release_sock(sk);
622 682
623 return retv; 683 return retv;
@@ -932,6 +992,24 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
932 val = np->sndflow; 992 val = np->sndflow;
933 break; 993 break;
934 994
995 case IPV6_ADDR_PREFERENCES:
996 val = 0;
997
998 if (np->srcprefs & IPV6_PREFER_SRC_TMP)
999 val |= IPV6_PREFER_SRC_TMP;
1000 else if (np->srcprefs & IPV6_PREFER_SRC_PUBLIC)
1001 val |= IPV6_PREFER_SRC_PUBLIC;
1002 else {
1003 /* XXX: should we return system default? */
1004 val |= IPV6_PREFER_SRC_PUBTMP_DEFAULT;
1005 }
1006
1007 if (np->srcprefs & IPV6_PREFER_SRC_COA)
1008 val |= IPV6_PREFER_SRC_COA;
1009 else
1010 val |= IPV6_PREFER_SRC_HOME;
1011 break;
1012
935 default: 1013 default:
936 return -ENOPROTOOPT; 1014 return -ENOPROTOOPT;
937 } 1015 }
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index e7d8e74704c1..3f68a6eae7b2 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -546,7 +546,9 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
546 override = 0; 546 override = 0;
547 in6_ifa_put(ifp); 547 in6_ifa_put(ifp);
548 } else { 548 } else {
549 if (ipv6_dev_get_saddr(dev, daddr, &tmpaddr)) 549 if (ipv6_dev_get_saddr(dev, daddr,
550 inet6_sk(dev->nd_net->ipv6.ndisc_sk)->srcprefs,
551 &tmpaddr))
550 return; 552 return;
551 src_addr = &tmpaddr; 553 src_addr = &tmpaddr;
552 } 554 }
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index aa3f08718e44..06faa46920e1 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -782,6 +782,15 @@ struct dst_entry * ip6_route_output(struct net *net, struct sock *sk,
782 782
783 if (!ipv6_addr_any(&fl->fl6_src)) 783 if (!ipv6_addr_any(&fl->fl6_src))
784 flags |= RT6_LOOKUP_F_HAS_SADDR; 784 flags |= RT6_LOOKUP_F_HAS_SADDR;
785 else if (sk) {
786 unsigned int prefs = inet6_sk(sk)->srcprefs;
787 if (prefs & IPV6_PREFER_SRC_TMP)
788 flags |= RT6_LOOKUP_F_SRCPREF_TMP;
789 if (prefs & IPV6_PREFER_SRC_PUBLIC)
790 flags |= RT6_LOOKUP_F_SRCPREF_PUBLIC;
791 if (prefs & IPV6_PREFER_SRC_COA)
792 flags |= RT6_LOOKUP_F_SRCPREF_COA;
793 }
785 794
786 return fib6_rule_lookup(net, fl, flags, ip6_pol_route_output); 795 return fib6_rule_lookup(net, fl, flags, ip6_pol_route_output);
787} 796}
@@ -2162,7 +2171,7 @@ static int rt6_fill_node(struct sk_buff *skb, struct rt6_info *rt,
2162 else if (dst) { 2171 else if (dst) {
2163 struct in6_addr saddr_buf; 2172 struct in6_addr saddr_buf;
2164 if (ipv6_dev_get_saddr(ip6_dst_idev(&rt->u.dst)->dev, 2173 if (ipv6_dev_get_saddr(ip6_dst_idev(&rt->u.dst)->dev,
2165 dst, &saddr_buf) == 0) 2174 dst, 0, &saddr_buf) == 0)
2166 NLA_PUT(skb, RTA_PREFSRC, 16, &saddr_buf); 2175 NLA_PUT(skb, RTA_PREFSRC, 16, &saddr_buf);
2167 } 2176 }
2168 2177
diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c
index e96dafdc7032..d92d1fceb8cf 100644
--- a/net/ipv6/xfrm6_policy.c
+++ b/net/ipv6/xfrm6_policy.c
@@ -58,7 +58,7 @@ static int xfrm6_get_saddr(xfrm_address_t *saddr, xfrm_address_t *daddr)
58 return -EHOSTUNREACH; 58 return -EHOSTUNREACH;
59 59
60 ipv6_dev_get_saddr(ip6_dst_idev(dst)->dev, 60 ipv6_dev_get_saddr(ip6_dst_idev(dst)->dev,
61 (struct in6_addr *)&daddr->a6, 61 (struct in6_addr *)&daddr->a6, 0,
62 (struct in6_addr *)&saddr->a6); 62 (struct in6_addr *)&saddr->a6);
63 dst_release(dst); 63 dst_release(dst);
64 return 0; 64 return 0;
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index 46c5b3c5cb99..dc71d0d83753 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -316,7 +316,9 @@ static void sctp_v6_get_saddr(struct sctp_association *asoc,
316 316
317 if (!asoc) { 317 if (!asoc) {
318 ipv6_dev_get_saddr(dst ? ip6_dst_idev(dst)->dev : NULL, 318 ipv6_dev_get_saddr(dst ? ip6_dst_idev(dst)->dev : NULL,
319 &daddr->v6.sin6_addr, &saddr->v6.sin6_addr); 319 &daddr->v6.sin6_addr,
320 inet6_sk(asoc->base.sk)->srcprefs,
321 &saddr->v6.sin6_addr);
320 SCTP_DEBUG_PRINTK("saddr from ipv6_get_saddr: " NIP6_FMT "\n", 322 SCTP_DEBUG_PRINTK("saddr from ipv6_get_saddr: " NIP6_FMT "\n",
321 NIP6(saddr->v6.sin6_addr)); 323 NIP6(saddr->v6.sin6_addr));
322 return; 324 return;