diff options
-rw-r--r-- | include/linux/in6.h | 11 | ||||
-rw-r--r-- | include/linux/ipv6.h | 6 | ||||
-rw-r--r-- | include/net/addrconf.h | 1 | ||||
-rw-r--r-- | include/net/ip6_route.h | 9 | ||||
-rw-r--r-- | net/ipv6/addrconf.c | 17 | ||||
-rw-r--r-- | net/ipv6/fib6_rules.c | 12 | ||||
-rw-r--r-- | net/ipv6/ip6_output.c | 4 | ||||
-rw-r--r-- | net/ipv6/ipv6_sockglue.c | 78 | ||||
-rw-r--r-- | net/ipv6/ndisc.c | 4 | ||||
-rw-r--r-- | net/ipv6/route.c | 11 | ||||
-rw-r--r-- | net/ipv6/xfrm6_policy.c | 2 | ||||
-rw-r--r-- | net/sctp/ipv6.c | 4 |
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 | ||
79 | extern int ipv6_dev_get_saddr(struct net_device *dev, | 79 | extern 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); |
82 | extern int ipv6_get_lladdr(struct net_device *dev, | 83 | extern 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 | ||
37 | extern struct rt6_info *ip6_null_entry; | 40 | extern 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 | ||
914 | static inline int ipv6_saddr_preferred(int type) | 915 | static 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 | ||
1032 | int ipv6_dev_get_saddr(struct net_device *dst_dev, | 1041 | int 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); | ||
647 | pref_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; | ||
662 | pref_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; |