diff options
author | YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org> | 2005-11-08 12:38:30 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2005-11-08 12:38:30 -0500 |
commit | 072047e4de3800905e09d0f8ef0e1cc4e91a601e (patch) | |
tree | f4b7b8c74213bc0b075d252eab46db0b012323fc | |
parent | b1cacb6820e0afc4aeeea67bcb5296a316862cad (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>
-rw-r--r-- | net/ipv6/addrconf.c | 344 |
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 | */ |
819 | static int inline ipv6_saddr_pref(const struct inet6_ifaddr *ifp, u8 invpref) | 814 | struct 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 | |||
829 | static 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) | 838 | static 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 | ||
835 | int ipv6_dev_get_saddr(struct net_device *dev, | 860 | int 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; | ||
1062 | record_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 | |||
931 | out_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 | ||
935 | out: | 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 | ||