aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/addrconf.c
diff options
context:
space:
mode:
authorYOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>2005-11-08 12:38:30 -0500
committerDavid S. Miller <davem@davemloft.net>2005-11-08 12:38:30 -0500
commit072047e4de3800905e09d0f8ef0e1cc4e91a601e (patch)
treef4b7b8c74213bc0b075d252eab46db0b012323fc /net/ipv6/addrconf.c
parentb1cacb6820e0afc4aeeea67bcb5296a316862cad (diff)
[IPV6]: RFC3484 compliant source address selection
Choose more appropriate source address; e.g. - outgoing interface - non-deprecated - scope - matching label Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6/addrconf.c')
-rw-r--r--net/ipv6/addrconf.c344
1 files changed, 240 insertions, 104 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index ff895da6395b..a34d1504deb9 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -809,138 +809,274 @@ out:
809#endif 809#endif
810 810
811/* 811/*
812 * Choose an appropriate source address 812 * Choose an appropriate source address (RFC3484)
813 * should do:
814 * i) get an address with an appropriate scope
815 * ii) see if there is a specific route for the destination and use
816 * an address of the attached interface
817 * iii) don't use deprecated addresses
818 */ 813 */
819static int inline ipv6_saddr_pref(const struct inet6_ifaddr *ifp, u8 invpref) 814struct ipv6_saddr_score {
815 int addr_type;
816 unsigned int attrs;
817 int matchlen;
818 unsigned int scope;
819 unsigned int rule;
820};
821
822#define IPV6_SADDR_SCORE_LOCAL 0x0001
823#define IPV6_SADDR_SCORE_PREFERRED 0x0004
824#define IPV6_SADDR_SCORE_HOA 0x0008
825#define IPV6_SADDR_SCORE_OIF 0x0010
826#define IPV6_SADDR_SCORE_LABEL 0x0020
827#define IPV6_SADDR_SCORE_PRIVACY 0x0040
828
829static int inline ipv6_saddr_preferred(int type)
820{ 830{
821 int pref; 831 if (type & (IPV6_ADDR_MAPPED|IPV6_ADDR_COMPATv4|
822 pref = ifp->flags&IFA_F_DEPRECATED ? 0 : 2; 832 IPV6_ADDR_LOOPBACK|IPV6_ADDR_RESERVED))
823#ifdef CONFIG_IPV6_PRIVACY 833 return 1;
824 pref |= (ifp->flags^invpref)&IFA_F_TEMPORARY ? 0 : 1; 834 return 0;
825#endif
826 return pref;
827} 835}
828 836
829#ifdef CONFIG_IPV6_PRIVACY 837/* static matching label */
830#define IPV6_GET_SADDR_MAXSCORE(score) ((score) == 3) 838static int inline ipv6_saddr_label(const struct in6_addr *addr, int type)
831#else 839{
832#define IPV6_GET_SADDR_MAXSCORE(score) (score) 840 /*
833#endif 841 * prefix (longest match) label
842 * -----------------------------
843 * ::1/128 0
844 * ::/0 1
845 * 2002::/16 2
846 * ::/96 3
847 * ::ffff:0:0/96 4
848 */
849 if (type & IPV6_ADDR_LOOPBACK)
850 return 0;
851 else if (type & IPV6_ADDR_COMPATv4)
852 return 3;
853 else if (type & IPV6_ADDR_MAPPED)
854 return 4;
855 else if (addr->s6_addr16[0] == htons(0x2002))
856 return 2;
857 return 1;
858}
834 859
835int ipv6_dev_get_saddr(struct net_device *dev, 860int ipv6_dev_get_saddr(struct net_device *daddr_dev,
836 struct in6_addr *daddr, struct in6_addr *saddr) 861 struct in6_addr *daddr, struct in6_addr *saddr)
837{ 862{
838 struct inet6_ifaddr *ifp = NULL; 863 struct ipv6_saddr_score hiscore;
839 struct inet6_ifaddr *match = NULL; 864 struct inet6_ifaddr *ifa_result = NULL;
840 struct inet6_dev *idev; 865 int daddr_type = __ipv6_addr_type(daddr);
841 int scope; 866 int daddr_scope = __ipv6_addr_src_scope(daddr_type);
842 int err; 867 u32 daddr_label = ipv6_saddr_label(daddr, daddr_type);
843 int hiscore = -1, score; 868 struct net_device *dev;
844 869
845 scope = ipv6_addr_scope(daddr); 870 memset(&hiscore, 0, sizeof(hiscore));
846 871
847 /* 872 read_lock(&dev_base_lock);
848 * known dev 873 read_lock(&addrconf_lock);
849 * search dev and walk through dev addresses
850 */
851 874
852 if (dev) { 875 for (dev = dev_base; dev; dev=dev->next) {
853 if (dev->flags & IFF_LOOPBACK) 876 struct inet6_dev *idev;
854 scope = IFA_HOST; 877 struct inet6_ifaddr *ifa;
878
879 /* Rule 0: Candidate Source Address (section 4)
880 * - multicast and link-local destination address,
881 * the set of candidate source address MUST only
882 * include addresses assigned to interfaces
883 * belonging to the same link as the outgoing
884 * interface.
885 * (- For site-local destination addresses, the
886 * set of candidate source addresses MUST only
887 * include addresses assigned to interfaces
888 * belonging to the same site as the outgoing
889 * interface.)
890 */
891 if ((daddr_type & IPV6_ADDR_MULTICAST ||
892 daddr_scope <= IPV6_ADDR_SCOPE_LINKLOCAL) &&
893 daddr_dev && dev != daddr_dev)
894 continue;
855 895
856 read_lock(&addrconf_lock);
857 idev = __in6_dev_get(dev); 896 idev = __in6_dev_get(dev);
858 if (idev) { 897 if (!idev)
859 read_lock_bh(&idev->lock); 898 continue;
860 for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) {
861 if (ifp->scope == scope) {
862 if (ifp->flags&IFA_F_TENTATIVE)
863 continue;
864#ifdef CONFIG_IPV6_PRIVACY
865 score = ipv6_saddr_pref(ifp, idev->cnf.use_tempaddr > 1 ? IFA_F_TEMPORARY : 0);
866#else
867 score = ipv6_saddr_pref(ifp, 0);
868#endif
869 if (score <= hiscore)
870 continue;
871 899
872 if (match) 900 read_lock_bh(&idev->lock);
873 in6_ifa_put(match); 901 for (ifa = idev->addr_list; ifa; ifa = ifa->if_next) {
874 match = ifp; 902 struct ipv6_saddr_score score;
875 hiscore = score;
876 in6_ifa_hold(ifp);
877 903
878 if (IPV6_GET_SADDR_MAXSCORE(score)) { 904 score.addr_type = __ipv6_addr_type(&ifa->addr);
879 read_unlock_bh(&idev->lock); 905
880 read_unlock(&addrconf_lock); 906 /* Rule 0: Candidate Source Address (section 4)
881 goto out; 907 * - In any case, anycast addresses, multicast
882 } 908 * addresses, and the unspecified address MUST
909 * NOT be included in a candidate set.
910 */
911 if (unlikely(score.addr_type == IPV6_ADDR_ANY ||
912 score.addr_type & IPV6_ADDR_MULTICAST)) {
913 LIMIT_NETDEBUG(KERN_DEBUG
914 "ADDRCONF: unspecified / multicast address"
915 "assigned as unicast address on %s",
916 dev->name);
917 continue;
918 }
919
920 score.attrs = 0;
921 score.matchlen = 0;
922 score.scope = 0;
923 score.rule = 0;
924
925 if (ifa_result == NULL) {
926 /* record it if the first available entry */
927 goto record_it;
928 }
929
930 /* Rule 1: Prefer same address */
931 if (hiscore.rule < 1) {
932 if (ipv6_addr_equal(&ifa_result->addr, daddr))
933 hiscore.attrs |= IPV6_SADDR_SCORE_LOCAL;
934 hiscore.rule++;
935 }
936 if (ipv6_addr_equal(&ifa->addr, daddr)) {
937 score.attrs |= IPV6_SADDR_SCORE_LOCAL;
938 if (!(hiscore.attrs & IPV6_SADDR_SCORE_LOCAL)) {
939 score.rule = 1;
940 goto record_it;
883 } 941 }
942 } else {
943 if (hiscore.attrs & IPV6_SADDR_SCORE_LOCAL)
944 continue;
884 } 945 }
885 read_unlock_bh(&idev->lock);
886 }
887 read_unlock(&addrconf_lock);
888 }
889 946
890 if (scope == IFA_LINK) 947 /* Rule 2: Prefer appropriate scope */
891 goto out; 948 if (hiscore.rule < 2) {
949 hiscore.scope = __ipv6_addr_src_scope(hiscore.addr_type);
950 hiscore.rule++;
951 }
952 score.scope = __ipv6_addr_src_scope(score.addr_type);
953 if (hiscore.scope < score.scope) {
954 if (hiscore.scope < daddr_scope) {
955 score.rule = 2;
956 goto record_it;
957 } else
958 continue;
959 } else if (score.scope < hiscore.scope) {
960 if (score.scope < daddr_scope)
961 continue;
962 else {
963 score.rule = 2;
964 goto record_it;
965 }
966 }
892 967
893 /* 968 /* Rule 3: Avoid deprecated address */
894 * dev == NULL or search failed for specified dev 969 if (hiscore.rule < 3) {
895 */ 970 if (ipv6_saddr_preferred(hiscore.addr_type) ||
971 !(ifa_result->flags & IFA_F_DEPRECATED))
972 hiscore.attrs |= IPV6_SADDR_SCORE_PREFERRED;
973 hiscore.rule++;
974 }
975 if (ipv6_saddr_preferred(score.addr_type) ||
976 !(ifa->flags & IFA_F_DEPRECATED)) {
977 score.attrs |= IPV6_SADDR_SCORE_PREFERRED;
978 if (!(hiscore.attrs & IPV6_SADDR_SCORE_PREFERRED)) {
979 score.rule = 3;
980 goto record_it;
981 }
982 } else {
983 if (hiscore.attrs & IPV6_SADDR_SCORE_PREFERRED)
984 continue;
985 }
896 986
897 read_lock(&dev_base_lock); 987 /* Rule 4: Prefer home address -- not implemented yet */
898 read_lock(&addrconf_lock);
899 for (dev = dev_base; dev; dev=dev->next) {
900 idev = __in6_dev_get(dev);
901 if (idev) {
902 read_lock_bh(&idev->lock);
903 for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) {
904 if (ifp->scope == scope) {
905 if (ifp->flags&IFA_F_TENTATIVE)
906 continue;
907#ifdef CONFIG_IPV6_PRIVACY
908 score = ipv6_saddr_pref(ifp, idev->cnf.use_tempaddr > 1 ? IFA_F_TEMPORARY : 0);
909#else
910 score = ipv6_saddr_pref(ifp, 0);
911#endif
912 if (score <= hiscore)
913 continue;
914 988
915 if (match) 989 /* Rule 5: Prefer outgoing interface */
916 in6_ifa_put(match); 990 if (hiscore.rule < 5) {
917 match = ifp; 991 if (daddr_dev == NULL ||
918 hiscore = score; 992 daddr_dev == ifa_result->idev->dev)
919 in6_ifa_hold(ifp); 993 hiscore.attrs |= IPV6_SADDR_SCORE_OIF;
994 hiscore.rule++;
995 }
996 if (daddr_dev == NULL ||
997 daddr_dev == ifa->idev->dev) {
998 score.attrs |= IPV6_SADDR_SCORE_OIF;
999 if (!(hiscore.attrs & IPV6_SADDR_SCORE_OIF)) {
1000 score.rule = 5;
1001 goto record_it;
1002 }
1003 } else {
1004 if (hiscore.attrs & IPV6_SADDR_SCORE_OIF)
1005 continue;
1006 }
920 1007
921 if (IPV6_GET_SADDR_MAXSCORE(score)) { 1008 /* Rule 6: Prefer matching label */
922 read_unlock_bh(&idev->lock); 1009 if (hiscore.rule < 6) {
923 goto out_unlock_base; 1010 if (ipv6_saddr_label(&ifa_result->addr, hiscore.addr_type) == daddr_label)
924 } 1011 hiscore.attrs |= IPV6_SADDR_SCORE_LABEL;
1012 hiscore.rule++;
1013 }
1014 if (ipv6_saddr_label(&ifa->addr, score.addr_type) == daddr_label) {
1015 score.attrs |= IPV6_SADDR_SCORE_LABEL;
1016 if (!(hiscore.attrs & IPV6_SADDR_SCORE_LABEL)) {
1017 score.rule = 6;
1018 goto record_it;
925 } 1019 }
1020 } else {
1021 if (hiscore.attrs & IPV6_SADDR_SCORE_LABEL)
1022 continue;
926 } 1023 }
927 read_unlock_bh(&idev->lock); 1024
1025 /* Rule 7: Prefer public address
1026 * Note: prefer temprary address if use_tempaddr >= 2
1027 */
1028 if (hiscore.rule < 7) {
1029 if ((!(ifa_result->flags & IFA_F_TEMPORARY)) ^
1030 (ifa_result->idev->cnf.use_tempaddr >= 2))
1031 hiscore.attrs |= IPV6_SADDR_SCORE_PRIVACY;
1032 hiscore.rule++;
1033 }
1034 if ((!(ifa->flags & IFA_F_TEMPORARY)) ^
1035 (ifa->idev->cnf.use_tempaddr >= 2)) {
1036 score.attrs |= IPV6_SADDR_SCORE_PRIVACY;
1037 if (!(hiscore.attrs & IPV6_SADDR_SCORE_PRIVACY)) {
1038 score.rule = 7;
1039 goto record_it;
1040 }
1041 } else {
1042 if (hiscore.attrs & IPV6_SADDR_SCORE_PRIVACY)
1043 continue;
1044 }
1045
1046 /* Rule 8: Use longest matching prefix */
1047 if (hiscore.rule < 8)
1048 hiscore.matchlen = ipv6_addr_diff(&ifa_result->addr, daddr);
1049 score.rule++;
1050 score.matchlen = ipv6_addr_diff(&ifa->addr, daddr);
1051 if (score.matchlen > hiscore.matchlen) {
1052 score.rule = 8;
1053 goto record_it;
1054 }
1055#if 0
1056 else if (score.matchlen < hiscore.matchlen)
1057 continue;
1058#endif
1059
1060 /* Final Rule: choose first available one */
1061 continue;
1062record_it:
1063 if (ifa_result)
1064 in6_ifa_put(ifa_result);
1065 in6_ifa_hold(ifa);
1066 ifa_result = ifa;
1067 hiscore = score;
928 } 1068 }
1069 read_unlock_bh(&idev->lock);
929 } 1070 }
930
931out_unlock_base:
932 read_unlock(&addrconf_lock); 1071 read_unlock(&addrconf_lock);
933 read_unlock(&dev_base_lock); 1072 read_unlock(&dev_base_lock);
934 1073
935out: 1074 if (!ifa_result)
936 err = -EADDRNOTAVAIL; 1075 return -EADDRNOTAVAIL;
937 if (match) { 1076
938 ipv6_addr_copy(saddr, &match->addr); 1077 ipv6_addr_copy(saddr, &ifa_result->addr);
939 err = 0; 1078 in6_ifa_put(ifa_result);
940 in6_ifa_put(match); 1079 return 0;
941 }
942
943 return err;
944} 1080}
945 1081
946 1082