aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/addrconf.c
diff options
context:
space:
mode:
authorYOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>2008-03-01 20:48:21 -0500
committerYOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>2008-03-24 21:23:58 -0400
commita9b05723ffa2e427b0257b81ea74363fcd7c304f (patch)
tree89f51b7d3fa28a83ab3f378f2fb6798e37bd21f0 /net/ipv6/addrconf.c
parent9bb182a7007515239091b237fe7169b1328a61d3 (diff)
[IPV6] ADDRCONF: Clean-up ipv6_dev_get_saddr().
old: | text data bss dec hex filename | 28599 1416 96 30111 759f net/ipv6/addrconf.o new: | text data bss dec hex filename | 28007 1416 96 29519 734f net/ipv6/addrconf.o Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Diffstat (limited to 'net/ipv6/addrconf.c')
-rw-r--r--net/ipv6/addrconf.c420
1 files changed, 206 insertions, 214 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 4b86d388bf63..787e90af166c 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -877,20 +877,39 @@ out:
877/* 877/*
878 * Choose an appropriate source address (RFC3484) 878 * Choose an appropriate source address (RFC3484)
879 */ 879 */
880enum {
881 IPV6_SADDR_RULE_INIT = 0,
882 IPV6_SADDR_RULE_LOCAL,
883 IPV6_SADDR_RULE_SCOPE,
884 IPV6_SADDR_RULE_PREFERRED,
885#ifdef CONFIG_IPV6_MIP6
886 IPV6_SADDR_RULE_HOA,
887#endif
888 IPV6_SADDR_RULE_OIF,
889 IPV6_SADDR_RULE_LABEL,
890#ifdef CONFIG_IPV6_PRIVACY
891 IPV6_SADDR_RULE_PRIVACY,
892#endif
893 IPV6_SADDR_RULE_ORCHID,
894 IPV6_SADDR_RULE_PREFIX,
895 IPV6_SADDR_RULE_MAX
896};
897
880struct ipv6_saddr_score { 898struct ipv6_saddr_score {
881 int addr_type; 899 int rule;
882 unsigned int attrs; 900 int addr_type;
883 int matchlen; 901 struct inet6_ifaddr *ifa;
884 int scope; 902 DECLARE_BITMAP(scorebits, IPV6_SADDR_RULE_MAX);
885 unsigned int rule; 903 int scopedist;
904 int matchlen;
886}; 905};
887 906
888#define IPV6_SADDR_SCORE_LOCAL 0x0001 907struct ipv6_saddr_dst {
889#define IPV6_SADDR_SCORE_PREFERRED 0x0004 908 struct in6_addr *addr;
890#define IPV6_SADDR_SCORE_HOA 0x0008 909 int ifindex;
891#define IPV6_SADDR_SCORE_OIF 0x0010 910 int scope;
892#define IPV6_SADDR_SCORE_LABEL 0x0020 911 int label;
893#define IPV6_SADDR_SCORE_PRIVACY 0x0040 912};
894 913
895static inline int ipv6_saddr_preferred(int type) 914static inline int ipv6_saddr_preferred(int type)
896{ 915{
@@ -900,28 +919,142 @@ static inline int ipv6_saddr_preferred(int type)
900 return 0; 919 return 0;
901} 920}
902 921
903int ipv6_dev_get_saddr(struct net_device *daddr_dev, 922static int ipv6_get_saddr_eval(struct ipv6_saddr_score *score,
923 struct ipv6_saddr_dst *dst,
924 int i)
925{
926 int ret;
927
928 if (i <= score->rule) {
929 switch (i) {
930 case IPV6_SADDR_RULE_SCOPE:
931 ret = score->scopedist;
932 break;
933 case IPV6_SADDR_RULE_PREFIX:
934 ret = score->matchlen;
935 break;
936 default:
937 ret = !!test_bit(i, score->scorebits);
938 }
939 goto out;
940 }
941
942 switch (i) {
943 case IPV6_SADDR_RULE_INIT:
944 /* Rule 0: remember if hiscore is not ready yet */
945 ret = !!score->ifa;
946 break;
947 case IPV6_SADDR_RULE_LOCAL:
948 /* Rule 1: Prefer same address */
949 ret = ipv6_addr_equal(&score->ifa->addr, dst->addr);
950 break;
951 case IPV6_SADDR_RULE_SCOPE:
952 /* Rule 2: Prefer appropriate scope
953 *
954 * ret
955 * ^
956 * -1 | d 15
957 * ---+--+-+---> scope
958 * |
959 * | d is scope of the destination.
960 * B-d | \
961 * | \ <- smaller scope is better if
962 * B-15 | \ if scope is enough for destinaion.
963 * | ret = B - scope (-1 <= scope >= d <= 15).
964 * d-C-1 | /
965 * |/ <- greater is better
966 * -C / if scope is not enough for destination.
967 * /| ret = scope - C (-1 <= d < scope <= 15).
968 *
969 * d - C - 1 < B -15 (for all -1 <= d <= 15).
970 * C > d + 14 - B >= 15 + 14 - B = 29 - B.
971 * Assume B = 0 and we get C > 29.
972 */
973 ret = __ipv6_addr_src_scope(score->addr_type);
974 if (ret >= dst->scope)
975 ret = -ret;
976 else
977 ret -= 128; /* 30 is enough */
978 score->scopedist = ret;
979 break;
980 case IPV6_SADDR_RULE_PREFERRED:
981 /* Rule 3: Avoid deprecated and optimistic addresses */
982 ret = ipv6_saddr_preferred(score->addr_type) ||
983 !(score->ifa->flags & (IFA_F_DEPRECATED|IFA_F_OPTIMISTIC));
984 break;
985#ifdef CONFIG_IPV6_MIP6
986 case IPV6_SADDR_RULE_HOA:
987 /* Rule 4: Prefer home address */
988 ret = !!(score->ifa->flags & IFA_F_HOMEADDRESS);
989 break;
990#endif
991 case IPV6_SADDR_RULE_OIF:
992 /* Rule 5: Prefer outgoing interface */
993 ret = (!dst->ifindex ||
994 dst->ifindex == score->ifa->idev->dev->ifindex);
995 break;
996 case IPV6_SADDR_RULE_LABEL:
997 /* Rule 6: Prefer matching label */
998 ret = ipv6_addr_label(&score->ifa->addr, score->addr_type,
999 score->ifa->idev->dev->ifindex) == dst->label;
1000 break;
1001#ifdef CONFIG_IPV6_PRIVACY
1002 case IPV6_SADDR_RULE_PRIVACY:
1003 /* Rule 7: Prefer public address
1004 * Note: prefer temprary address if use_tempaddr >= 2
1005 */
1006 ret = (!(score->ifa->flags & IFA_F_TEMPORARY)) ^ (score->ifa->idev->cnf.use_tempaddr >= 2);
1007 break;
1008#endif
1009 case IPV6_SADDR_RULE_ORCHID:
1010 /* Rule 8-: Prefer ORCHID vs ORCHID or
1011 * non-ORCHID vs non-ORCHID
1012 */
1013 ret = !(ipv6_addr_orchid(&score->ifa->addr) ^
1014 ipv6_addr_orchid(dst->addr));
1015 break;
1016 case IPV6_SADDR_RULE_PREFIX:
1017 /* Rule 8: Use longest matching prefix */
1018 score->matchlen = ret = ipv6_addr_diff(&score->ifa->addr,
1019 dst->addr);
1020 break;
1021 default:
1022 ret = 0;
1023 }
1024
1025 if (ret)
1026 __set_bit(i, score->scorebits);
1027 score->rule = i;
1028out:
1029 return ret;
1030}
1031
1032int ipv6_dev_get_saddr(struct net_device *dst_dev,
904 struct in6_addr *daddr, struct in6_addr *saddr) 1033 struct in6_addr *daddr, struct in6_addr *saddr)
905{ 1034{
906 struct ipv6_saddr_score hiscore; 1035 struct ipv6_saddr_score scores[2],
907 struct inet6_ifaddr *ifa_result = NULL; 1036 *score = &scores[0], *hiscore = &scores[1];
908 struct net *net = daddr_dev->nd_net; 1037 struct net *net = dst_dev->nd_net;
909 int daddr_type = __ipv6_addr_type(daddr); 1038 struct ipv6_saddr_dst dst;
910 int daddr_scope = __ipv6_addr_src_scope(daddr_type);
911 int daddr_ifindex = daddr_dev ? daddr_dev->ifindex : 0;
912 u32 daddr_label = ipv6_addr_label(daddr, daddr_type, daddr_ifindex);
913 struct net_device *dev; 1039 struct net_device *dev;
1040 int dst_type;
1041
1042 dst_type = __ipv6_addr_type(daddr);
1043 dst.addr = daddr;
1044 dst.ifindex = dst_dev ? dst_dev->ifindex : 0;
1045 dst.scope = __ipv6_addr_src_scope(dst_type);
1046 dst.label = ipv6_addr_label(daddr, dst_type, dst.ifindex);
914 1047
915 memset(&hiscore, 0, sizeof(hiscore)); 1048 hiscore->rule = -1;
1049 hiscore->ifa = NULL;
916 1050
917 read_lock(&dev_base_lock); 1051 read_lock(&dev_base_lock);
918 rcu_read_lock(); 1052 rcu_read_lock();
919 1053
920 for_each_netdev(net, dev) { 1054 for_each_netdev(net, dev) {
921 struct inet6_dev *idev; 1055 struct inet6_dev *idev;
922 struct inet6_ifaddr *ifa;
923 1056
924 /* Rule 0: Candidate Source Address (section 4) 1057 /* Candidate Source Address (section 4)
925 * - multicast and link-local destination address, 1058 * - multicast and link-local destination address,
926 * the set of candidate source address MUST only 1059 * the set of candidate source address MUST only
927 * include addresses assigned to interfaces 1060 * include addresses assigned to interfaces
@@ -933,9 +1066,9 @@ int ipv6_dev_get_saddr(struct net_device *daddr_dev,
933 * belonging to the same site as the outgoing 1066 * belonging to the same site as the outgoing
934 * interface.) 1067 * interface.)
935 */ 1068 */
936 if ((daddr_type & IPV6_ADDR_MULTICAST || 1069 if (((dst_type & IPV6_ADDR_MULTICAST) ||
937 daddr_scope <= IPV6_ADDR_SCOPE_LINKLOCAL) && 1070 dst.scope <= IPV6_ADDR_SCOPE_LINKLOCAL) &&
938 daddr_dev && dev != daddr_dev) 1071 dst.ifindex && dev->ifindex != dst.ifindex)
939 continue; 1072 continue;
940 1073
941 idev = __in6_dev_get(dev); 1074 idev = __in6_dev_get(dev);
@@ -943,12 +1076,10 @@ int ipv6_dev_get_saddr(struct net_device *daddr_dev,
943 continue; 1076 continue;
944 1077
945 read_lock_bh(&idev->lock); 1078 read_lock_bh(&idev->lock);
946 for (ifa = idev->addr_list; ifa; ifa = ifa->if_next) { 1079 for (score->ifa = idev->addr_list; score->ifa; score->ifa = score->ifa->if_next) {
947 struct ipv6_saddr_score score; 1080 int i;
948 1081
949 score.addr_type = __ipv6_addr_type(&ifa->addr); 1082 /*
950
951 /* Rule 0:
952 * - Tentative Address (RFC2462 section 5.4) 1083 * - Tentative Address (RFC2462 section 5.4)
953 * - A tentative address is not considered 1084 * - A tentative address is not considered
954 * "assigned to an interface" in the traditional 1085 * "assigned to an interface" in the traditional
@@ -958,11 +1089,14 @@ int ipv6_dev_get_saddr(struct net_device *daddr_dev,
958 * addresses, and the unspecified address MUST 1089 * addresses, and the unspecified address MUST
959 * NOT be included in a candidate set. 1090 * NOT be included in a candidate set.
960 */ 1091 */
961 if ((ifa->flags & IFA_F_TENTATIVE) && 1092 if ((score->ifa->flags & IFA_F_TENTATIVE) &&
962 (!(ifa->flags & IFA_F_OPTIMISTIC))) 1093 (!(score->ifa->flags & IFA_F_OPTIMISTIC)))
963 continue; 1094 continue;
964 if (unlikely(score.addr_type == IPV6_ADDR_ANY || 1095
965 score.addr_type & IPV6_ADDR_MULTICAST)) { 1096 score->addr_type = __ipv6_addr_type(&score->ifa->addr);
1097
1098 if (unlikely(score->addr_type == IPV6_ADDR_ANY ||
1099 score->addr_type & IPV6_ADDR_MULTICAST)) {
966 LIMIT_NETDEBUG(KERN_DEBUG 1100 LIMIT_NETDEBUG(KERN_DEBUG
967 "ADDRCONF: unspecified / multicast address " 1101 "ADDRCONF: unspecified / multicast address "
968 "assigned as unicast address on %s", 1102 "assigned as unicast address on %s",
@@ -970,201 +1104,59 @@ int ipv6_dev_get_saddr(struct net_device *daddr_dev,
970 continue; 1104 continue;
971 } 1105 }
972 1106
973 score.attrs = 0; 1107 score->rule = -1;
974 score.matchlen = 0; 1108 bitmap_zero(score->scorebits, IPV6_SADDR_RULE_MAX);
975 score.scope = 0; 1109
976 score.rule = 0; 1110 for (i = 0; i < IPV6_SADDR_RULE_MAX; i++) {
977 1111 int minihiscore, miniscore;
978 if (ifa_result == NULL) { 1112
979 /* record it if the first available entry */ 1113 minihiscore = ipv6_get_saddr_eval(hiscore, &dst, i);
980 goto record_it; 1114 miniscore = ipv6_get_saddr_eval(score, &dst, i);
981 } 1115
982 1116 if (minihiscore > miniscore) {
983 /* Rule 1: Prefer same address */ 1117 if (i == IPV6_SADDR_RULE_SCOPE &&
984 if (hiscore.rule < 1) { 1118 score->scopedist > 0) {
985 if (ipv6_addr_equal(&ifa_result->addr, daddr)) 1119 /*
986 hiscore.attrs |= IPV6_SADDR_SCORE_LOCAL; 1120 * special case:
987 hiscore.rule++; 1121 * each remaining entry
988 } 1122 * has too small (not enough)
989 if (ipv6_addr_equal(&ifa->addr, daddr)) { 1123 * scope, because ifa entries
990 score.attrs |= IPV6_SADDR_SCORE_LOCAL; 1124 * are sorted by their scope
991 if (!(hiscore.attrs & IPV6_SADDR_SCORE_LOCAL)) { 1125 * values.
992 score.rule = 1; 1126 */
993 goto record_it; 1127 goto try_nextdev;
994 } 1128 }
995 } else { 1129 break;
996 if (hiscore.attrs & IPV6_SADDR_SCORE_LOCAL) 1130 } else if (minihiscore < miniscore) {
997 continue; 1131 struct ipv6_saddr_score *tmp;
998 }
999 1132
1000 /* Rule 2: Prefer appropriate scope */ 1133 if (hiscore->ifa)
1001 if (hiscore.rule < 2) { 1134 in6_ifa_put(hiscore->ifa);
1002 hiscore.scope = __ipv6_addr_src_scope(hiscore.addr_type);
1003 hiscore.rule++;
1004 }
1005 score.scope = __ipv6_addr_src_scope(score.addr_type);
1006 if (hiscore.scope < score.scope) {
1007 if (hiscore.scope < daddr_scope) {
1008 score.rule = 2;
1009 goto record_it;
1010 } else
1011 continue;
1012 } else if (score.scope < hiscore.scope) {
1013 if (score.scope < daddr_scope)
1014 break; /* addresses sorted by scope */
1015 else {
1016 score.rule = 2;
1017 goto record_it;
1018 }
1019 }
1020 1135
1021 /* Rule 3: Avoid deprecated and optimistic addresses */ 1136 in6_ifa_hold(score->ifa);
1022 if (hiscore.rule < 3) {
1023 if (ipv6_saddr_preferred(hiscore.addr_type) ||
1024 (((ifa_result->flags &
1025 (IFA_F_DEPRECATED|IFA_F_OPTIMISTIC)) == 0)))
1026 hiscore.attrs |= IPV6_SADDR_SCORE_PREFERRED;
1027 hiscore.rule++;
1028 }
1029 if (ipv6_saddr_preferred(score.addr_type) ||
1030 (((ifa->flags &
1031 (IFA_F_DEPRECATED|IFA_F_OPTIMISTIC)) == 0))) {
1032 score.attrs |= IPV6_SADDR_SCORE_PREFERRED;
1033 if (!(hiscore.attrs & IPV6_SADDR_SCORE_PREFERRED)) {
1034 score.rule = 3;
1035 goto record_it;
1036 }
1037 } else {
1038 if (hiscore.attrs & IPV6_SADDR_SCORE_PREFERRED)
1039 continue;
1040 }
1041 1137
1042 /* Rule 4: Prefer home address */ 1138 tmp = hiscore;
1043#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) 1139 hiscore = score;
1044 if (hiscore.rule < 4) { 1140 score = tmp;
1045 if (ifa_result->flags & IFA_F_HOMEADDRESS)
1046 hiscore.attrs |= IPV6_SADDR_SCORE_HOA;
1047 hiscore.rule++;
1048 }
1049 if (ifa->flags & IFA_F_HOMEADDRESS) {
1050 score.attrs |= IPV6_SADDR_SCORE_HOA;
1051 if (!(ifa_result->flags & IFA_F_HOMEADDRESS)) {
1052 score.rule = 4;
1053 goto record_it;
1054 }
1055 } else {
1056 if (hiscore.attrs & IPV6_SADDR_SCORE_HOA)
1057 continue;
1058 }
1059#else
1060 if (hiscore.rule < 4)
1061 hiscore.rule++;
1062#endif
1063 1141
1064 /* Rule 5: Prefer outgoing interface */ 1142 /* restore our iterator */
1065 if (hiscore.rule < 5) { 1143 score->ifa = hiscore->ifa;
1066 if (daddr_dev == NULL ||
1067 daddr_dev == ifa_result->idev->dev)
1068 hiscore.attrs |= IPV6_SADDR_SCORE_OIF;
1069 hiscore.rule++;
1070 }
1071 if (daddr_dev == NULL ||
1072 daddr_dev == ifa->idev->dev) {
1073 score.attrs |= IPV6_SADDR_SCORE_OIF;
1074 if (!(hiscore.attrs & IPV6_SADDR_SCORE_OIF)) {
1075 score.rule = 5;
1076 goto record_it;
1077 }
1078 } else {
1079 if (hiscore.attrs & IPV6_SADDR_SCORE_OIF)
1080 continue;
1081 }
1082
1083 /* Rule 6: Prefer matching label */
1084 if (hiscore.rule < 6) {
1085 if (ipv6_addr_label(&ifa_result->addr,
1086 hiscore.addr_type,
1087 ifa_result->idev->dev->ifindex) == daddr_label)
1088 hiscore.attrs |= IPV6_SADDR_SCORE_LABEL;
1089 hiscore.rule++;
1090 }
1091 if (ipv6_addr_label(&ifa->addr,
1092 score.addr_type,
1093 ifa->idev->dev->ifindex) == daddr_label) {
1094 score.attrs |= IPV6_SADDR_SCORE_LABEL;
1095 if (!(hiscore.attrs & IPV6_SADDR_SCORE_LABEL)) {
1096 score.rule = 6;
1097 goto record_it;
1098 }
1099 } else {
1100 if (hiscore.attrs & IPV6_SADDR_SCORE_LABEL)
1101 continue;
1102 }
1103 1144
1104#ifdef CONFIG_IPV6_PRIVACY 1145 break;
1105 /* Rule 7: Prefer public address
1106 * Note: prefer temprary address if use_tempaddr >= 2
1107 */
1108 if (hiscore.rule < 7) {
1109 if ((!(ifa_result->flags & IFA_F_TEMPORARY)) ^
1110 (ifa_result->idev->cnf.use_tempaddr >= 2))
1111 hiscore.attrs |= IPV6_SADDR_SCORE_PRIVACY;
1112 hiscore.rule++;
1113 }
1114 if ((!(ifa->flags & IFA_F_TEMPORARY)) ^
1115 (ifa->idev->cnf.use_tempaddr >= 2)) {
1116 score.attrs |= IPV6_SADDR_SCORE_PRIVACY;
1117 if (!(hiscore.attrs & IPV6_SADDR_SCORE_PRIVACY)) {
1118 score.rule = 7;
1119 goto record_it;
1120 } 1146 }
1121 } else {
1122 if (hiscore.attrs & IPV6_SADDR_SCORE_PRIVACY)
1123 continue;
1124 }
1125#else
1126 if (hiscore.rule < 7)
1127 hiscore.rule++;
1128#endif
1129
1130 /* Skip rule 8 for orchid -> non-orchid address pairs. */
1131 if (ipv6_addr_orchid(&ifa->addr) && !ipv6_addr_orchid(daddr))
1132 continue;
1133
1134 /* Rule 8: Use longest matching prefix */
1135 if (hiscore.rule < 8) {
1136 hiscore.matchlen = ipv6_addr_diff(&ifa_result->addr, daddr);
1137 hiscore.rule++;
1138 }
1139 score.matchlen = ipv6_addr_diff(&ifa->addr, daddr);
1140 if (score.matchlen > hiscore.matchlen) {
1141 score.rule = 8;
1142 goto record_it;
1143 } 1147 }
1144#if 0
1145 else if (score.matchlen < hiscore.matchlen)
1146 continue;
1147#endif
1148
1149 /* Final Rule: choose first available one */
1150 continue;
1151record_it:
1152 if (ifa_result)
1153 in6_ifa_put(ifa_result);
1154 in6_ifa_hold(ifa);
1155 ifa_result = ifa;
1156 hiscore = score;
1157 } 1148 }
1149try_nextdev:
1158 read_unlock_bh(&idev->lock); 1150 read_unlock_bh(&idev->lock);
1159 } 1151 }
1160 rcu_read_unlock(); 1152 rcu_read_unlock();
1161 read_unlock(&dev_base_lock); 1153 read_unlock(&dev_base_lock);
1162 1154
1163 if (!ifa_result) 1155 if (!hiscore->ifa)
1164 return -EADDRNOTAVAIL; 1156 return -EADDRNOTAVAIL;
1165 1157
1166 ipv6_addr_copy(saddr, &ifa_result->addr); 1158 ipv6_addr_copy(saddr, &hiscore->ifa->addr);
1167 in6_ifa_put(ifa_result); 1159 in6_ifa_put(hiscore->ifa);
1168 return 0; 1160 return 0;
1169} 1161}
1170 1162