diff options
Diffstat (limited to 'net/ipv6')
| -rw-r--r-- | net/ipv6/addrconf.c | 434 | ||||
| -rw-r--r-- | net/ipv6/ip6_fib.c | 54 | ||||
| -rw-r--r-- | net/ipv6/ip6_input.c | 5 | ||||
| -rw-r--r-- | net/ipv6/ip6_output.c | 21 | ||||
| -rw-r--r-- | net/ipv6/ip6_tunnel.c | 7 | ||||
| -rw-r--r-- | net/ipv6/ipcomp6.c | 3 | ||||
| -rw-r--r-- | net/ipv6/ipv6_sockglue.c | 3 | ||||
| -rw-r--r-- | net/ipv6/ipv6_syms.c | 2 | ||||
| -rw-r--r-- | net/ipv6/netfilter/Kconfig | 14 | ||||
| -rw-r--r-- | net/ipv6/netfilter/Makefile | 6 | ||||
| -rw-r--r-- | net/ipv6/netfilter/ip6t_MARK.c | 6 | ||||
| -rw-r--r-- | net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 556 | ||||
| -rw-r--r-- | net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c | 272 | ||||
| -rw-r--r-- | net/ipv6/netfilter/nf_conntrack_reasm.c | 885 | ||||
| -rw-r--r-- | net/ipv6/raw.c | 4 | ||||
| -rw-r--r-- | net/ipv6/route.c | 2 | ||||
| -rw-r--r-- | net/ipv6/tcp_ipv6.c | 15 |
17 files changed, 2055 insertions, 234 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 2c5f57299d63..ddcf7754eec2 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c | |||
| @@ -35,6 +35,9 @@ | |||
| 35 | * YOSHIFUJI Hideaki @USAGI : ARCnet support | 35 | * YOSHIFUJI Hideaki @USAGI : ARCnet support |
| 36 | * YOSHIFUJI Hideaki @USAGI : convert /proc/net/if_inet6 to | 36 | * YOSHIFUJI Hideaki @USAGI : convert /proc/net/if_inet6 to |
| 37 | * seq_file. | 37 | * seq_file. |
| 38 | * YOSHIFUJI Hideaki @USAGI : improved source address | ||
| 39 | * selection; consider scope, | ||
| 40 | * status etc. | ||
| 38 | */ | 41 | */ |
| 39 | 42 | ||
| 40 | #include <linux/config.h> | 43 | #include <linux/config.h> |
| @@ -193,46 +196,51 @@ const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; | |||
| 193 | #endif | 196 | #endif |
| 194 | const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT; | 197 | const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT; |
| 195 | 198 | ||
| 196 | int ipv6_addr_type(const struct in6_addr *addr) | 199 | #define IPV6_ADDR_SCOPE_TYPE(scope) ((scope) << 16) |
| 200 | |||
| 201 | static inline unsigned ipv6_addr_scope2type(unsigned scope) | ||
| 202 | { | ||
| 203 | switch(scope) { | ||
| 204 | case IPV6_ADDR_SCOPE_NODELOCAL: | ||
| 205 | return (IPV6_ADDR_SCOPE_TYPE(IPV6_ADDR_SCOPE_NODELOCAL) | | ||
| 206 | IPV6_ADDR_LOOPBACK); | ||
| 207 | case IPV6_ADDR_SCOPE_LINKLOCAL: | ||
| 208 | return (IPV6_ADDR_SCOPE_TYPE(IPV6_ADDR_SCOPE_LINKLOCAL) | | ||
| 209 | IPV6_ADDR_LINKLOCAL); | ||
| 210 | case IPV6_ADDR_SCOPE_SITELOCAL: | ||
| 211 | return (IPV6_ADDR_SCOPE_TYPE(IPV6_ADDR_SCOPE_SITELOCAL) | | ||
| 212 | IPV6_ADDR_SITELOCAL); | ||
| 213 | } | ||
| 214 | return IPV6_ADDR_SCOPE_TYPE(scope); | ||
| 215 | } | ||
| 216 | |||
| 217 | int __ipv6_addr_type(const struct in6_addr *addr) | ||
| 197 | { | 218 | { |
| 198 | int type; | ||
| 199 | u32 st; | 219 | u32 st; |
| 200 | 220 | ||
| 201 | st = addr->s6_addr32[0]; | 221 | st = addr->s6_addr32[0]; |
| 202 | 222 | ||
| 203 | if ((st & htonl(0xFF000000)) == htonl(0xFF000000)) { | ||
| 204 | type = IPV6_ADDR_MULTICAST; | ||
| 205 | |||
| 206 | switch((st & htonl(0x00FF0000))) { | ||
| 207 | case __constant_htonl(0x00010000): | ||
| 208 | type |= IPV6_ADDR_LOOPBACK; | ||
| 209 | break; | ||
| 210 | |||
| 211 | case __constant_htonl(0x00020000): | ||
| 212 | type |= IPV6_ADDR_LINKLOCAL; | ||
| 213 | break; | ||
| 214 | |||
| 215 | case __constant_htonl(0x00050000): | ||
| 216 | type |= IPV6_ADDR_SITELOCAL; | ||
| 217 | break; | ||
| 218 | }; | ||
| 219 | return type; | ||
| 220 | } | ||
| 221 | |||
| 222 | type = IPV6_ADDR_UNICAST; | ||
| 223 | |||
| 224 | /* Consider all addresses with the first three bits different of | 223 | /* Consider all addresses with the first three bits different of |
| 225 | 000 and 111 as finished. | 224 | 000 and 111 as unicasts. |
| 226 | */ | 225 | */ |
| 227 | if ((st & htonl(0xE0000000)) != htonl(0x00000000) && | 226 | if ((st & htonl(0xE0000000)) != htonl(0x00000000) && |
| 228 | (st & htonl(0xE0000000)) != htonl(0xE0000000)) | 227 | (st & htonl(0xE0000000)) != htonl(0xE0000000)) |
| 229 | return type; | 228 | return (IPV6_ADDR_UNICAST | |
| 230 | 229 | IPV6_ADDR_SCOPE_TYPE(IPV6_ADDR_SCOPE_GLOBAL)); | |
| 231 | if ((st & htonl(0xFFC00000)) == htonl(0xFE800000)) | 230 | |
| 232 | return (IPV6_ADDR_LINKLOCAL | type); | 231 | if ((st & htonl(0xFF000000)) == htonl(0xFF000000)) { |
| 232 | /* multicast */ | ||
| 233 | /* addr-select 3.1 */ | ||
| 234 | return (IPV6_ADDR_MULTICAST | | ||
| 235 | ipv6_addr_scope2type(IPV6_ADDR_MC_SCOPE(addr))); | ||
| 236 | } | ||
| 233 | 237 | ||
| 238 | if ((st & htonl(0xFFC00000)) == htonl(0xFE800000)) | ||
| 239 | return (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_UNICAST | | ||
| 240 | IPV6_ADDR_SCOPE_TYPE(IPV6_ADDR_SCOPE_LINKLOCAL)); /* addr-select 3.1 */ | ||
| 234 | if ((st & htonl(0xFFC00000)) == htonl(0xFEC00000)) | 241 | if ((st & htonl(0xFFC00000)) == htonl(0xFEC00000)) |
| 235 | return (IPV6_ADDR_SITELOCAL | type); | 242 | return (IPV6_ADDR_SITELOCAL | IPV6_ADDR_UNICAST | |
| 243 | IPV6_ADDR_SCOPE_TYPE(IPV6_ADDR_SCOPE_SITELOCAL)); /* addr-select 3.1 */ | ||
| 236 | 244 | ||
| 237 | if ((addr->s6_addr32[0] | addr->s6_addr32[1]) == 0) { | 245 | if ((addr->s6_addr32[0] | addr->s6_addr32[1]) == 0) { |
| 238 | if (addr->s6_addr32[2] == 0) { | 246 | if (addr->s6_addr32[2] == 0) { |
| @@ -240,24 +248,20 @@ int ipv6_addr_type(const struct in6_addr *addr) | |||
| 240 | return IPV6_ADDR_ANY; | 248 | return IPV6_ADDR_ANY; |
| 241 | 249 | ||
| 242 | if (addr->s6_addr32[3] == htonl(0x00000001)) | 250 | if (addr->s6_addr32[3] == htonl(0x00000001)) |
| 243 | return (IPV6_ADDR_LOOPBACK | type); | 251 | return (IPV6_ADDR_LOOPBACK | IPV6_ADDR_UNICAST | |
| 252 | IPV6_ADDR_SCOPE_TYPE(IPV6_ADDR_SCOPE_LINKLOCAL)); /* addr-select 3.4 */ | ||
| 244 | 253 | ||
| 245 | return (IPV6_ADDR_COMPATv4 | type); | 254 | return (IPV6_ADDR_COMPATv4 | IPV6_ADDR_UNICAST | |
| 255 | IPV6_ADDR_SCOPE_TYPE(IPV6_ADDR_SCOPE_GLOBAL)); /* addr-select 3.3 */ | ||
| 246 | } | 256 | } |
| 247 | 257 | ||
| 248 | if (addr->s6_addr32[2] == htonl(0x0000ffff)) | 258 | if (addr->s6_addr32[2] == htonl(0x0000ffff)) |
| 249 | return IPV6_ADDR_MAPPED; | 259 | return (IPV6_ADDR_MAPPED | |
| 260 | IPV6_ADDR_SCOPE_TYPE(IPV6_ADDR_SCOPE_GLOBAL)); /* addr-select 3.3 */ | ||
| 250 | } | 261 | } |
| 251 | 262 | ||
| 252 | st &= htonl(0xFF000000); | 263 | return (IPV6_ADDR_RESERVED | |
| 253 | if (st == 0) | 264 | IPV6_ADDR_SCOPE_TYPE(IPV6_ADDR_SCOPE_GLOBAL)); /* addr-select 3.4 */ |
| 254 | return IPV6_ADDR_RESERVED; | ||
| 255 | st &= htonl(0xFE000000); | ||
| 256 | if (st == htonl(0x02000000)) | ||
| 257 | return IPV6_ADDR_RESERVED; /* for NSAP */ | ||
| 258 | if (st == htonl(0x04000000)) | ||
| 259 | return IPV6_ADDR_RESERVED; /* for IPX */ | ||
| 260 | return type; | ||
| 261 | } | 265 | } |
| 262 | 266 | ||
| 263 | static void addrconf_del_timer(struct inet6_ifaddr *ifp) | 267 | static void addrconf_del_timer(struct inet6_ifaddr *ifp) |
| @@ -805,138 +809,275 @@ out: | |||
| 805 | #endif | 809 | #endif |
| 806 | 810 | ||
| 807 | /* | 811 | /* |
| 808 | * Choose an appropriate source address | 812 | * Choose an appropriate source address (RFC3484) |
| 809 | * should do: | ||
| 810 | * i) get an address with an appropriate scope | ||
| 811 | * ii) see if there is a specific route for the destination and use | ||
| 812 | * an address of the attached interface | ||
| 813 | * iii) don't use deprecated addresses | ||
| 814 | */ | 813 | */ |
| 815 | 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) | ||
| 816 | { | 830 | { |
| 817 | int pref; | 831 | if (type & (IPV6_ADDR_MAPPED|IPV6_ADDR_COMPATv4| |
| 818 | pref = ifp->flags&IFA_F_DEPRECATED ? 0 : 2; | 832 | IPV6_ADDR_LOOPBACK|IPV6_ADDR_RESERVED)) |
| 819 | #ifdef CONFIG_IPV6_PRIVACY | 833 | return 1; |
| 820 | pref |= (ifp->flags^invpref)&IFA_F_TEMPORARY ? 0 : 1; | 834 | return 0; |
| 821 | #endif | ||
| 822 | return pref; | ||
| 823 | } | 835 | } |
| 824 | 836 | ||
| 825 | #ifdef CONFIG_IPV6_PRIVACY | 837 | /* static matching label */ |
| 826 | #define IPV6_GET_SADDR_MAXSCORE(score) ((score) == 3) | 838 | static int inline ipv6_saddr_label(const struct in6_addr *addr, int type) |
| 827 | #else | 839 | { |
| 828 | #define IPV6_GET_SADDR_MAXSCORE(score) (score) | 840 | /* |
| 829 | #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 | } | ||
| 830 | 859 | ||
| 831 | int ipv6_dev_get_saddr(struct net_device *dev, | 860 | int ipv6_dev_get_saddr(struct net_device *daddr_dev, |
| 832 | struct in6_addr *daddr, struct in6_addr *saddr) | 861 | struct in6_addr *daddr, struct in6_addr *saddr) |
| 833 | { | 862 | { |
| 834 | struct inet6_ifaddr *ifp = NULL; | 863 | struct ipv6_saddr_score hiscore; |
| 835 | struct inet6_ifaddr *match = NULL; | 864 | struct inet6_ifaddr *ifa_result = NULL; |
| 836 | struct inet6_dev *idev; | 865 | int daddr_type = __ipv6_addr_type(daddr); |
| 837 | int scope; | 866 | int daddr_scope = __ipv6_addr_src_scope(daddr_type); |
| 838 | int err; | 867 | u32 daddr_label = ipv6_saddr_label(daddr, daddr_type); |
| 839 | int hiscore = -1, score; | 868 | struct net_device *dev; |
| 840 | 869 | ||
| 841 | scope = ipv6_addr_scope(daddr); | 870 | memset(&hiscore, 0, sizeof(hiscore)); |
| 842 | 871 | ||
| 843 | /* | 872 | read_lock(&dev_base_lock); |
| 844 | * known dev | 873 | read_lock(&addrconf_lock); |
| 845 | * search dev and walk through dev addresses | ||
| 846 | */ | ||
| 847 | 874 | ||
| 848 | if (dev) { | 875 | for (dev = dev_base; dev; dev=dev->next) { |
| 849 | if (dev->flags & IFF_LOOPBACK) | 876 | struct inet6_dev *idev; |
| 850 | 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; | ||
| 851 | 895 | ||
| 852 | read_lock(&addrconf_lock); | ||
| 853 | idev = __in6_dev_get(dev); | 896 | idev = __in6_dev_get(dev); |
| 854 | if (idev) { | 897 | if (!idev) |
| 855 | read_lock_bh(&idev->lock); | 898 | continue; |
| 856 | for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) { | ||
| 857 | if (ifp->scope == scope) { | ||
| 858 | if (ifp->flags&IFA_F_TENTATIVE) | ||
| 859 | continue; | ||
| 860 | #ifdef CONFIG_IPV6_PRIVACY | ||
| 861 | score = ipv6_saddr_pref(ifp, idev->cnf.use_tempaddr > 1 ? IFA_F_TEMPORARY : 0); | ||
| 862 | #else | ||
| 863 | score = ipv6_saddr_pref(ifp, 0); | ||
| 864 | #endif | ||
| 865 | if (score <= hiscore) | ||
| 866 | continue; | ||
| 867 | 899 | ||
| 868 | if (match) | 900 | read_lock_bh(&idev->lock); |
| 869 | in6_ifa_put(match); | 901 | for (ifa = idev->addr_list; ifa; ifa = ifa->if_next) { |
| 870 | match = ifp; | 902 | struct ipv6_saddr_score score; |
| 871 | hiscore = score; | ||
| 872 | in6_ifa_hold(ifp); | ||
| 873 | 903 | ||
| 874 | if (IPV6_GET_SADDR_MAXSCORE(score)) { | 904 | score.addr_type = __ipv6_addr_type(&ifa->addr); |
| 875 | read_unlock_bh(&idev->lock); | 905 | |
| 876 | read_unlock(&addrconf_lock); | 906 | /* Rule 0: Candidate Source Address (section 4) |
| 877 | goto out; | 907 | * - In any case, anycast addresses, multicast |
| 878 | } | 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; | ||
| 879 | } | 941 | } |
| 942 | } else { | ||
| 943 | if (hiscore.attrs & IPV6_SADDR_SCORE_LOCAL) | ||
| 944 | continue; | ||
| 880 | } | 945 | } |
| 881 | read_unlock_bh(&idev->lock); | ||
| 882 | } | ||
| 883 | read_unlock(&addrconf_lock); | ||
| 884 | } | ||
| 885 | 946 | ||
| 886 | if (scope == IFA_LINK) | 947 | /* Rule 2: Prefer appropriate scope */ |
| 887 | 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 | } | ||
| 888 | 967 | ||
| 889 | /* | 968 | /* Rule 3: Avoid deprecated address */ |
| 890 | * dev == NULL or search failed for specified dev | 969 | if (hiscore.rule < 3) { |
| 891 | */ | 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 | } | ||
| 892 | 986 | ||
| 893 | read_lock(&dev_base_lock); | 987 | /* Rule 4: Prefer home address -- not implemented yet */ |
| 894 | read_lock(&addrconf_lock); | ||
| 895 | for (dev = dev_base; dev; dev=dev->next) { | ||
| 896 | idev = __in6_dev_get(dev); | ||
| 897 | if (idev) { | ||
| 898 | read_lock_bh(&idev->lock); | ||
| 899 | for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) { | ||
| 900 | if (ifp->scope == scope) { | ||
| 901 | if (ifp->flags&IFA_F_TENTATIVE) | ||
| 902 | continue; | ||
| 903 | #ifdef CONFIG_IPV6_PRIVACY | ||
| 904 | score = ipv6_saddr_pref(ifp, idev->cnf.use_tempaddr > 1 ? IFA_F_TEMPORARY : 0); | ||
| 905 | #else | ||
| 906 | score = ipv6_saddr_pref(ifp, 0); | ||
| 907 | #endif | ||
| 908 | if (score <= hiscore) | ||
| 909 | continue; | ||
| 910 | 988 | ||
| 911 | if (match) | 989 | /* Rule 5: Prefer outgoing interface */ |
| 912 | in6_ifa_put(match); | 990 | if (hiscore.rule < 5) { |
| 913 | match = ifp; | 991 | if (daddr_dev == NULL || |
| 914 | hiscore = score; | 992 | daddr_dev == ifa_result->idev->dev) |
| 915 | 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 | } | ||
| 916 | 1007 | ||
| 917 | if (IPV6_GET_SADDR_MAXSCORE(score)) { | 1008 | /* Rule 6: Prefer matching label */ |
| 918 | read_unlock_bh(&idev->lock); | 1009 | if (hiscore.rule < 6) { |
| 919 | goto out_unlock_base; | 1010 | if (ipv6_saddr_label(&ifa_result->addr, hiscore.addr_type) == daddr_label) |
| 920 | } | 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; | ||
| 921 | } | 1019 | } |
| 1020 | } else { | ||
| 1021 | if (hiscore.attrs & IPV6_SADDR_SCORE_LABEL) | ||
| 1022 | continue; | ||
| 922 | } | 1023 | } |
| 923 | read_unlock_bh(&idev->lock); | 1024 | |
| 1025 | #ifdef CONFIG_IPV6_PRIVACY | ||
| 1026 | /* Rule 7: Prefer public address | ||
| 1027 | * Note: prefer temprary address if use_tempaddr >= 2 | ||
| 1028 | */ | ||
| 1029 | if (hiscore.rule < 7) { | ||
| 1030 | if ((!(ifa_result->flags & IFA_F_TEMPORARY)) ^ | ||
| 1031 | (ifa_result->idev->cnf.use_tempaddr >= 2)) | ||
| 1032 | hiscore.attrs |= IPV6_SADDR_SCORE_PRIVACY; | ||
| 1033 | hiscore.rule++; | ||
| 1034 | } | ||
| 1035 | if ((!(ifa->flags & IFA_F_TEMPORARY)) ^ | ||
| 1036 | (ifa->idev->cnf.use_tempaddr >= 2)) { | ||
| 1037 | score.attrs |= IPV6_SADDR_SCORE_PRIVACY; | ||
| 1038 | if (!(hiscore.attrs & IPV6_SADDR_SCORE_PRIVACY)) { | ||
| 1039 | score.rule = 7; | ||
| 1040 | goto record_it; | ||
| 1041 | } | ||
| 1042 | } else { | ||
| 1043 | if (hiscore.attrs & IPV6_SADDR_SCORE_PRIVACY) | ||
| 1044 | continue; | ||
| 1045 | } | ||
| 1046 | #endif | ||
| 1047 | /* Rule 8: Use longest matching prefix */ | ||
| 1048 | if (hiscore.rule < 8) | ||
| 1049 | hiscore.matchlen = ipv6_addr_diff(&ifa_result->addr, daddr); | ||
| 1050 | score.rule++; | ||
| 1051 | score.matchlen = ipv6_addr_diff(&ifa->addr, daddr); | ||
| 1052 | if (score.matchlen > hiscore.matchlen) { | ||
| 1053 | score.rule = 8; | ||
| 1054 | goto record_it; | ||
| 1055 | } | ||
| 1056 | #if 0 | ||
| 1057 | else if (score.matchlen < hiscore.matchlen) | ||
| 1058 | continue; | ||
| 1059 | #endif | ||
| 1060 | |||
| 1061 | /* Final Rule: choose first available one */ | ||
| 1062 | continue; | ||
| 1063 | record_it: | ||
| 1064 | if (ifa_result) | ||
| 1065 | in6_ifa_put(ifa_result); | ||
| 1066 | in6_ifa_hold(ifa); | ||
| 1067 | ifa_result = ifa; | ||
| 1068 | hiscore = score; | ||
| 924 | } | 1069 | } |
| 1070 | read_unlock_bh(&idev->lock); | ||
| 925 | } | 1071 | } |
| 926 | |||
| 927 | out_unlock_base: | ||
| 928 | read_unlock(&addrconf_lock); | 1072 | read_unlock(&addrconf_lock); |
| 929 | read_unlock(&dev_base_lock); | 1073 | read_unlock(&dev_base_lock); |
| 930 | 1074 | ||
| 931 | out: | 1075 | if (!ifa_result) |
| 932 | err = -EADDRNOTAVAIL; | 1076 | return -EADDRNOTAVAIL; |
| 933 | if (match) { | 1077 | |
| 934 | ipv6_addr_copy(saddr, &match->addr); | 1078 | ipv6_addr_copy(saddr, &ifa_result->addr); |
| 935 | err = 0; | 1079 | in6_ifa_put(ifa_result); |
| 936 | in6_ifa_put(match); | 1080 | return 0; |
| 937 | } | ||
| 938 | |||
| 939 | return err; | ||
| 940 | } | 1081 | } |
| 941 | 1082 | ||
| 942 | 1083 | ||
| @@ -2950,8 +3091,7 @@ static int inet6_fill_ifinfo(struct sk_buff *skb, struct inet6_dev *idev, | |||
| 2950 | 3091 | ||
| 2951 | nlmsg_failure: | 3092 | nlmsg_failure: |
| 2952 | rtattr_failure: | 3093 | rtattr_failure: |
| 2953 | if (array) | 3094 | kfree(array); |
| 2954 | kfree(array); | ||
| 2955 | skb_trim(skb, b - skb->data); | 3095 | skb_trim(skb, b - skb->data); |
| 2956 | return -1; | 3096 | return -1; |
| 2957 | } | 3097 | } |
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 4fcc5a7acf6e..1bf6d9a769e6 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c | |||
| @@ -127,56 +127,6 @@ static __inline__ int addr_bit_set(void *token, int fn_bit) | |||
| 127 | return htonl(1 << ((~fn_bit)&0x1F)) & addr[fn_bit>>5]; | 127 | return htonl(1 << ((~fn_bit)&0x1F)) & addr[fn_bit>>5]; |
| 128 | } | 128 | } |
| 129 | 129 | ||
| 130 | /* | ||
| 131 | * find the first different bit between two addresses | ||
| 132 | * length of address must be a multiple of 32bits | ||
| 133 | */ | ||
| 134 | |||
| 135 | static __inline__ int addr_diff(void *token1, void *token2, int addrlen) | ||
| 136 | { | ||
| 137 | __u32 *a1 = token1; | ||
| 138 | __u32 *a2 = token2; | ||
| 139 | int i; | ||
| 140 | |||
| 141 | addrlen >>= 2; | ||
| 142 | |||
| 143 | for (i = 0; i < addrlen; i++) { | ||
| 144 | __u32 xb; | ||
| 145 | |||
| 146 | xb = a1[i] ^ a2[i]; | ||
| 147 | |||
| 148 | if (xb) { | ||
| 149 | int j = 31; | ||
| 150 | |||
| 151 | xb = ntohl(xb); | ||
| 152 | |||
| 153 | while ((xb & (1 << j)) == 0) | ||
| 154 | j--; | ||
| 155 | |||
| 156 | return (i * 32 + 31 - j); | ||
| 157 | } | ||
| 158 | } | ||
| 159 | |||
| 160 | /* | ||
| 161 | * we should *never* get to this point since that | ||
| 162 | * would mean the addrs are equal | ||
| 163 | * | ||
| 164 | * However, we do get to it 8) And exacly, when | ||
| 165 | * addresses are equal 8) | ||
| 166 | * | ||
| 167 | * ip route add 1111::/128 via ... | ||
| 168 | * ip route add 1111::/64 via ... | ||
| 169 | * and we are here. | ||
| 170 | * | ||
| 171 | * Ideally, this function should stop comparison | ||
| 172 | * at prefix length. It does not, but it is still OK, | ||
| 173 | * if returned value is greater than prefix length. | ||
| 174 | * --ANK (980803) | ||
| 175 | */ | ||
| 176 | |||
| 177 | return addrlen<<5; | ||
| 178 | } | ||
| 179 | |||
| 180 | static __inline__ struct fib6_node * node_alloc(void) | 130 | static __inline__ struct fib6_node * node_alloc(void) |
| 181 | { | 131 | { |
| 182 | struct fib6_node *fn; | 132 | struct fib6_node *fn; |
| @@ -296,11 +246,11 @@ insert_above: | |||
| 296 | 246 | ||
| 297 | /* find 1st bit in difference between the 2 addrs. | 247 | /* find 1st bit in difference between the 2 addrs. |
| 298 | 248 | ||
| 299 | See comment in addr_diff: bit may be an invalid value, | 249 | See comment in __ipv6_addr_diff: bit may be an invalid value, |
| 300 | but if it is >= plen, the value is ignored in any case. | 250 | but if it is >= plen, the value is ignored in any case. |
| 301 | */ | 251 | */ |
| 302 | 252 | ||
| 303 | bit = addr_diff(addr, &key->addr, addrlen); | 253 | bit = __ipv6_addr_diff(addr, &key->addr, addrlen); |
| 304 | 254 | ||
| 305 | /* | 255 | /* |
| 306 | * (intermediate)[in] | 256 | * (intermediate)[in] |
diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c index 6e3480426939..a6026d2787d2 100644 --- a/net/ipv6/ip6_input.c +++ b/net/ipv6/ip6_input.c | |||
| @@ -176,6 +176,11 @@ resubmit: | |||
| 176 | if (ipprot->flags & INET6_PROTO_FINAL) { | 176 | if (ipprot->flags & INET6_PROTO_FINAL) { |
| 177 | struct ipv6hdr *hdr; | 177 | struct ipv6hdr *hdr; |
| 178 | 178 | ||
| 179 | /* Free reference early: we don't need it any more, | ||
| 180 | and it may hold ip_conntrack module loaded | ||
| 181 | indefinitely. */ | ||
| 182 | nf_reset(skb); | ||
| 183 | |||
| 179 | skb_postpull_rcsum(skb, skb->nh.raw, | 184 | skb_postpull_rcsum(skb, skb->nh.raw, |
| 180 | skb->h.raw - skb->nh.raw); | 185 | skb->h.raw - skb->nh.raw); |
| 181 | hdr = skb->nh.ipv6h; | 186 | hdr = skb->nh.ipv6h; |
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 614296a920c6..c1fa693511a1 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c | |||
| @@ -441,9 +441,15 @@ static void ip6_copy_metadata(struct sk_buff *to, struct sk_buff *from) | |||
| 441 | #ifdef CONFIG_NETFILTER | 441 | #ifdef CONFIG_NETFILTER |
| 442 | to->nfmark = from->nfmark; | 442 | to->nfmark = from->nfmark; |
| 443 | /* Connection association is same as pre-frag packet */ | 443 | /* Connection association is same as pre-frag packet */ |
| 444 | nf_conntrack_put(to->nfct); | ||
| 444 | to->nfct = from->nfct; | 445 | to->nfct = from->nfct; |
| 445 | nf_conntrack_get(to->nfct); | 446 | nf_conntrack_get(to->nfct); |
| 446 | to->nfctinfo = from->nfctinfo; | 447 | to->nfctinfo = from->nfctinfo; |
| 448 | #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) | ||
| 449 | nf_conntrack_put_reasm(to->nfct_reasm); | ||
| 450 | to->nfct_reasm = from->nfct_reasm; | ||
| 451 | nf_conntrack_get_reasm(to->nfct_reasm); | ||
| 452 | #endif | ||
| 447 | #ifdef CONFIG_BRIDGE_NETFILTER | 453 | #ifdef CONFIG_BRIDGE_NETFILTER |
| 448 | nf_bridge_put(to->nf_bridge); | 454 | nf_bridge_put(to->nf_bridge); |
| 449 | to->nf_bridge = from->nf_bridge; | 455 | to->nf_bridge = from->nf_bridge; |
| @@ -587,8 +593,7 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) | |||
| 587 | skb->next = NULL; | 593 | skb->next = NULL; |
| 588 | } | 594 | } |
| 589 | 595 | ||
| 590 | if (tmp_hdr) | 596 | kfree(tmp_hdr); |
| 591 | kfree(tmp_hdr); | ||
| 592 | 597 | ||
| 593 | if (err == 0) { | 598 | if (err == 0) { |
| 594 | IP6_INC_STATS(IPSTATS_MIB_FRAGOKS); | 599 | IP6_INC_STATS(IPSTATS_MIB_FRAGOKS); |
| @@ -1186,10 +1191,8 @@ int ip6_push_pending_frames(struct sock *sk) | |||
| 1186 | 1191 | ||
| 1187 | out: | 1192 | out: |
| 1188 | inet->cork.flags &= ~IPCORK_OPT; | 1193 | inet->cork.flags &= ~IPCORK_OPT; |
| 1189 | if (np->cork.opt) { | 1194 | kfree(np->cork.opt); |
| 1190 | kfree(np->cork.opt); | 1195 | np->cork.opt = NULL; |
| 1191 | np->cork.opt = NULL; | ||
| 1192 | } | ||
| 1193 | if (np->cork.rt) { | 1196 | if (np->cork.rt) { |
| 1194 | dst_release(&np->cork.rt->u.dst); | 1197 | dst_release(&np->cork.rt->u.dst); |
| 1195 | np->cork.rt = NULL; | 1198 | np->cork.rt = NULL; |
| @@ -1214,10 +1217,8 @@ void ip6_flush_pending_frames(struct sock *sk) | |||
| 1214 | 1217 | ||
| 1215 | inet->cork.flags &= ~IPCORK_OPT; | 1218 | inet->cork.flags &= ~IPCORK_OPT; |
| 1216 | 1219 | ||
| 1217 | if (np->cork.opt) { | 1220 | kfree(np->cork.opt); |
| 1218 | kfree(np->cork.opt); | 1221 | np->cork.opt = NULL; |
| 1219 | np->cork.opt = NULL; | ||
| 1220 | } | ||
| 1221 | if (np->cork.rt) { | 1222 | if (np->cork.rt) { |
| 1222 | dst_release(&np->cork.rt->u.dst); | 1223 | dst_release(&np->cork.rt->u.dst); |
| 1223 | np->cork.rt = NULL; | 1224 | np->cork.rt = NULL; |
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index cf94372d1af3..e315d0f80af1 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c | |||
| @@ -525,6 +525,7 @@ ip6ip6_rcv(struct sk_buff **pskb, unsigned int *nhoffp) | |||
| 525 | 525 | ||
| 526 | if ((t = ip6ip6_tnl_lookup(&ipv6h->saddr, &ipv6h->daddr)) != NULL) { | 526 | if ((t = ip6ip6_tnl_lookup(&ipv6h->saddr, &ipv6h->daddr)) != NULL) { |
| 527 | if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) { | 527 | if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) { |
| 528 | read_unlock(&ip6ip6_lock); | ||
| 528 | kfree_skb(skb); | 529 | kfree_skb(skb); |
| 529 | return 0; | 530 | return 0; |
| 530 | } | 531 | } |
| @@ -756,8 +757,7 @@ ip6ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) | |||
| 756 | } | 757 | } |
| 757 | ip6_tnl_dst_store(t, dst); | 758 | ip6_tnl_dst_store(t, dst); |
| 758 | 759 | ||
| 759 | if (opt) | 760 | kfree(opt); |
| 760 | kfree(opt); | ||
| 761 | 761 | ||
| 762 | t->recursion--; | 762 | t->recursion--; |
| 763 | return 0; | 763 | return 0; |
| @@ -766,8 +766,7 @@ tx_err_link_failure: | |||
| 766 | dst_link_failure(skb); | 766 | dst_link_failure(skb); |
| 767 | tx_err_dst_release: | 767 | tx_err_dst_release: |
| 768 | dst_release(dst); | 768 | dst_release(dst); |
| 769 | if (opt) | 769 | kfree(opt); |
| 770 | kfree(opt); | ||
| 771 | tx_err: | 770 | tx_err: |
| 772 | stats->tx_errors++; | 771 | stats->tx_errors++; |
| 773 | stats->tx_dropped++; | 772 | stats->tx_dropped++; |
diff --git a/net/ipv6/ipcomp6.c b/net/ipv6/ipcomp6.c index 85bfbc69b2c3..55917fb17094 100644 --- a/net/ipv6/ipcomp6.c +++ b/net/ipv6/ipcomp6.c | |||
| @@ -130,8 +130,7 @@ static int ipcomp6_input(struct xfrm_state *x, struct xfrm_decap_state *decap, s | |||
| 130 | out_put_cpu: | 130 | out_put_cpu: |
| 131 | put_cpu(); | 131 | put_cpu(); |
| 132 | out: | 132 | out: |
| 133 | if (tmp_hdr) | 133 | kfree(tmp_hdr); |
| 134 | kfree(tmp_hdr); | ||
| 135 | if (err) | 134 | if (err) |
| 136 | goto error_out; | 135 | goto error_out; |
| 137 | return nexthdr; | 136 | return nexthdr; |
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index 8567873d0dd8..003fd99ff597 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c | |||
| @@ -80,8 +80,7 @@ int ip6_ra_control(struct sock *sk, int sel, void (*destructor)(struct sock *)) | |||
| 80 | if (ra->sk == sk) { | 80 | if (ra->sk == sk) { |
| 81 | if (sel>=0) { | 81 | if (sel>=0) { |
| 82 | write_unlock_bh(&ip6_ra_lock); | 82 | write_unlock_bh(&ip6_ra_lock); |
| 83 | if (new_ra) | 83 | kfree(new_ra); |
| 84 | kfree(new_ra); | ||
| 85 | return -EADDRINUSE; | 84 | return -EADDRINUSE; |
| 86 | } | 85 | } |
| 87 | 86 | ||
diff --git a/net/ipv6/ipv6_syms.c b/net/ipv6/ipv6_syms.c index 37a4a99c9fe9..16482785bdfd 100644 --- a/net/ipv6/ipv6_syms.c +++ b/net/ipv6/ipv6_syms.c | |||
| @@ -7,7 +7,7 @@ | |||
| 7 | #include <net/ip6_route.h> | 7 | #include <net/ip6_route.h> |
| 8 | #include <net/xfrm.h> | 8 | #include <net/xfrm.h> |
| 9 | 9 | ||
| 10 | EXPORT_SYMBOL(ipv6_addr_type); | 10 | EXPORT_SYMBOL(__ipv6_addr_type); |
| 11 | EXPORT_SYMBOL(icmpv6_send); | 11 | EXPORT_SYMBOL(icmpv6_send); |
| 12 | EXPORT_SYMBOL(icmpv6_statistics); | 12 | EXPORT_SYMBOL(icmpv6_statistics); |
| 13 | EXPORT_SYMBOL(icmpv6_err_convert); | 13 | EXPORT_SYMBOL(icmpv6_err_convert); |
diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig index bb7ccfe33f23..971ba60bf6e9 100644 --- a/net/ipv6/netfilter/Kconfig +++ b/net/ipv6/netfilter/Kconfig | |||
| @@ -278,5 +278,19 @@ config IP6_NF_RAW | |||
| 278 | If you want to compile it as a module, say M here and read | 278 | If you want to compile it as a module, say M here and read |
| 279 | <file:Documentation/modules.txt>. If unsure, say `N'. | 279 | <file:Documentation/modules.txt>. If unsure, say `N'. |
| 280 | 280 | ||
| 281 | config NF_CONNTRACK_IPV6 | ||
| 282 | tristate "IPv6 support for new connection tracking (EXPERIMENTAL)" | ||
| 283 | depends on EXPERIMENTAL && NF_CONNTRACK | ||
| 284 | ---help--- | ||
| 285 | Connection tracking keeps a record of what packets have passed | ||
| 286 | through your machine, in order to figure out how they are related | ||
| 287 | into connections. | ||
| 288 | |||
| 289 | This is IPv6 support on Layer 3 independent connection tracking. | ||
| 290 | Layer 3 independent connection tracking is experimental scheme | ||
| 291 | which generalize ip_conntrack to support other layer 3 protocols. | ||
| 292 | |||
| 293 | To compile it as a module, choose M here. If unsure, say N. | ||
| 294 | |||
| 281 | endmenu | 295 | endmenu |
| 282 | 296 | ||
diff --git a/net/ipv6/netfilter/Makefile b/net/ipv6/netfilter/Makefile index 2b2c370e8b1c..9ab5b2ca1f59 100644 --- a/net/ipv6/netfilter/Makefile +++ b/net/ipv6/netfilter/Makefile | |||
| @@ -27,3 +27,9 @@ obj-$(CONFIG_IP6_NF_TARGET_LOG) += ip6t_LOG.o | |||
| 27 | obj-$(CONFIG_IP6_NF_RAW) += ip6table_raw.o | 27 | obj-$(CONFIG_IP6_NF_RAW) += ip6table_raw.o |
| 28 | obj-$(CONFIG_IP6_NF_MATCH_HL) += ip6t_hl.o | 28 | obj-$(CONFIG_IP6_NF_MATCH_HL) += ip6t_hl.o |
| 29 | obj-$(CONFIG_IP6_NF_TARGET_REJECT) += ip6t_REJECT.o | 29 | obj-$(CONFIG_IP6_NF_TARGET_REJECT) += ip6t_REJECT.o |
| 30 | |||
| 31 | # objects for l3 independent conntrack | ||
| 32 | nf_conntrack_ipv6-objs := nf_conntrack_l3proto_ipv6.o nf_conntrack_proto_icmpv6.o nf_conntrack_reasm.o | ||
| 33 | |||
| 34 | # l3 independent conntrack | ||
| 35 | obj-$(CONFIG_NF_CONNTRACK_IPV6) += nf_conntrack_ipv6.o | ||
diff --git a/net/ipv6/netfilter/ip6t_MARK.c b/net/ipv6/netfilter/ip6t_MARK.c index 0c7584f92172..eab8fb864ee0 100644 --- a/net/ipv6/netfilter/ip6t_MARK.c +++ b/net/ipv6/netfilter/ip6t_MARK.c | |||
| @@ -56,9 +56,9 @@ checkentry(const char *tablename, | |||
| 56 | return 1; | 56 | return 1; |
| 57 | } | 57 | } |
| 58 | 58 | ||
| 59 | static struct ip6t_target ip6t_mark_reg = { | 59 | static struct ip6t_target ip6t_mark_reg = { |
| 60 | .name = "MARK", | 60 | .name = "MARK", |
| 61 | .target = target, | 61 | .target = target, |
| 62 | .checkentry = checkentry, | 62 | .checkentry = checkentry, |
| 63 | .me = THIS_MODULE | 63 | .me = THIS_MODULE |
| 64 | }; | 64 | }; |
diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c new file mode 100644 index 000000000000..e2c90b3a8074 --- /dev/null +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | |||
| @@ -0,0 +1,556 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C)2004 USAGI/WIDE Project | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * Author: | ||
| 9 | * Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp> | ||
| 10 | * | ||
| 11 | * 16 Dec 2003: Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp> | ||
| 12 | * - support Layer 3 protocol independent connection tracking. | ||
| 13 | * Based on the original ip_conntrack code which had the following | ||
| 14 | * copyright information: | ||
| 15 | * (C) 1999-2001 Paul `Rusty' Russell | ||
| 16 | * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org> | ||
| 17 | * | ||
| 18 | * 23 Mar 2004: Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp> | ||
| 19 | * - add get_features() to support various size of conntrack | ||
| 20 | * structures. | ||
| 21 | */ | ||
| 22 | |||
| 23 | #include <linux/config.h> | ||
| 24 | #include <linux/types.h> | ||
| 25 | #include <linux/ipv6.h> | ||
| 26 | #include <linux/in6.h> | ||
| 27 | #include <linux/netfilter.h> | ||
| 28 | #include <linux/module.h> | ||
| 29 | #include <linux/skbuff.h> | ||
| 30 | #include <linux/icmp.h> | ||
| 31 | #include <linux/sysctl.h> | ||
| 32 | #include <net/ipv6.h> | ||
| 33 | |||
| 34 | #include <linux/netfilter_ipv6.h> | ||
| 35 | #include <net/netfilter/nf_conntrack.h> | ||
| 36 | #include <net/netfilter/nf_conntrack_helper.h> | ||
| 37 | #include <net/netfilter/nf_conntrack_protocol.h> | ||
| 38 | #include <net/netfilter/nf_conntrack_l3proto.h> | ||
| 39 | #include <net/netfilter/nf_conntrack_core.h> | ||
| 40 | |||
| 41 | #if 0 | ||
| 42 | #define DEBUGP printk | ||
| 43 | #else | ||
| 44 | #define DEBUGP(format, args...) | ||
| 45 | #endif | ||
| 46 | |||
| 47 | DECLARE_PER_CPU(struct ip_conntrack_stat, nf_conntrack_stat); | ||
| 48 | |||
| 49 | static int ipv6_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff, | ||
| 50 | struct nf_conntrack_tuple *tuple) | ||
| 51 | { | ||
| 52 | u_int32_t _addrs[8], *ap; | ||
| 53 | |||
| 54 | ap = skb_header_pointer(skb, nhoff + offsetof(struct ipv6hdr, saddr), | ||
| 55 | sizeof(_addrs), _addrs); | ||
| 56 | if (ap == NULL) | ||
| 57 | return 0; | ||
| 58 | |||
| 59 | memcpy(tuple->src.u3.ip6, ap, sizeof(tuple->src.u3.ip6)); | ||
| 60 | memcpy(tuple->dst.u3.ip6, ap + 4, sizeof(tuple->dst.u3.ip6)); | ||
| 61 | |||
| 62 | return 1; | ||
| 63 | } | ||
| 64 | |||
| 65 | static int ipv6_invert_tuple(struct nf_conntrack_tuple *tuple, | ||
| 66 | const struct nf_conntrack_tuple *orig) | ||
| 67 | { | ||
| 68 | memcpy(tuple->src.u3.ip6, orig->dst.u3.ip6, sizeof(tuple->src.u3.ip6)); | ||
| 69 | memcpy(tuple->dst.u3.ip6, orig->src.u3.ip6, sizeof(tuple->dst.u3.ip6)); | ||
| 70 | |||
| 71 | return 1; | ||
| 72 | } | ||
| 73 | |||
| 74 | static int ipv6_print_tuple(struct seq_file *s, | ||
| 75 | const struct nf_conntrack_tuple *tuple) | ||
| 76 | { | ||
| 77 | return seq_printf(s, "src=%x:%x:%x:%x:%x:%x:%x:%x dst=%x:%x:%x:%x:%x:%x:%x:%x ", | ||
| 78 | NIP6(*((struct in6_addr *)tuple->src.u3.ip6)), | ||
| 79 | NIP6(*((struct in6_addr *)tuple->dst.u3.ip6))); | ||
| 80 | } | ||
| 81 | |||
| 82 | static int ipv6_print_conntrack(struct seq_file *s, | ||
| 83 | const struct nf_conn *conntrack) | ||
| 84 | { | ||
| 85 | return 0; | ||
| 86 | } | ||
| 87 | |||
| 88 | /* | ||
| 89 | * Based on ipv6_skip_exthdr() in net/ipv6/exthdr.c | ||
| 90 | * | ||
| 91 | * This function parses (probably truncated) exthdr set "hdr" | ||
| 92 | * of length "len". "nexthdrp" initially points to some place, | ||
| 93 | * where type of the first header can be found. | ||
| 94 | * | ||
| 95 | * It skips all well-known exthdrs, and returns pointer to the start | ||
| 96 | * of unparsable area i.e. the first header with unknown type. | ||
| 97 | * if success, *nexthdr is updated by type/protocol of this header. | ||
| 98 | * | ||
| 99 | * NOTES: - it may return pointer pointing beyond end of packet, | ||
| 100 | * if the last recognized header is truncated in the middle. | ||
| 101 | * - if packet is truncated, so that all parsed headers are skipped, | ||
| 102 | * it returns -1. | ||
| 103 | * - if packet is fragmented, return pointer of the fragment header. | ||
| 104 | * - ESP is unparsable for now and considered like | ||
| 105 | * normal payload protocol. | ||
| 106 | * - Note also special handling of AUTH header. Thanks to IPsec wizards. | ||
| 107 | */ | ||
| 108 | |||
| 109 | int nf_ct_ipv6_skip_exthdr(struct sk_buff *skb, int start, u8 *nexthdrp, | ||
| 110 | int len) | ||
| 111 | { | ||
| 112 | u8 nexthdr = *nexthdrp; | ||
| 113 | |||
| 114 | while (ipv6_ext_hdr(nexthdr)) { | ||
| 115 | struct ipv6_opt_hdr hdr; | ||
| 116 | int hdrlen; | ||
| 117 | |||
| 118 | if (len < (int)sizeof(struct ipv6_opt_hdr)) | ||
| 119 | return -1; | ||
| 120 | if (nexthdr == NEXTHDR_NONE) | ||
| 121 | break; | ||
| 122 | if (nexthdr == NEXTHDR_FRAGMENT) | ||
| 123 | break; | ||
| 124 | if (skb_copy_bits(skb, start, &hdr, sizeof(hdr))) | ||
| 125 | BUG(); | ||
| 126 | if (nexthdr == NEXTHDR_AUTH) | ||
| 127 | hdrlen = (hdr.hdrlen+2)<<2; | ||
| 128 | else | ||
| 129 | hdrlen = ipv6_optlen(&hdr); | ||
| 130 | |||
| 131 | nexthdr = hdr.nexthdr; | ||
| 132 | len -= hdrlen; | ||
| 133 | start += hdrlen; | ||
| 134 | } | ||
| 135 | |||
| 136 | *nexthdrp = nexthdr; | ||
| 137 | return start; | ||
| 138 | } | ||
| 139 | |||
| 140 | static int | ||
| 141 | ipv6_prepare(struct sk_buff **pskb, unsigned int hooknum, unsigned int *dataoff, | ||
| 142 | u_int8_t *protonum) | ||
| 143 | { | ||
| 144 | unsigned int extoff; | ||
| 145 | unsigned char pnum; | ||
| 146 | int protoff; | ||
| 147 | |||
| 148 | extoff = (u8*)((*pskb)->nh.ipv6h + 1) - (*pskb)->data; | ||
| 149 | pnum = (*pskb)->nh.ipv6h->nexthdr; | ||
| 150 | |||
| 151 | protoff = nf_ct_ipv6_skip_exthdr(*pskb, extoff, &pnum, | ||
| 152 | (*pskb)->len - extoff); | ||
| 153 | |||
| 154 | /* | ||
| 155 | * (protoff == (*pskb)->len) mean that the packet doesn't have no data | ||
| 156 | * except of IPv6 & ext headers. but it's tracked anyway. - YK | ||
| 157 | */ | ||
| 158 | if ((protoff < 0) || (protoff > (*pskb)->len)) { | ||
| 159 | DEBUGP("ip6_conntrack_core: can't find proto in pkt\n"); | ||
| 160 | NF_CT_STAT_INC(error); | ||
| 161 | NF_CT_STAT_INC(invalid); | ||
| 162 | return -NF_ACCEPT; | ||
| 163 | } | ||
| 164 | |||
| 165 | *dataoff = protoff; | ||
| 166 | *protonum = pnum; | ||
| 167 | return NF_ACCEPT; | ||
| 168 | } | ||
| 169 | |||
| 170 | static u_int32_t ipv6_get_features(const struct nf_conntrack_tuple *tuple) | ||
| 171 | { | ||
| 172 | return NF_CT_F_BASIC; | ||
| 173 | } | ||
| 174 | |||
| 175 | static unsigned int ipv6_confirm(unsigned int hooknum, | ||
| 176 | struct sk_buff **pskb, | ||
| 177 | const struct net_device *in, | ||
| 178 | const struct net_device *out, | ||
| 179 | int (*okfn)(struct sk_buff *)) | ||
| 180 | { | ||
| 181 | struct nf_conn *ct; | ||
| 182 | enum ip_conntrack_info ctinfo; | ||
| 183 | |||
| 184 | /* This is where we call the helper: as the packet goes out. */ | ||
| 185 | ct = nf_ct_get(*pskb, &ctinfo); | ||
| 186 | if (ct && ct->helper) { | ||
| 187 | unsigned int ret, protoff; | ||
| 188 | unsigned int extoff = (u8*)((*pskb)->nh.ipv6h + 1) | ||
| 189 | - (*pskb)->data; | ||
| 190 | unsigned char pnum = (*pskb)->nh.ipv6h->nexthdr; | ||
| 191 | |||
| 192 | protoff = nf_ct_ipv6_skip_exthdr(*pskb, extoff, &pnum, | ||
| 193 | (*pskb)->len - extoff); | ||
| 194 | if (protoff < 0 || protoff > (*pskb)->len || | ||
| 195 | pnum == NEXTHDR_FRAGMENT) { | ||
| 196 | DEBUGP("proto header not found\n"); | ||
| 197 | return NF_ACCEPT; | ||
| 198 | } | ||
| 199 | |||
| 200 | ret = ct->helper->help(pskb, protoff, ct, ctinfo); | ||
| 201 | if (ret != NF_ACCEPT) | ||
| 202 | return ret; | ||
| 203 | } | ||
| 204 | |||
| 205 | /* We've seen it coming out the other side: confirm it */ | ||
| 206 | |||
| 207 | return nf_conntrack_confirm(pskb); | ||
| 208 | } | ||
| 209 | |||
| 210 | extern struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb); | ||
| 211 | extern void nf_ct_frag6_output(unsigned int hooknum, struct sk_buff *skb, | ||
| 212 | struct net_device *in, | ||
| 213 | struct net_device *out, | ||
| 214 | int (*okfn)(struct sk_buff *)); | ||
| 215 | static unsigned int ipv6_defrag(unsigned int hooknum, | ||
| 216 | struct sk_buff **pskb, | ||
| 217 | const struct net_device *in, | ||
| 218 | const struct net_device *out, | ||
| 219 | int (*okfn)(struct sk_buff *)) | ||
| 220 | { | ||
| 221 | struct sk_buff *reasm; | ||
| 222 | |||
| 223 | /* Previously seen (loopback)? */ | ||
| 224 | if ((*pskb)->nfct) | ||
| 225 | return NF_ACCEPT; | ||
| 226 | |||
| 227 | reasm = nf_ct_frag6_gather(*pskb); | ||
| 228 | |||
| 229 | /* queued */ | ||
| 230 | if (reasm == NULL) | ||
| 231 | return NF_STOLEN; | ||
| 232 | |||
| 233 | /* error occured or not fragmented */ | ||
| 234 | if (reasm == *pskb) | ||
| 235 | return NF_ACCEPT; | ||
| 236 | |||
| 237 | nf_ct_frag6_output(hooknum, reasm, (struct net_device *)in, | ||
| 238 | (struct net_device *)out, okfn); | ||
| 239 | |||
| 240 | return NF_STOLEN; | ||
| 241 | } | ||
| 242 | |||
| 243 | static unsigned int ipv6_conntrack_in(unsigned int hooknum, | ||
| 244 | struct sk_buff **pskb, | ||
| 245 | const struct net_device *in, | ||
| 246 | const struct net_device *out, | ||
| 247 | int (*okfn)(struct sk_buff *)) | ||
| 248 | { | ||
| 249 | struct sk_buff *reasm = (*pskb)->nfct_reasm; | ||
| 250 | |||
| 251 | /* This packet is fragmented and has reassembled packet. */ | ||
| 252 | if (reasm) { | ||
| 253 | /* Reassembled packet isn't parsed yet ? */ | ||
| 254 | if (!reasm->nfct) { | ||
| 255 | unsigned int ret; | ||
| 256 | |||
| 257 | ret = nf_conntrack_in(PF_INET6, hooknum, &reasm); | ||
| 258 | if (ret != NF_ACCEPT) | ||
| 259 | return ret; | ||
| 260 | } | ||
| 261 | nf_conntrack_get(reasm->nfct); | ||
| 262 | (*pskb)->nfct = reasm->nfct; | ||
| 263 | return NF_ACCEPT; | ||
| 264 | } | ||
| 265 | |||
| 266 | return nf_conntrack_in(PF_INET6, hooknum, pskb); | ||
| 267 | } | ||
| 268 | |||
| 269 | static unsigned int ipv6_conntrack_local(unsigned int hooknum, | ||
| 270 | struct sk_buff **pskb, | ||
| 271 | const struct net_device *in, | ||
| 272 | const struct net_device *out, | ||
| 273 | int (*okfn)(struct sk_buff *)) | ||
| 274 | { | ||
| 275 | /* root is playing with raw sockets. */ | ||
| 276 | if ((*pskb)->len < sizeof(struct ipv6hdr)) { | ||
| 277 | if (net_ratelimit()) | ||
| 278 | printk("ipv6_conntrack_local: packet too short\n"); | ||
| 279 | return NF_ACCEPT; | ||
| 280 | } | ||
| 281 | return ipv6_conntrack_in(hooknum, pskb, in, out, okfn); | ||
| 282 | } | ||
| 283 | |||
| 284 | /* Connection tracking may drop packets, but never alters them, so | ||
| 285 | make it the first hook. */ | ||
| 286 | static struct nf_hook_ops ipv6_conntrack_defrag_ops = { | ||
| 287 | .hook = ipv6_defrag, | ||
| 288 | .owner = THIS_MODULE, | ||
| 289 | .pf = PF_INET6, | ||
| 290 | .hooknum = NF_IP6_PRE_ROUTING, | ||
| 291 | .priority = NF_IP6_PRI_CONNTRACK_DEFRAG, | ||
| 292 | }; | ||
| 293 | |||
| 294 | static struct nf_hook_ops ipv6_conntrack_in_ops = { | ||
| 295 | .hook = ipv6_conntrack_in, | ||
| 296 | .owner = THIS_MODULE, | ||
| 297 | .pf = PF_INET6, | ||
| 298 | .hooknum = NF_IP6_PRE_ROUTING, | ||
| 299 | .priority = NF_IP6_PRI_CONNTRACK, | ||
| 300 | }; | ||
| 301 | |||
| 302 | static struct nf_hook_ops ipv6_conntrack_local_out_ops = { | ||
| 303 | .hook = ipv6_conntrack_local, | ||
| 304 | .owner = THIS_MODULE, | ||
| 305 | .pf = PF_INET6, | ||
| 306 | .hooknum = NF_IP6_LOCAL_OUT, | ||
| 307 | .priority = NF_IP6_PRI_CONNTRACK, | ||
| 308 | }; | ||
| 309 | |||
| 310 | static struct nf_hook_ops ipv6_conntrack_defrag_local_out_ops = { | ||
| 311 | .hook = ipv6_defrag, | ||
| 312 | .owner = THIS_MODULE, | ||
| 313 | .pf = PF_INET6, | ||
| 314 | .hooknum = NF_IP6_LOCAL_OUT, | ||
| 315 | .priority = NF_IP6_PRI_CONNTRACK_DEFRAG, | ||
| 316 | }; | ||
| 317 | |||
| 318 | /* Refragmenter; last chance. */ | ||
| 319 | static struct nf_hook_ops ipv6_conntrack_out_ops = { | ||
| 320 | .hook = ipv6_confirm, | ||
| 321 | .owner = THIS_MODULE, | ||
| 322 | .pf = PF_INET6, | ||
| 323 | .hooknum = NF_IP6_POST_ROUTING, | ||
| 324 | .priority = NF_IP6_PRI_LAST, | ||
| 325 | }; | ||
| 326 | |||
| 327 | static struct nf_hook_ops ipv6_conntrack_local_in_ops = { | ||
| 328 | .hook = ipv6_confirm, | ||
| 329 | .owner = THIS_MODULE, | ||
| 330 | .pf = PF_INET6, | ||
| 331 | .hooknum = NF_IP6_LOCAL_IN, | ||
| 332 | .priority = NF_IP6_PRI_LAST-1, | ||
| 333 | }; | ||
| 334 | |||
| 335 | #ifdef CONFIG_SYSCTL | ||
| 336 | |||
| 337 | /* From nf_conntrack_proto_icmpv6.c */ | ||
| 338 | extern unsigned long nf_ct_icmpv6_timeout; | ||
| 339 | |||
| 340 | /* From nf_conntrack_frag6.c */ | ||
| 341 | extern unsigned long nf_ct_frag6_timeout; | ||
| 342 | extern unsigned long nf_ct_frag6_low_thresh; | ||
| 343 | extern unsigned long nf_ct_frag6_high_thresh; | ||
| 344 | |||
| 345 | static struct ctl_table_header *nf_ct_ipv6_sysctl_header; | ||
| 346 | |||
| 347 | static ctl_table nf_ct_sysctl_table[] = { | ||
| 348 | { | ||
| 349 | .ctl_name = NET_NF_CONNTRACK_ICMPV6_TIMEOUT, | ||
| 350 | .procname = "nf_conntrack_icmpv6_timeout", | ||
| 351 | .data = &nf_ct_icmpv6_timeout, | ||
| 352 | .maxlen = sizeof(unsigned int), | ||
| 353 | .mode = 0644, | ||
| 354 | .proc_handler = &proc_dointvec_jiffies, | ||
| 355 | }, | ||
| 356 | { | ||
| 357 | .ctl_name = NET_NF_CONNTRACK_FRAG6_TIMEOUT, | ||
| 358 | .procname = "nf_conntrack_frag6_timeout", | ||
| 359 | .data = &nf_ct_frag6_timeout, | ||
| 360 | .maxlen = sizeof(unsigned int), | ||
| 361 | .mode = 0644, | ||
| 362 | .proc_handler = &proc_dointvec_jiffies, | ||
| 363 | }, | ||
| 364 | { | ||
| 365 | .ctl_name = NET_NF_CONNTRACK_FRAG6_LOW_THRESH, | ||
| 366 | .procname = "nf_conntrack_frag6_low_thresh", | ||
| 367 | .data = &nf_ct_frag6_low_thresh, | ||
| 368 | .maxlen = sizeof(unsigned int), | ||
| 369 | .mode = 0644, | ||
| 370 | .proc_handler = &proc_dointvec_jiffies, | ||
| 371 | }, | ||
| 372 | { | ||
| 373 | .ctl_name = NET_NF_CONNTRACK_FRAG6_HIGH_THRESH, | ||
| 374 | .procname = "nf_conntrack_frag6_high_thresh", | ||
| 375 | .data = &nf_ct_frag6_high_thresh, | ||
| 376 | .maxlen = sizeof(unsigned int), | ||
| 377 | .mode = 0644, | ||
| 378 | .proc_handler = &proc_dointvec_jiffies, | ||
| 379 | }, | ||
| 380 | { .ctl_name = 0 } | ||
| 381 | }; | ||
| 382 | |||
| 383 | static ctl_table nf_ct_netfilter_table[] = { | ||
| 384 | { | ||
| 385 | .ctl_name = NET_NETFILTER, | ||
| 386 | .procname = "netfilter", | ||
| 387 | .mode = 0555, | ||
| 388 | .child = nf_ct_sysctl_table, | ||
| 389 | }, | ||
| 390 | { .ctl_name = 0 } | ||
| 391 | }; | ||
| 392 | |||
| 393 | static ctl_table nf_ct_net_table[] = { | ||
| 394 | { | ||
| 395 | .ctl_name = CTL_NET, | ||
| 396 | .procname = "net", | ||
| 397 | .mode = 0555, | ||
| 398 | .child = nf_ct_netfilter_table, | ||
| 399 | }, | ||
| 400 | { .ctl_name = 0 } | ||
| 401 | }; | ||
| 402 | #endif | ||
| 403 | |||
| 404 | struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6 = { | ||
| 405 | .l3proto = PF_INET6, | ||
| 406 | .name = "ipv6", | ||
| 407 | .pkt_to_tuple = ipv6_pkt_to_tuple, | ||
| 408 | .invert_tuple = ipv6_invert_tuple, | ||
| 409 | .print_tuple = ipv6_print_tuple, | ||
| 410 | .print_conntrack = ipv6_print_conntrack, | ||
| 411 | .prepare = ipv6_prepare, | ||
| 412 | .get_features = ipv6_get_features, | ||
| 413 | .me = THIS_MODULE, | ||
| 414 | }; | ||
| 415 | |||
| 416 | extern struct nf_conntrack_protocol nf_conntrack_protocol_tcp6; | ||
| 417 | extern struct nf_conntrack_protocol nf_conntrack_protocol_udp6; | ||
| 418 | extern struct nf_conntrack_protocol nf_conntrack_protocol_icmpv6; | ||
| 419 | extern int nf_ct_frag6_init(void); | ||
| 420 | extern void nf_ct_frag6_cleanup(void); | ||
| 421 | static int init_or_cleanup(int init) | ||
| 422 | { | ||
| 423 | int ret = 0; | ||
| 424 | |||
| 425 | if (!init) goto cleanup; | ||
| 426 | |||
| 427 | ret = nf_ct_frag6_init(); | ||
| 428 | if (ret < 0) { | ||
| 429 | printk("nf_conntrack_ipv6: can't initialize frag6.\n"); | ||
| 430 | goto cleanup_nothing; | ||
| 431 | } | ||
| 432 | ret = nf_conntrack_protocol_register(&nf_conntrack_protocol_tcp6); | ||
| 433 | if (ret < 0) { | ||
| 434 | printk("nf_conntrack_ipv6: can't register tcp.\n"); | ||
| 435 | goto cleanup_frag6; | ||
| 436 | } | ||
| 437 | |||
| 438 | ret = nf_conntrack_protocol_register(&nf_conntrack_protocol_udp6); | ||
| 439 | if (ret < 0) { | ||
| 440 | printk("nf_conntrack_ipv6: can't register udp.\n"); | ||
| 441 | goto cleanup_tcp; | ||
| 442 | } | ||
| 443 | |||
| 444 | ret = nf_conntrack_protocol_register(&nf_conntrack_protocol_icmpv6); | ||
| 445 | if (ret < 0) { | ||
| 446 | printk("nf_conntrack_ipv6: can't register icmpv6.\n"); | ||
| 447 | goto cleanup_udp; | ||
| 448 | } | ||
| 449 | |||
| 450 | ret = nf_conntrack_l3proto_register(&nf_conntrack_l3proto_ipv6); | ||
| 451 | if (ret < 0) { | ||
| 452 | printk("nf_conntrack_ipv6: can't register ipv6\n"); | ||
| 453 | goto cleanup_icmpv6; | ||
| 454 | } | ||
| 455 | |||
| 456 | ret = nf_register_hook(&ipv6_conntrack_defrag_ops); | ||
| 457 | if (ret < 0) { | ||
| 458 | printk("nf_conntrack_ipv6: can't register pre-routing defrag " | ||
| 459 | "hook.\n"); | ||
| 460 | goto cleanup_ipv6; | ||
| 461 | } | ||
| 462 | |||
| 463 | ret = nf_register_hook(&ipv6_conntrack_defrag_local_out_ops); | ||
| 464 | if (ret < 0) { | ||
| 465 | printk("nf_conntrack_ipv6: can't register local_out defrag " | ||
| 466 | "hook.\n"); | ||
| 467 | goto cleanup_defragops; | ||
| 468 | } | ||
| 469 | |||
| 470 | ret = nf_register_hook(&ipv6_conntrack_in_ops); | ||
| 471 | if (ret < 0) { | ||
| 472 | printk("nf_conntrack_ipv6: can't register pre-routing hook.\n"); | ||
| 473 | goto cleanup_defraglocalops; | ||
| 474 | } | ||
| 475 | |||
| 476 | ret = nf_register_hook(&ipv6_conntrack_local_out_ops); | ||
| 477 | if (ret < 0) { | ||
| 478 | printk("nf_conntrack_ipv6: can't register local out hook.\n"); | ||
| 479 | goto cleanup_inops; | ||
| 480 | } | ||
| 481 | |||
| 482 | ret = nf_register_hook(&ipv6_conntrack_out_ops); | ||
| 483 | if (ret < 0) { | ||
| 484 | printk("nf_conntrack_ipv6: can't register post-routing hook.\n"); | ||
| 485 | goto cleanup_inandlocalops; | ||
| 486 | } | ||
| 487 | |||
| 488 | ret = nf_register_hook(&ipv6_conntrack_local_in_ops); | ||
| 489 | if (ret < 0) { | ||
| 490 | printk("nf_conntrack_ipv6: can't register local in hook.\n"); | ||
| 491 | goto cleanup_inoutandlocalops; | ||
| 492 | } | ||
| 493 | |||
| 494 | #ifdef CONFIG_SYSCTL | ||
| 495 | nf_ct_ipv6_sysctl_header = register_sysctl_table(nf_ct_net_table, 0); | ||
| 496 | if (nf_ct_ipv6_sysctl_header == NULL) { | ||
| 497 | printk("nf_conntrack: can't register to sysctl.\n"); | ||
| 498 | ret = -ENOMEM; | ||
| 499 | goto cleanup_localinops; | ||
| 500 | } | ||
| 501 | #endif | ||
| 502 | return ret; | ||
| 503 | |||
| 504 | cleanup: | ||
| 505 | synchronize_net(); | ||
| 506 | #ifdef CONFIG_SYSCTL | ||
| 507 | unregister_sysctl_table(nf_ct_ipv6_sysctl_header); | ||
| 508 | cleanup_localinops: | ||
| 509 | #endif | ||
| 510 | nf_unregister_hook(&ipv6_conntrack_local_in_ops); | ||
| 511 | cleanup_inoutandlocalops: | ||
| 512 | nf_unregister_hook(&ipv6_conntrack_out_ops); | ||
| 513 | cleanup_inandlocalops: | ||
| 514 | nf_unregister_hook(&ipv6_conntrack_local_out_ops); | ||
| 515 | cleanup_inops: | ||
| 516 | nf_unregister_hook(&ipv6_conntrack_in_ops); | ||
| 517 | cleanup_defraglocalops: | ||
| 518 | nf_unregister_hook(&ipv6_conntrack_defrag_local_out_ops); | ||
| 519 | cleanup_defragops: | ||
| 520 | nf_unregister_hook(&ipv6_conntrack_defrag_ops); | ||
| 521 | cleanup_ipv6: | ||
| 522 | nf_conntrack_l3proto_unregister(&nf_conntrack_l3proto_ipv6); | ||
| 523 | cleanup_icmpv6: | ||
| 524 | nf_conntrack_protocol_unregister(&nf_conntrack_protocol_icmpv6); | ||
| 525 | cleanup_udp: | ||
| 526 | nf_conntrack_protocol_unregister(&nf_conntrack_protocol_udp6); | ||
| 527 | cleanup_tcp: | ||
| 528 | nf_conntrack_protocol_unregister(&nf_conntrack_protocol_tcp6); | ||
| 529 | cleanup_frag6: | ||
| 530 | nf_ct_frag6_cleanup(); | ||
| 531 | cleanup_nothing: | ||
| 532 | return ret; | ||
| 533 | } | ||
| 534 | |||
| 535 | MODULE_LICENSE("GPL"); | ||
| 536 | MODULE_AUTHOR("Yasuyuki KOZAKAI @USAGI <yasuyuki.kozakai@toshiba.co.jp>"); | ||
| 537 | |||
| 538 | static int __init init(void) | ||
| 539 | { | ||
| 540 | need_nf_conntrack(); | ||
| 541 | return init_or_cleanup(1); | ||
| 542 | } | ||
| 543 | |||
| 544 | static void __exit fini(void) | ||
| 545 | { | ||
| 546 | init_or_cleanup(0); | ||
| 547 | } | ||
| 548 | |||
| 549 | module_init(init); | ||
| 550 | module_exit(fini); | ||
| 551 | |||
| 552 | void need_ip6_conntrack(void) | ||
| 553 | { | ||
| 554 | } | ||
| 555 | |||
| 556 | EXPORT_SYMBOL(need_ip6_conntrack); | ||
diff --git a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c new file mode 100644 index 000000000000..c0f1da5497a9 --- /dev/null +++ b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c | |||
| @@ -0,0 +1,272 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C)2003,2004 USAGI/WIDE Project | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * Author: | ||
| 9 | * Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp> | ||
| 10 | * | ||
| 11 | * 16 Dec 2003: Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp> | ||
| 12 | * - ICMPv6 tracking support. Derived from the original ip_conntrack code | ||
| 13 | * net/ipv4/netfilter/ip_conntrack_proto_icmp.c which had the following | ||
| 14 | * copyright information: | ||
| 15 | * (C) 1999-2001 Paul `Rusty' Russell | ||
| 16 | * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org> | ||
| 17 | */ | ||
| 18 | |||
| 19 | #include <linux/types.h> | ||
| 20 | #include <linux/sched.h> | ||
| 21 | #include <linux/timer.h> | ||
| 22 | #include <linux/module.h> | ||
| 23 | #include <linux/netfilter.h> | ||
| 24 | #include <linux/in6.h> | ||
| 25 | #include <linux/icmpv6.h> | ||
| 26 | #include <linux/ipv6.h> | ||
| 27 | #include <net/ipv6.h> | ||
| 28 | #include <net/ip6_checksum.h> | ||
| 29 | #include <linux/seq_file.h> | ||
| 30 | #include <linux/netfilter_ipv6.h> | ||
| 31 | #include <net/netfilter/nf_conntrack_tuple.h> | ||
| 32 | #include <net/netfilter/nf_conntrack_protocol.h> | ||
| 33 | #include <net/netfilter/nf_conntrack_core.h> | ||
| 34 | #include <net/netfilter/ipv6/nf_conntrack_icmpv6.h> | ||
| 35 | |||
| 36 | unsigned long nf_ct_icmpv6_timeout = 30*HZ; | ||
| 37 | |||
| 38 | #if 0 | ||
| 39 | #define DEBUGP printk | ||
| 40 | #else | ||
| 41 | #define DEBUGP(format, args...) | ||
| 42 | #endif | ||
| 43 | |||
| 44 | static int icmpv6_pkt_to_tuple(const struct sk_buff *skb, | ||
| 45 | unsigned int dataoff, | ||
| 46 | struct nf_conntrack_tuple *tuple) | ||
| 47 | { | ||
| 48 | struct icmp6hdr _hdr, *hp; | ||
| 49 | |||
| 50 | hp = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr); | ||
| 51 | if (hp == NULL) | ||
| 52 | return 0; | ||
| 53 | tuple->dst.u.icmp.type = hp->icmp6_type; | ||
| 54 | tuple->src.u.icmp.id = hp->icmp6_identifier; | ||
| 55 | tuple->dst.u.icmp.code = hp->icmp6_code; | ||
| 56 | |||
| 57 | return 1; | ||
| 58 | } | ||
| 59 | |||
| 60 | static int icmpv6_invert_tuple(struct nf_conntrack_tuple *tuple, | ||
| 61 | const struct nf_conntrack_tuple *orig) | ||
| 62 | { | ||
| 63 | /* Add 1; spaces filled with 0. */ | ||
| 64 | static u_int8_t invmap[] = { | ||
| 65 | [ICMPV6_ECHO_REQUEST - 128] = ICMPV6_ECHO_REPLY + 1, | ||
| 66 | [ICMPV6_ECHO_REPLY - 128] = ICMPV6_ECHO_REQUEST + 1, | ||
| 67 | [ICMPV6_NI_QUERY - 128] = ICMPV6_NI_QUERY + 1, | ||
| 68 | [ICMPV6_NI_REPLY - 128] = ICMPV6_NI_REPLY +1 | ||
| 69 | }; | ||
| 70 | |||
| 71 | __u8 type = orig->dst.u.icmp.type - 128; | ||
| 72 | if (type >= sizeof(invmap) || !invmap[type]) | ||
| 73 | return 0; | ||
| 74 | |||
| 75 | tuple->src.u.icmp.id = orig->src.u.icmp.id; | ||
| 76 | tuple->dst.u.icmp.type = invmap[type] - 1; | ||
| 77 | tuple->dst.u.icmp.code = orig->dst.u.icmp.code; | ||
| 78 | return 1; | ||
| 79 | } | ||
| 80 | |||
| 81 | /* Print out the per-protocol part of the tuple. */ | ||
| 82 | static int icmpv6_print_tuple(struct seq_file *s, | ||
| 83 | const struct nf_conntrack_tuple *tuple) | ||
| 84 | { | ||
| 85 | return seq_printf(s, "type=%u code=%u id=%u ", | ||
| 86 | tuple->dst.u.icmp.type, | ||
| 87 | tuple->dst.u.icmp.code, | ||
| 88 | ntohs(tuple->src.u.icmp.id)); | ||
| 89 | } | ||
| 90 | |||
| 91 | /* Print out the private part of the conntrack. */ | ||
| 92 | static int icmpv6_print_conntrack(struct seq_file *s, | ||
| 93 | const struct nf_conn *conntrack) | ||
| 94 | { | ||
| 95 | return 0; | ||
| 96 | } | ||
| 97 | |||
| 98 | /* Returns verdict for packet, or -1 for invalid. */ | ||
| 99 | static int icmpv6_packet(struct nf_conn *ct, | ||
| 100 | const struct sk_buff *skb, | ||
| 101 | unsigned int dataoff, | ||
| 102 | enum ip_conntrack_info ctinfo, | ||
| 103 | int pf, | ||
| 104 | unsigned int hooknum) | ||
| 105 | { | ||
| 106 | /* Try to delete connection immediately after all replies: | ||
| 107 | won't actually vanish as we still have skb, and del_timer | ||
| 108 | means this will only run once even if count hits zero twice | ||
| 109 | (theoretically possible with SMP) */ | ||
| 110 | if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) { | ||
| 111 | if (atomic_dec_and_test(&ct->proto.icmp.count) | ||
| 112 | && del_timer(&ct->timeout)) | ||
| 113 | ct->timeout.function((unsigned long)ct); | ||
| 114 | } else { | ||
| 115 | atomic_inc(&ct->proto.icmp.count); | ||
| 116 | nf_conntrack_event_cache(IPCT_PROTOINFO_VOLATILE, skb); | ||
| 117 | nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_icmpv6_timeout); | ||
| 118 | } | ||
| 119 | |||
| 120 | return NF_ACCEPT; | ||
| 121 | } | ||
| 122 | |||
| 123 | /* Called when a new connection for this protocol found. */ | ||
| 124 | static int icmpv6_new(struct nf_conn *conntrack, | ||
| 125 | const struct sk_buff *skb, | ||
| 126 | unsigned int dataoff) | ||
| 127 | { | ||
| 128 | static u_int8_t valid_new[] = { | ||
| 129 | [ICMPV6_ECHO_REQUEST - 128] = 1, | ||
| 130 | [ICMPV6_NI_QUERY - 128] = 1 | ||
| 131 | }; | ||
| 132 | |||
| 133 | if (conntrack->tuplehash[0].tuple.dst.u.icmp.type - 128 >= sizeof(valid_new) | ||
| 134 | || !valid_new[conntrack->tuplehash[0].tuple.dst.u.icmp.type - 128]) { | ||
| 135 | /* Can't create a new ICMPv6 `conn' with this. */ | ||
| 136 | DEBUGP("icmp: can't create new conn with type %u\n", | ||
| 137 | conntrack->tuplehash[0].tuple.dst.u.icmp.type); | ||
| 138 | NF_CT_DUMP_TUPLE(&conntrack->tuplehash[0].tuple); | ||
| 139 | return 0; | ||
| 140 | } | ||
| 141 | atomic_set(&conntrack->proto.icmp.count, 0); | ||
| 142 | return 1; | ||
| 143 | } | ||
| 144 | |||
| 145 | extern int | ||
| 146 | nf_ct_ipv6_skip_exthdr(struct sk_buff *skb, int start, u8 *nexthdrp, int len); | ||
| 147 | extern struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6; | ||
| 148 | static int | ||
| 149 | icmpv6_error_message(struct sk_buff *skb, | ||
| 150 | unsigned int icmp6off, | ||
| 151 | enum ip_conntrack_info *ctinfo, | ||
| 152 | unsigned int hooknum) | ||
| 153 | { | ||
| 154 | struct nf_conntrack_tuple intuple, origtuple; | ||
| 155 | struct nf_conntrack_tuple_hash *h; | ||
| 156 | struct icmp6hdr _hdr, *hp; | ||
| 157 | unsigned int inip6off; | ||
| 158 | struct nf_conntrack_protocol *inproto; | ||
| 159 | u_int8_t inprotonum; | ||
| 160 | unsigned int inprotoff; | ||
| 161 | |||
| 162 | NF_CT_ASSERT(skb->nfct == NULL); | ||
| 163 | |||
| 164 | hp = skb_header_pointer(skb, icmp6off, sizeof(_hdr), &_hdr); | ||
| 165 | if (hp == NULL) { | ||
| 166 | DEBUGP("icmpv6_error: Can't get ICMPv6 hdr.\n"); | ||
| 167 | return -NF_ACCEPT; | ||
| 168 | } | ||
| 169 | |||
| 170 | inip6off = icmp6off + sizeof(_hdr); | ||
| 171 | if (skb_copy_bits(skb, inip6off+offsetof(struct ipv6hdr, nexthdr), | ||
| 172 | &inprotonum, sizeof(inprotonum)) != 0) { | ||
| 173 | DEBUGP("icmpv6_error: Can't get nexthdr in inner IPv6 header.\n"); | ||
| 174 | return -NF_ACCEPT; | ||
| 175 | } | ||
| 176 | inprotoff = nf_ct_ipv6_skip_exthdr(skb, | ||
| 177 | inip6off + sizeof(struct ipv6hdr), | ||
| 178 | &inprotonum, | ||
| 179 | skb->len - inip6off | ||
| 180 | - sizeof(struct ipv6hdr)); | ||
| 181 | |||
| 182 | if ((inprotoff < 0) || (inprotoff > skb->len) || | ||
| 183 | (inprotonum == NEXTHDR_FRAGMENT)) { | ||
| 184 | DEBUGP("icmpv6_error: Can't get protocol header in ICMPv6 payload.\n"); | ||
| 185 | return -NF_ACCEPT; | ||
| 186 | } | ||
| 187 | |||
| 188 | inproto = nf_ct_find_proto(PF_INET6, inprotonum); | ||
| 189 | |||
| 190 | /* Are they talking about one of our connections? */ | ||
| 191 | if (!nf_ct_get_tuple(skb, inip6off, inprotoff, PF_INET6, inprotonum, | ||
| 192 | &origtuple, &nf_conntrack_l3proto_ipv6, inproto)) { | ||
| 193 | DEBUGP("icmpv6_error: Can't get tuple\n"); | ||
| 194 | return -NF_ACCEPT; | ||
| 195 | } | ||
| 196 | |||
| 197 | /* Ordinarily, we'd expect the inverted tupleproto, but it's | ||
| 198 | been preserved inside the ICMP. */ | ||
| 199 | if (!nf_ct_invert_tuple(&intuple, &origtuple, | ||
| 200 | &nf_conntrack_l3proto_ipv6, inproto)) { | ||
| 201 | DEBUGP("icmpv6_error: Can't invert tuple\n"); | ||
| 202 | return -NF_ACCEPT; | ||
| 203 | } | ||
| 204 | |||
| 205 | *ctinfo = IP_CT_RELATED; | ||
| 206 | |||
| 207 | h = nf_conntrack_find_get(&intuple, NULL); | ||
| 208 | if (!h) { | ||
| 209 | DEBUGP("icmpv6_error: no match\n"); | ||
| 210 | return -NF_ACCEPT; | ||
| 211 | } else { | ||
| 212 | if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY) | ||
| 213 | *ctinfo += IP_CT_IS_REPLY; | ||
| 214 | } | ||
| 215 | |||
| 216 | /* Update skb to refer to this connection */ | ||
| 217 | skb->nfct = &nf_ct_tuplehash_to_ctrack(h)->ct_general; | ||
| 218 | skb->nfctinfo = *ctinfo; | ||
| 219 | return -NF_ACCEPT; | ||
| 220 | } | ||
| 221 | |||
| 222 | static int | ||
| 223 | icmpv6_error(struct sk_buff *skb, unsigned int dataoff, | ||
| 224 | enum ip_conntrack_info *ctinfo, int pf, unsigned int hooknum) | ||
| 225 | { | ||
| 226 | struct icmp6hdr _ih, *icmp6h; | ||
| 227 | |||
| 228 | icmp6h = skb_header_pointer(skb, dataoff, sizeof(_ih), &_ih); | ||
| 229 | if (icmp6h == NULL) { | ||
| 230 | if (LOG_INVALID(IPPROTO_ICMPV6)) | ||
| 231 | nf_log_packet(PF_INET6, 0, skb, NULL, NULL, NULL, | ||
| 232 | "nf_ct_icmpv6: short packet "); | ||
| 233 | return -NF_ACCEPT; | ||
| 234 | } | ||
| 235 | |||
| 236 | if (hooknum != NF_IP6_PRE_ROUTING) | ||
| 237 | goto skipped; | ||
| 238 | |||
| 239 | /* Ignore it if the checksum's bogus. */ | ||
| 240 | if (csum_ipv6_magic(&skb->nh.ipv6h->saddr, &skb->nh.ipv6h->daddr, | ||
| 241 | skb->len - dataoff, IPPROTO_ICMPV6, | ||
| 242 | skb_checksum(skb, dataoff, | ||
| 243 | skb->len - dataoff, 0))) { | ||
| 244 | nf_log_packet(PF_INET6, 0, skb, NULL, NULL, NULL, | ||
| 245 | "nf_ct_icmpv6: ICMPv6 checksum failed\n"); | ||
| 246 | return -NF_ACCEPT; | ||
| 247 | } | ||
| 248 | |||
| 249 | skipped: | ||
| 250 | |||
| 251 | /* is not error message ? */ | ||
| 252 | if (icmp6h->icmp6_type >= 128) | ||
| 253 | return NF_ACCEPT; | ||
| 254 | |||
| 255 | return icmpv6_error_message(skb, dataoff, ctinfo, hooknum); | ||
| 256 | } | ||
| 257 | |||
| 258 | struct nf_conntrack_protocol nf_conntrack_protocol_icmpv6 = | ||
| 259 | { | ||
| 260 | .l3proto = PF_INET6, | ||
| 261 | .proto = IPPROTO_ICMPV6, | ||
| 262 | .name = "icmpv6", | ||
| 263 | .pkt_to_tuple = icmpv6_pkt_to_tuple, | ||
| 264 | .invert_tuple = icmpv6_invert_tuple, | ||
| 265 | .print_tuple = icmpv6_print_tuple, | ||
| 266 | .print_conntrack = icmpv6_print_conntrack, | ||
| 267 | .packet = icmpv6_packet, | ||
| 268 | .new = icmpv6_new, | ||
| 269 | .error = icmpv6_error, | ||
| 270 | }; | ||
| 271 | |||
| 272 | EXPORT_SYMBOL(nf_conntrack_protocol_icmpv6); | ||
diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c new file mode 100644 index 000000000000..7640b9bb7694 --- /dev/null +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c | |||
| @@ -0,0 +1,885 @@ | |||
| 1 | /* | ||
| 2 | * IPv6 fragment reassembly for connection tracking | ||
| 3 | * | ||
| 4 | * Copyright (C)2004 USAGI/WIDE Project | ||
| 5 | * | ||
| 6 | * Author: | ||
| 7 | * Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp> | ||
| 8 | * | ||
| 9 | * Based on: net/ipv6/reassembly.c | ||
| 10 | * | ||
| 11 | * This program is free software; you can redistribute it and/or | ||
| 12 | * modify it under the terms of the GNU General Public License | ||
| 13 | * as published by the Free Software Foundation; either version | ||
| 14 | * 2 of the License, or (at your option) any later version. | ||
| 15 | */ | ||
| 16 | |||
| 17 | #include <linux/config.h> | ||
| 18 | #include <linux/errno.h> | ||
| 19 | #include <linux/types.h> | ||
| 20 | #include <linux/string.h> | ||
| 21 | #include <linux/socket.h> | ||
| 22 | #include <linux/sockios.h> | ||
| 23 | #include <linux/jiffies.h> | ||
| 24 | #include <linux/net.h> | ||
| 25 | #include <linux/list.h> | ||
| 26 | #include <linux/netdevice.h> | ||
| 27 | #include <linux/in6.h> | ||
| 28 | #include <linux/ipv6.h> | ||
| 29 | #include <linux/icmpv6.h> | ||
| 30 | #include <linux/random.h> | ||
| 31 | #include <linux/jhash.h> | ||
| 32 | |||
| 33 | #include <net/sock.h> | ||
| 34 | #include <net/snmp.h> | ||
| 35 | |||
| 36 | #include <net/ipv6.h> | ||
| 37 | #include <net/protocol.h> | ||
| 38 | #include <net/transp_v6.h> | ||
| 39 | #include <net/rawv6.h> | ||
| 40 | #include <net/ndisc.h> | ||
| 41 | #include <net/addrconf.h> | ||
| 42 | #include <linux/sysctl.h> | ||
| 43 | #include <linux/netfilter.h> | ||
| 44 | #include <linux/netfilter_ipv6.h> | ||
| 45 | #include <linux/kernel.h> | ||
| 46 | #include <linux/module.h> | ||
| 47 | |||
| 48 | #if 0 | ||
| 49 | #define DEBUGP printk | ||
| 50 | #else | ||
| 51 | #define DEBUGP(format, args...) | ||
| 52 | #endif | ||
| 53 | |||
| 54 | #define NF_CT_FRAG6_HIGH_THRESH 262144 /* == 256*1024 */ | ||
| 55 | #define NF_CT_FRAG6_LOW_THRESH 196608 /* == 192*1024 */ | ||
| 56 | #define NF_CT_FRAG6_TIMEOUT IPV6_FRAG_TIMEOUT | ||
| 57 | |||
| 58 | int nf_ct_frag6_high_thresh = 256*1024; | ||
| 59 | int nf_ct_frag6_low_thresh = 192*1024; | ||
| 60 | int nf_ct_frag6_timeout = IPV6_FRAG_TIMEOUT; | ||
| 61 | |||
| 62 | struct nf_ct_frag6_skb_cb | ||
| 63 | { | ||
| 64 | struct inet6_skb_parm h; | ||
| 65 | int offset; | ||
| 66 | struct sk_buff *orig; | ||
| 67 | }; | ||
| 68 | |||
| 69 | #define NFCT_FRAG6_CB(skb) ((struct nf_ct_frag6_skb_cb*)((skb)->cb)) | ||
| 70 | |||
| 71 | struct nf_ct_frag6_queue | ||
| 72 | { | ||
| 73 | struct nf_ct_frag6_queue *next; | ||
| 74 | struct list_head lru_list; /* lru list member */ | ||
| 75 | |||
| 76 | __u32 id; /* fragment id */ | ||
| 77 | struct in6_addr saddr; | ||
| 78 | struct in6_addr daddr; | ||
| 79 | |||
| 80 | spinlock_t lock; | ||
| 81 | atomic_t refcnt; | ||
| 82 | struct timer_list timer; /* expire timer */ | ||
| 83 | struct sk_buff *fragments; | ||
| 84 | int len; | ||
| 85 | int meat; | ||
| 86 | struct timeval stamp; | ||
| 87 | unsigned int csum; | ||
| 88 | __u8 last_in; /* has first/last segment arrived? */ | ||
| 89 | #define COMPLETE 4 | ||
| 90 | #define FIRST_IN 2 | ||
| 91 | #define LAST_IN 1 | ||
| 92 | __u16 nhoffset; | ||
| 93 | struct nf_ct_frag6_queue **pprev; | ||
| 94 | }; | ||
| 95 | |||
| 96 | /* Hash table. */ | ||
| 97 | |||
| 98 | #define FRAG6Q_HASHSZ 64 | ||
| 99 | |||
| 100 | static struct nf_ct_frag6_queue *nf_ct_frag6_hash[FRAG6Q_HASHSZ]; | ||
| 101 | static rwlock_t nf_ct_frag6_lock = RW_LOCK_UNLOCKED; | ||
| 102 | static u32 nf_ct_frag6_hash_rnd; | ||
| 103 | static LIST_HEAD(nf_ct_frag6_lru_list); | ||
| 104 | int nf_ct_frag6_nqueues = 0; | ||
| 105 | |||
| 106 | static __inline__ void __fq_unlink(struct nf_ct_frag6_queue *fq) | ||
| 107 | { | ||
| 108 | if (fq->next) | ||
| 109 | fq->next->pprev = fq->pprev; | ||
| 110 | *fq->pprev = fq->next; | ||
| 111 | list_del(&fq->lru_list); | ||
| 112 | nf_ct_frag6_nqueues--; | ||
| 113 | } | ||
| 114 | |||
| 115 | static __inline__ void fq_unlink(struct nf_ct_frag6_queue *fq) | ||
| 116 | { | ||
| 117 | write_lock(&nf_ct_frag6_lock); | ||
| 118 | __fq_unlink(fq); | ||
| 119 | write_unlock(&nf_ct_frag6_lock); | ||
| 120 | } | ||
| 121 | |||
| 122 | static unsigned int ip6qhashfn(u32 id, struct in6_addr *saddr, | ||
| 123 | struct in6_addr *daddr) | ||
| 124 | { | ||
| 125 | u32 a, b, c; | ||
| 126 | |||
| 127 | a = saddr->s6_addr32[0]; | ||
| 128 | b = saddr->s6_addr32[1]; | ||
| 129 | c = saddr->s6_addr32[2]; | ||
| 130 | |||
| 131 | a += JHASH_GOLDEN_RATIO; | ||
| 132 | b += JHASH_GOLDEN_RATIO; | ||
| 133 | c += nf_ct_frag6_hash_rnd; | ||
| 134 | __jhash_mix(a, b, c); | ||
| 135 | |||
| 136 | a += saddr->s6_addr32[3]; | ||
| 137 | b += daddr->s6_addr32[0]; | ||
| 138 | c += daddr->s6_addr32[1]; | ||
| 139 | __jhash_mix(a, b, c); | ||
| 140 | |||
| 141 | a += daddr->s6_addr32[2]; | ||
| 142 | b += daddr->s6_addr32[3]; | ||
| 143 | c += id; | ||
| 144 | __jhash_mix(a, b, c); | ||
| 145 | |||
| 146 | return c & (FRAG6Q_HASHSZ - 1); | ||
| 147 | } | ||
| 148 | |||
| 149 | static struct timer_list nf_ct_frag6_secret_timer; | ||
| 150 | int nf_ct_frag6_secret_interval = 10 * 60 * HZ; | ||
| 151 | |||
| 152 | static void nf_ct_frag6_secret_rebuild(unsigned long dummy) | ||
| 153 | { | ||
| 154 | unsigned long now = jiffies; | ||
| 155 | int i; | ||
| 156 | |||
| 157 | write_lock(&nf_ct_frag6_lock); | ||
| 158 | get_random_bytes(&nf_ct_frag6_hash_rnd, sizeof(u32)); | ||
| 159 | for (i = 0; i < FRAG6Q_HASHSZ; i++) { | ||
| 160 | struct nf_ct_frag6_queue *q; | ||
| 161 | |||
| 162 | q = nf_ct_frag6_hash[i]; | ||
| 163 | while (q) { | ||
| 164 | struct nf_ct_frag6_queue *next = q->next; | ||
| 165 | unsigned int hval = ip6qhashfn(q->id, | ||
| 166 | &q->saddr, | ||
| 167 | &q->daddr); | ||
| 168 | |||
| 169 | if (hval != i) { | ||
| 170 | /* Unlink. */ | ||
| 171 | if (q->next) | ||
| 172 | q->next->pprev = q->pprev; | ||
| 173 | *q->pprev = q->next; | ||
| 174 | |||
| 175 | /* Relink to new hash chain. */ | ||
| 176 | if ((q->next = nf_ct_frag6_hash[hval]) != NULL) | ||
| 177 | q->next->pprev = &q->next; | ||
| 178 | nf_ct_frag6_hash[hval] = q; | ||
| 179 | q->pprev = &nf_ct_frag6_hash[hval]; | ||
| 180 | } | ||
| 181 | |||
| 182 | q = next; | ||
| 183 | } | ||
| 184 | } | ||
| 185 | write_unlock(&nf_ct_frag6_lock); | ||
| 186 | |||
| 187 | mod_timer(&nf_ct_frag6_secret_timer, now + nf_ct_frag6_secret_interval); | ||
| 188 | } | ||
| 189 | |||
| 190 | atomic_t nf_ct_frag6_mem = ATOMIC_INIT(0); | ||
| 191 | |||
| 192 | /* Memory Tracking Functions. */ | ||
| 193 | static inline void frag_kfree_skb(struct sk_buff *skb) | ||
| 194 | { | ||
| 195 | atomic_sub(skb->truesize, &nf_ct_frag6_mem); | ||
| 196 | if (NFCT_FRAG6_CB(skb)->orig) | ||
| 197 | kfree_skb(NFCT_FRAG6_CB(skb)->orig); | ||
| 198 | |||
| 199 | kfree_skb(skb); | ||
| 200 | } | ||
| 201 | |||
| 202 | static inline void frag_free_queue(struct nf_ct_frag6_queue *fq) | ||
| 203 | { | ||
| 204 | atomic_sub(sizeof(struct nf_ct_frag6_queue), &nf_ct_frag6_mem); | ||
| 205 | kfree(fq); | ||
| 206 | } | ||
| 207 | |||
| 208 | static inline struct nf_ct_frag6_queue *frag_alloc_queue(void) | ||
| 209 | { | ||
| 210 | struct nf_ct_frag6_queue *fq = kmalloc(sizeof(struct nf_ct_frag6_queue), GFP_ATOMIC); | ||
| 211 | |||
| 212 | if (!fq) | ||
| 213 | return NULL; | ||
| 214 | atomic_add(sizeof(struct nf_ct_frag6_queue), &nf_ct_frag6_mem); | ||
| 215 | return fq; | ||
| 216 | } | ||
| 217 | |||
| 218 | /* Destruction primitives. */ | ||
| 219 | |||
| 220 | /* Complete destruction of fq. */ | ||
| 221 | static void nf_ct_frag6_destroy(struct nf_ct_frag6_queue *fq) | ||
| 222 | { | ||
| 223 | struct sk_buff *fp; | ||
| 224 | |||
| 225 | BUG_TRAP(fq->last_in&COMPLETE); | ||
| 226 | BUG_TRAP(del_timer(&fq->timer) == 0); | ||
| 227 | |||
| 228 | /* Release all fragment data. */ | ||
| 229 | fp = fq->fragments; | ||
| 230 | while (fp) { | ||
| 231 | struct sk_buff *xp = fp->next; | ||
| 232 | |||
| 233 | frag_kfree_skb(fp); | ||
| 234 | fp = xp; | ||
| 235 | } | ||
| 236 | |||
| 237 | frag_free_queue(fq); | ||
| 238 | } | ||
| 239 | |||
| 240 | static __inline__ void fq_put(struct nf_ct_frag6_queue *fq) | ||
| 241 | { | ||
| 242 | if (atomic_dec_and_test(&fq->refcnt)) | ||
| 243 | nf_ct_frag6_destroy(fq); | ||
| 244 | } | ||
| 245 | |||
| 246 | /* Kill fq entry. It is not destroyed immediately, | ||
| 247 | * because caller (and someone more) holds reference count. | ||
| 248 | */ | ||
| 249 | static __inline__ void fq_kill(struct nf_ct_frag6_queue *fq) | ||
| 250 | { | ||
| 251 | if (del_timer(&fq->timer)) | ||
| 252 | atomic_dec(&fq->refcnt); | ||
| 253 | |||
| 254 | if (!(fq->last_in & COMPLETE)) { | ||
| 255 | fq_unlink(fq); | ||
| 256 | atomic_dec(&fq->refcnt); | ||
| 257 | fq->last_in |= COMPLETE; | ||
| 258 | } | ||
| 259 | } | ||
| 260 | |||
| 261 | static void nf_ct_frag6_evictor(void) | ||
| 262 | { | ||
| 263 | struct nf_ct_frag6_queue *fq; | ||
| 264 | struct list_head *tmp; | ||
| 265 | |||
| 266 | for (;;) { | ||
| 267 | if (atomic_read(&nf_ct_frag6_mem) <= nf_ct_frag6_low_thresh) | ||
| 268 | return; | ||
| 269 | read_lock(&nf_ct_frag6_lock); | ||
| 270 | if (list_empty(&nf_ct_frag6_lru_list)) { | ||
| 271 | read_unlock(&nf_ct_frag6_lock); | ||
| 272 | return; | ||
| 273 | } | ||
| 274 | tmp = nf_ct_frag6_lru_list.next; | ||
| 275 | fq = list_entry(tmp, struct nf_ct_frag6_queue, lru_list); | ||
| 276 | atomic_inc(&fq->refcnt); | ||
| 277 | read_unlock(&nf_ct_frag6_lock); | ||
| 278 | |||
| 279 | spin_lock(&fq->lock); | ||
| 280 | if (!(fq->last_in&COMPLETE)) | ||
| 281 | fq_kill(fq); | ||
| 282 | spin_unlock(&fq->lock); | ||
| 283 | |||
| 284 | fq_put(fq); | ||
| 285 | } | ||
| 286 | } | ||
| 287 | |||
| 288 | static void nf_ct_frag6_expire(unsigned long data) | ||
| 289 | { | ||
| 290 | struct nf_ct_frag6_queue *fq = (struct nf_ct_frag6_queue *) data; | ||
| 291 | |||
| 292 | spin_lock(&fq->lock); | ||
| 293 | |||
| 294 | if (fq->last_in & COMPLETE) | ||
| 295 | goto out; | ||
| 296 | |||
| 297 | fq_kill(fq); | ||
| 298 | |||
| 299 | out: | ||
| 300 | spin_unlock(&fq->lock); | ||
| 301 | fq_put(fq); | ||
| 302 | } | ||
| 303 | |||
| 304 | /* Creation primitives. */ | ||
| 305 | |||
| 306 | |||
| 307 | static struct nf_ct_frag6_queue *nf_ct_frag6_intern(unsigned int hash, | ||
| 308 | struct nf_ct_frag6_queue *fq_in) | ||
| 309 | { | ||
| 310 | struct nf_ct_frag6_queue *fq; | ||
| 311 | |||
| 312 | write_lock(&nf_ct_frag6_lock); | ||
| 313 | #ifdef CONFIG_SMP | ||
| 314 | for (fq = nf_ct_frag6_hash[hash]; fq; fq = fq->next) { | ||
| 315 | if (fq->id == fq_in->id && | ||
| 316 | !ipv6_addr_cmp(&fq_in->saddr, &fq->saddr) && | ||
| 317 | !ipv6_addr_cmp(&fq_in->daddr, &fq->daddr)) { | ||
| 318 | atomic_inc(&fq->refcnt); | ||
| 319 | write_unlock(&nf_ct_frag6_lock); | ||
| 320 | fq_in->last_in |= COMPLETE; | ||
| 321 | fq_put(fq_in); | ||
| 322 | return fq; | ||
| 323 | } | ||
| 324 | } | ||
| 325 | #endif | ||
| 326 | fq = fq_in; | ||
| 327 | |||
| 328 | if (!mod_timer(&fq->timer, jiffies + nf_ct_frag6_timeout)) | ||
| 329 | atomic_inc(&fq->refcnt); | ||
| 330 | |||
| 331 | atomic_inc(&fq->refcnt); | ||
| 332 | if ((fq->next = nf_ct_frag6_hash[hash]) != NULL) | ||
| 333 | fq->next->pprev = &fq->next; | ||
| 334 | nf_ct_frag6_hash[hash] = fq; | ||
| 335 | fq->pprev = &nf_ct_frag6_hash[hash]; | ||
| 336 | INIT_LIST_HEAD(&fq->lru_list); | ||
| 337 | list_add_tail(&fq->lru_list, &nf_ct_frag6_lru_list); | ||
| 338 | nf_ct_frag6_nqueues++; | ||
| 339 | write_unlock(&nf_ct_frag6_lock); | ||
| 340 | return fq; | ||
| 341 | } | ||
| 342 | |||
| 343 | |||
| 344 | static struct nf_ct_frag6_queue * | ||
| 345 | nf_ct_frag6_create(unsigned int hash, u32 id, struct in6_addr *src, struct in6_addr *dst) | ||
| 346 | { | ||
| 347 | struct nf_ct_frag6_queue *fq; | ||
| 348 | |||
| 349 | if ((fq = frag_alloc_queue()) == NULL) { | ||
| 350 | DEBUGP("Can't alloc new queue\n"); | ||
| 351 | goto oom; | ||
| 352 | } | ||
| 353 | |||
| 354 | memset(fq, 0, sizeof(struct nf_ct_frag6_queue)); | ||
| 355 | |||
| 356 | fq->id = id; | ||
| 357 | ipv6_addr_copy(&fq->saddr, src); | ||
| 358 | ipv6_addr_copy(&fq->daddr, dst); | ||
| 359 | |||
| 360 | init_timer(&fq->timer); | ||
| 361 | fq->timer.function = nf_ct_frag6_expire; | ||
| 362 | fq->timer.data = (long) fq; | ||
| 363 | fq->lock = SPIN_LOCK_UNLOCKED; | ||
| 364 | atomic_set(&fq->refcnt, 1); | ||
| 365 | |||
| 366 | return nf_ct_frag6_intern(hash, fq); | ||
| 367 | |||
| 368 | oom: | ||
| 369 | return NULL; | ||
| 370 | } | ||
| 371 | |||
| 372 | static __inline__ struct nf_ct_frag6_queue * | ||
| 373 | fq_find(u32 id, struct in6_addr *src, struct in6_addr *dst) | ||
| 374 | { | ||
| 375 | struct nf_ct_frag6_queue *fq; | ||
| 376 | unsigned int hash = ip6qhashfn(id, src, dst); | ||
| 377 | |||
| 378 | read_lock(&nf_ct_frag6_lock); | ||
| 379 | for (fq = nf_ct_frag6_hash[hash]; fq; fq = fq->next) { | ||
| 380 | if (fq->id == id && | ||
| 381 | !ipv6_addr_cmp(src, &fq->saddr) && | ||
| 382 | !ipv6_addr_cmp(dst, &fq->daddr)) { | ||
| 383 | atomic_inc(&fq->refcnt); | ||
| 384 | read_unlock(&nf_ct_frag6_lock); | ||
| 385 | return fq; | ||
| 386 | } | ||
| 387 | } | ||
| 388 | read_unlock(&nf_ct_frag6_lock); | ||
| 389 | |||
| 390 | return nf_ct_frag6_create(hash, id, src, dst); | ||
| 391 | } | ||
| 392 | |||
| 393 | |||
| 394 | static int nf_ct_frag6_queue(struct nf_ct_frag6_queue *fq, struct sk_buff *skb, | ||
| 395 | struct frag_hdr *fhdr, int nhoff) | ||
| 396 | { | ||
| 397 | struct sk_buff *prev, *next; | ||
| 398 | int offset, end; | ||
| 399 | |||
| 400 | if (fq->last_in & COMPLETE) { | ||
| 401 | DEBUGP("Allready completed\n"); | ||
| 402 | goto err; | ||
| 403 | } | ||
| 404 | |||
| 405 | offset = ntohs(fhdr->frag_off) & ~0x7; | ||
| 406 | end = offset + (ntohs(skb->nh.ipv6h->payload_len) - | ||
| 407 | ((u8 *) (fhdr + 1) - (u8 *) (skb->nh.ipv6h + 1))); | ||
| 408 | |||
| 409 | if ((unsigned int)end > IPV6_MAXPLEN) { | ||
| 410 | DEBUGP("offset is too large.\n"); | ||
| 411 | return -1; | ||
| 412 | } | ||
| 413 | |||
| 414 | if (skb->ip_summed == CHECKSUM_HW) | ||
| 415 | skb->csum = csum_sub(skb->csum, | ||
| 416 | csum_partial(skb->nh.raw, | ||
| 417 | (u8*)(fhdr + 1) - skb->nh.raw, | ||
| 418 | 0)); | ||
| 419 | |||
| 420 | /* Is this the final fragment? */ | ||
| 421 | if (!(fhdr->frag_off & htons(IP6_MF))) { | ||
| 422 | /* If we already have some bits beyond end | ||
| 423 | * or have different end, the segment is corrupted. | ||
| 424 | */ | ||
| 425 | if (end < fq->len || | ||
| 426 | ((fq->last_in & LAST_IN) && end != fq->len)) { | ||
| 427 | DEBUGP("already received last fragment\n"); | ||
| 428 | goto err; | ||
| 429 | } | ||
| 430 | fq->last_in |= LAST_IN; | ||
| 431 | fq->len = end; | ||
| 432 | } else { | ||
| 433 | /* Check if the fragment is rounded to 8 bytes. | ||
| 434 | * Required by the RFC. | ||
| 435 | */ | ||
| 436 | if (end & 0x7) { | ||
| 437 | /* RFC2460 says always send parameter problem in | ||
| 438 | * this case. -DaveM | ||
| 439 | */ | ||
| 440 | DEBUGP("the end of this fragment is not rounded to 8 bytes.\n"); | ||
| 441 | return -1; | ||
| 442 | } | ||
| 443 | if (end > fq->len) { | ||
| 444 | /* Some bits beyond end -> corruption. */ | ||
| 445 | if (fq->last_in & LAST_IN) { | ||
| 446 | DEBUGP("last packet already reached.\n"); | ||
| 447 | goto err; | ||
| 448 | } | ||
| 449 | fq->len = end; | ||
| 450 | } | ||
| 451 | } | ||
| 452 | |||
| 453 | if (end == offset) | ||
| 454 | goto err; | ||
| 455 | |||
| 456 | /* Point into the IP datagram 'data' part. */ | ||
| 457 | if (!pskb_pull(skb, (u8 *) (fhdr + 1) - skb->data)) { | ||
| 458 | DEBUGP("queue: message is too short.\n"); | ||
| 459 | goto err; | ||
| 460 | } | ||
| 461 | if (end-offset < skb->len) { | ||
| 462 | if (pskb_trim(skb, end - offset)) { | ||
| 463 | DEBUGP("Can't trim\n"); | ||
| 464 | goto err; | ||
| 465 | } | ||
| 466 | if (skb->ip_summed != CHECKSUM_UNNECESSARY) | ||
| 467 | skb->ip_summed = CHECKSUM_NONE; | ||
| 468 | } | ||
| 469 | |||
| 470 | /* Find out which fragments are in front and at the back of us | ||
| 471 | * in the chain of fragments so far. We must know where to put | ||
| 472 | * this fragment, right? | ||
| 473 | */ | ||
| 474 | prev = NULL; | ||
| 475 | for (next = fq->fragments; next != NULL; next = next->next) { | ||
| 476 | if (NFCT_FRAG6_CB(next)->offset >= offset) | ||
| 477 | break; /* bingo! */ | ||
| 478 | prev = next; | ||
| 479 | } | ||
| 480 | |||
| 481 | /* We found where to put this one. Check for overlap with | ||
| 482 | * preceding fragment, and, if needed, align things so that | ||
| 483 | * any overlaps are eliminated. | ||
| 484 | */ | ||
| 485 | if (prev) { | ||
| 486 | int i = (NFCT_FRAG6_CB(prev)->offset + prev->len) - offset; | ||
| 487 | |||
| 488 | if (i > 0) { | ||
| 489 | offset += i; | ||
| 490 | if (end <= offset) { | ||
| 491 | DEBUGP("overlap\n"); | ||
| 492 | goto err; | ||
| 493 | } | ||
| 494 | if (!pskb_pull(skb, i)) { | ||
| 495 | DEBUGP("Can't pull\n"); | ||
| 496 | goto err; | ||
| 497 | } | ||
| 498 | if (skb->ip_summed != CHECKSUM_UNNECESSARY) | ||
| 499 | skb->ip_summed = CHECKSUM_NONE; | ||
| 500 | } | ||
| 501 | } | ||
| 502 | |||
| 503 | /* Look for overlap with succeeding segments. | ||
| 504 | * If we can merge fragments, do it. | ||
| 505 | */ | ||
| 506 | while (next && NFCT_FRAG6_CB(next)->offset < end) { | ||
| 507 | /* overlap is 'i' bytes */ | ||
| 508 | int i = end - NFCT_FRAG6_CB(next)->offset; | ||
| 509 | |||
| 510 | if (i < next->len) { | ||
| 511 | /* Eat head of the next overlapped fragment | ||
| 512 | * and leave the loop. The next ones cannot overlap. | ||
| 513 | */ | ||
| 514 | DEBUGP("Eat head of the overlapped parts.: %d", i); | ||
| 515 | if (!pskb_pull(next, i)) | ||
| 516 | goto err; | ||
| 517 | |||
| 518 | /* next fragment */ | ||
| 519 | NFCT_FRAG6_CB(next)->offset += i; | ||
| 520 | fq->meat -= i; | ||
| 521 | if (next->ip_summed != CHECKSUM_UNNECESSARY) | ||
| 522 | next->ip_summed = CHECKSUM_NONE; | ||
| 523 | break; | ||
| 524 | } else { | ||
| 525 | struct sk_buff *free_it = next; | ||
| 526 | |||
| 527 | /* Old fragmnet is completely overridden with | ||
| 528 | * new one drop it. | ||
| 529 | */ | ||
| 530 | next = next->next; | ||
| 531 | |||
| 532 | if (prev) | ||
| 533 | prev->next = next; | ||
| 534 | else | ||
| 535 | fq->fragments = next; | ||
| 536 | |||
| 537 | fq->meat -= free_it->len; | ||
| 538 | frag_kfree_skb(free_it); | ||
| 539 | } | ||
| 540 | } | ||
| 541 | |||
| 542 | NFCT_FRAG6_CB(skb)->offset = offset; | ||
| 543 | |||
| 544 | /* Insert this fragment in the chain of fragments. */ | ||
| 545 | skb->next = next; | ||
| 546 | if (prev) | ||
| 547 | prev->next = skb; | ||
| 548 | else | ||
| 549 | fq->fragments = skb; | ||
| 550 | |||
| 551 | skb->dev = NULL; | ||
| 552 | skb_get_timestamp(skb, &fq->stamp); | ||
| 553 | fq->meat += skb->len; | ||
| 554 | atomic_add(skb->truesize, &nf_ct_frag6_mem); | ||
| 555 | |||
| 556 | /* The first fragment. | ||
| 557 | * nhoffset is obtained from the first fragment, of course. | ||
| 558 | */ | ||
| 559 | if (offset == 0) { | ||
| 560 | fq->nhoffset = nhoff; | ||
| 561 | fq->last_in |= FIRST_IN; | ||
| 562 | } | ||
| 563 | write_lock(&nf_ct_frag6_lock); | ||
| 564 | list_move_tail(&fq->lru_list, &nf_ct_frag6_lru_list); | ||
| 565 | write_unlock(&nf_ct_frag6_lock); | ||
| 566 | return 0; | ||
| 567 | |||
| 568 | err: | ||
| 569 | return -1; | ||
| 570 | } | ||
| 571 | |||
| 572 | /* | ||
| 573 | * Check if this packet is complete. | ||
| 574 | * Returns NULL on failure by any reason, and pointer | ||
| 575 | * to current nexthdr field in reassembled frame. | ||
| 576 | * | ||
| 577 | * It is called with locked fq, and caller must check that | ||
| 578 | * queue is eligible for reassembly i.e. it is not COMPLETE, | ||
| 579 | * the last and the first frames arrived and all the bits are here. | ||
| 580 | */ | ||
| 581 | static struct sk_buff * | ||
| 582 | nf_ct_frag6_reasm(struct nf_ct_frag6_queue *fq, struct net_device *dev) | ||
| 583 | { | ||
| 584 | struct sk_buff *fp, *op, *head = fq->fragments; | ||
| 585 | int payload_len; | ||
| 586 | |||
| 587 | fq_kill(fq); | ||
| 588 | |||
| 589 | BUG_TRAP(head != NULL); | ||
| 590 | BUG_TRAP(NFCT_FRAG6_CB(head)->offset == 0); | ||
| 591 | |||
| 592 | /* Unfragmented part is taken from the first segment. */ | ||
| 593 | payload_len = (head->data - head->nh.raw) - sizeof(struct ipv6hdr) + fq->len - sizeof(struct frag_hdr); | ||
| 594 | if (payload_len > IPV6_MAXPLEN) { | ||
| 595 | DEBUGP("payload len is too large.\n"); | ||
| 596 | goto out_oversize; | ||
| 597 | } | ||
| 598 | |||
| 599 | /* Head of list must not be cloned. */ | ||
| 600 | if (skb_cloned(head) && pskb_expand_head(head, 0, 0, GFP_ATOMIC)) { | ||
| 601 | DEBUGP("skb is cloned but can't expand head"); | ||
| 602 | goto out_oom; | ||
| 603 | } | ||
| 604 | |||
| 605 | /* If the first fragment is fragmented itself, we split | ||
| 606 | * it to two chunks: the first with data and paged part | ||
| 607 | * and the second, holding only fragments. */ | ||
| 608 | if (skb_shinfo(head)->frag_list) { | ||
| 609 | struct sk_buff *clone; | ||
| 610 | int i, plen = 0; | ||
| 611 | |||
| 612 | if ((clone = alloc_skb(0, GFP_ATOMIC)) == NULL) { | ||
| 613 | DEBUGP("Can't alloc skb\n"); | ||
| 614 | goto out_oom; | ||
| 615 | } | ||
| 616 | clone->next = head->next; | ||
| 617 | head->next = clone; | ||
| 618 | skb_shinfo(clone)->frag_list = skb_shinfo(head)->frag_list; | ||
| 619 | skb_shinfo(head)->frag_list = NULL; | ||
| 620 | for (i=0; i<skb_shinfo(head)->nr_frags; i++) | ||
| 621 | plen += skb_shinfo(head)->frags[i].size; | ||
| 622 | clone->len = clone->data_len = head->data_len - plen; | ||
| 623 | head->data_len -= clone->len; | ||
| 624 | head->len -= clone->len; | ||
| 625 | clone->csum = 0; | ||
| 626 | clone->ip_summed = head->ip_summed; | ||
| 627 | |||
| 628 | NFCT_FRAG6_CB(clone)->orig = NULL; | ||
| 629 | atomic_add(clone->truesize, &nf_ct_frag6_mem); | ||
| 630 | } | ||
| 631 | |||
| 632 | /* We have to remove fragment header from datagram and to relocate | ||
| 633 | * header in order to calculate ICV correctly. */ | ||
| 634 | head->nh.raw[fq->nhoffset] = head->h.raw[0]; | ||
| 635 | memmove(head->head + sizeof(struct frag_hdr), head->head, | ||
| 636 | (head->data - head->head) - sizeof(struct frag_hdr)); | ||
| 637 | head->mac.raw += sizeof(struct frag_hdr); | ||
| 638 | head->nh.raw += sizeof(struct frag_hdr); | ||
| 639 | |||
| 640 | skb_shinfo(head)->frag_list = head->next; | ||
| 641 | head->h.raw = head->data; | ||
| 642 | skb_push(head, head->data - head->nh.raw); | ||
| 643 | atomic_sub(head->truesize, &nf_ct_frag6_mem); | ||
| 644 | |||
| 645 | for (fp=head->next; fp; fp = fp->next) { | ||
| 646 | head->data_len += fp->len; | ||
| 647 | head->len += fp->len; | ||
| 648 | if (head->ip_summed != fp->ip_summed) | ||
| 649 | head->ip_summed = CHECKSUM_NONE; | ||
| 650 | else if (head->ip_summed == CHECKSUM_HW) | ||
| 651 | head->csum = csum_add(head->csum, fp->csum); | ||
| 652 | head->truesize += fp->truesize; | ||
| 653 | atomic_sub(fp->truesize, &nf_ct_frag6_mem); | ||
| 654 | } | ||
| 655 | |||
| 656 | head->next = NULL; | ||
| 657 | head->dev = dev; | ||
| 658 | skb_set_timestamp(head, &fq->stamp); | ||
| 659 | head->nh.ipv6h->payload_len = htons(payload_len); | ||
| 660 | |||
| 661 | /* Yes, and fold redundant checksum back. 8) */ | ||
| 662 | if (head->ip_summed == CHECKSUM_HW) | ||
| 663 | head->csum = csum_partial(head->nh.raw, head->h.raw-head->nh.raw, head->csum); | ||
| 664 | |||
| 665 | fq->fragments = NULL; | ||
| 666 | |||
| 667 | /* all original skbs are linked into the NFCT_FRAG6_CB(head).orig */ | ||
| 668 | fp = skb_shinfo(head)->frag_list; | ||
| 669 | if (NFCT_FRAG6_CB(fp)->orig == NULL) | ||
| 670 | /* at above code, head skb is divided into two skbs. */ | ||
| 671 | fp = fp->next; | ||
| 672 | |||
| 673 | op = NFCT_FRAG6_CB(head)->orig; | ||
| 674 | for (; fp; fp = fp->next) { | ||
| 675 | struct sk_buff *orig = NFCT_FRAG6_CB(fp)->orig; | ||
| 676 | |||
| 677 | op->next = orig; | ||
| 678 | op = orig; | ||
| 679 | NFCT_FRAG6_CB(fp)->orig = NULL; | ||
| 680 | } | ||
| 681 | |||
| 682 | return head; | ||
| 683 | |||
| 684 | out_oversize: | ||
| 685 | if (net_ratelimit()) | ||
| 686 | printk(KERN_DEBUG "nf_ct_frag6_reasm: payload len = %d\n", payload_len); | ||
| 687 | goto out_fail; | ||
| 688 | out_oom: | ||
| 689 | if (net_ratelimit()) | ||
| 690 | printk(KERN_DEBUG "nf_ct_frag6_reasm: no memory for reassembly\n"); | ||
| 691 | out_fail: | ||
| 692 | return NULL; | ||
| 693 | } | ||
| 694 | |||
| 695 | /* | ||
| 696 | * find the header just before Fragment Header. | ||
| 697 | * | ||
| 698 | * if success return 0 and set ... | ||
| 699 | * (*prevhdrp): the value of "Next Header Field" in the header | ||
| 700 | * just before Fragment Header. | ||
| 701 | * (*prevhoff): the offset of "Next Header Field" in the header | ||
| 702 | * just before Fragment Header. | ||
| 703 | * (*fhoff) : the offset of Fragment Header. | ||
| 704 | * | ||
| 705 | * Based on ipv6_skip_hdr() in net/ipv6/exthdr.c | ||
| 706 | * | ||
| 707 | */ | ||
| 708 | static int | ||
| 709 | find_prev_fhdr(struct sk_buff *skb, u8 *prevhdrp, int *prevhoff, int *fhoff) | ||
| 710 | { | ||
| 711 | u8 nexthdr = skb->nh.ipv6h->nexthdr; | ||
| 712 | u8 prev_nhoff = (u8 *)&skb->nh.ipv6h->nexthdr - skb->data; | ||
| 713 | int start = (u8 *)(skb->nh.ipv6h+1) - skb->data; | ||
| 714 | int len = skb->len - start; | ||
| 715 | u8 prevhdr = NEXTHDR_IPV6; | ||
| 716 | |||
| 717 | while (nexthdr != NEXTHDR_FRAGMENT) { | ||
| 718 | struct ipv6_opt_hdr hdr; | ||
| 719 | int hdrlen; | ||
| 720 | |||
| 721 | if (!ipv6_ext_hdr(nexthdr)) { | ||
| 722 | return -1; | ||
| 723 | } | ||
| 724 | if (len < (int)sizeof(struct ipv6_opt_hdr)) { | ||
| 725 | DEBUGP("too short\n"); | ||
| 726 | return -1; | ||
| 727 | } | ||
| 728 | if (nexthdr == NEXTHDR_NONE) { | ||
| 729 | DEBUGP("next header is none\n"); | ||
| 730 | return -1; | ||
| 731 | } | ||
| 732 | if (skb_copy_bits(skb, start, &hdr, sizeof(hdr))) | ||
| 733 | BUG(); | ||
| 734 | if (nexthdr == NEXTHDR_AUTH) | ||
| 735 | hdrlen = (hdr.hdrlen+2)<<2; | ||
| 736 | else | ||
| 737 | hdrlen = ipv6_optlen(&hdr); | ||
| 738 | |||
| 739 | prevhdr = nexthdr; | ||
| 740 | prev_nhoff = start; | ||
| 741 | |||
| 742 | nexthdr = hdr.nexthdr; | ||
| 743 | len -= hdrlen; | ||
| 744 | start += hdrlen; | ||
| 745 | } | ||
| 746 | |||
| 747 | if (len < 0) | ||
| 748 | return -1; | ||
| 749 | |||
| 750 | *prevhdrp = prevhdr; | ||
| 751 | *prevhoff = prev_nhoff; | ||
| 752 | *fhoff = start; | ||
| 753 | |||
| 754 | return 0; | ||
| 755 | } | ||
| 756 | |||
| 757 | struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb) | ||
| 758 | { | ||
| 759 | struct sk_buff *clone; | ||
| 760 | struct net_device *dev = skb->dev; | ||
| 761 | struct frag_hdr *fhdr; | ||
| 762 | struct nf_ct_frag6_queue *fq; | ||
| 763 | struct ipv6hdr *hdr; | ||
| 764 | int fhoff, nhoff; | ||
| 765 | u8 prevhdr; | ||
| 766 | struct sk_buff *ret_skb = NULL; | ||
| 767 | |||
| 768 | /* Jumbo payload inhibits frag. header */ | ||
| 769 | if (skb->nh.ipv6h->payload_len == 0) { | ||
| 770 | DEBUGP("payload len = 0\n"); | ||
| 771 | return skb; | ||
| 772 | } | ||
| 773 | |||
| 774 | if (find_prev_fhdr(skb, &prevhdr, &nhoff, &fhoff) < 0) | ||
| 775 | return skb; | ||
| 776 | |||
| 777 | clone = skb_clone(skb, GFP_ATOMIC); | ||
| 778 | if (clone == NULL) { | ||
| 779 | DEBUGP("Can't clone skb\n"); | ||
| 780 | return skb; | ||
| 781 | } | ||
| 782 | |||
| 783 | NFCT_FRAG6_CB(clone)->orig = skb; | ||
| 784 | |||
| 785 | if (!pskb_may_pull(clone, fhoff + sizeof(*fhdr))) { | ||
| 786 | DEBUGP("message is too short.\n"); | ||
| 787 | goto ret_orig; | ||
| 788 | } | ||
| 789 | |||
| 790 | clone->h.raw = clone->data + fhoff; | ||
| 791 | hdr = clone->nh.ipv6h; | ||
| 792 | fhdr = (struct frag_hdr *)clone->h.raw; | ||
| 793 | |||
| 794 | if (!(fhdr->frag_off & htons(0xFFF9))) { | ||
| 795 | DEBUGP("Invalid fragment offset\n"); | ||
| 796 | /* It is not a fragmented frame */ | ||
| 797 | goto ret_orig; | ||
| 798 | } | ||
| 799 | |||
| 800 | if (atomic_read(&nf_ct_frag6_mem) > nf_ct_frag6_high_thresh) | ||
| 801 | nf_ct_frag6_evictor(); | ||
| 802 | |||
| 803 | fq = fq_find(fhdr->identification, &hdr->saddr, &hdr->daddr); | ||
| 804 | if (fq == NULL) { | ||
| 805 | DEBUGP("Can't find and can't create new queue\n"); | ||
| 806 | goto ret_orig; | ||
| 807 | } | ||
| 808 | |||
| 809 | spin_lock(&fq->lock); | ||
| 810 | |||
| 811 | if (nf_ct_frag6_queue(fq, clone, fhdr, nhoff) < 0) { | ||
| 812 | spin_unlock(&fq->lock); | ||
| 813 | DEBUGP("Can't insert skb to queue\n"); | ||
| 814 | fq_put(fq); | ||
| 815 | goto ret_orig; | ||
| 816 | } | ||
| 817 | |||
| 818 | if (fq->last_in == (FIRST_IN|LAST_IN) && fq->meat == fq->len) { | ||
| 819 | ret_skb = nf_ct_frag6_reasm(fq, dev); | ||
| 820 | if (ret_skb == NULL) | ||
| 821 | DEBUGP("Can't reassemble fragmented packets\n"); | ||
| 822 | } | ||
| 823 | spin_unlock(&fq->lock); | ||
| 824 | |||
| 825 | fq_put(fq); | ||
| 826 | return ret_skb; | ||
| 827 | |||
| 828 | ret_orig: | ||
| 829 | kfree_skb(clone); | ||
| 830 | return skb; | ||
| 831 | } | ||
| 832 | |||
| 833 | void nf_ct_frag6_output(unsigned int hooknum, struct sk_buff *skb, | ||
| 834 | struct net_device *in, struct net_device *out, | ||
| 835 | int (*okfn)(struct sk_buff *)) | ||
| 836 | { | ||
| 837 | struct sk_buff *s, *s2; | ||
| 838 | |||
| 839 | for (s = NFCT_FRAG6_CB(skb)->orig; s;) { | ||
| 840 | nf_conntrack_put_reasm(s->nfct_reasm); | ||
| 841 | nf_conntrack_get_reasm(skb); | ||
| 842 | s->nfct_reasm = skb; | ||
| 843 | |||
| 844 | s2 = s->next; | ||
| 845 | NF_HOOK_THRESH(PF_INET6, hooknum, s, in, out, okfn, | ||
| 846 | NF_IP6_PRI_CONNTRACK_DEFRAG + 1); | ||
| 847 | s = s2; | ||
| 848 | } | ||
| 849 | nf_conntrack_put_reasm(skb); | ||
| 850 | } | ||
| 851 | |||
| 852 | int nf_ct_frag6_kfree_frags(struct sk_buff *skb) | ||
| 853 | { | ||
| 854 | struct sk_buff *s, *s2; | ||
| 855 | |||
| 856 | for (s = NFCT_FRAG6_CB(skb)->orig; s; s = s2) { | ||
| 857 | |||
| 858 | s2 = s->next; | ||
| 859 | kfree_skb(s); | ||
| 860 | } | ||
| 861 | |||
| 862 | kfree_skb(skb); | ||
| 863 | |||
| 864 | return 0; | ||
| 865 | } | ||
| 866 | |||
| 867 | int nf_ct_frag6_init(void) | ||
| 868 | { | ||
| 869 | nf_ct_frag6_hash_rnd = (u32) ((num_physpages ^ (num_physpages>>7)) ^ | ||
| 870 | (jiffies ^ (jiffies >> 6))); | ||
| 871 | |||
| 872 | init_timer(&nf_ct_frag6_secret_timer); | ||
| 873 | nf_ct_frag6_secret_timer.function = nf_ct_frag6_secret_rebuild; | ||
| 874 | nf_ct_frag6_secret_timer.expires = jiffies | ||
| 875 | + nf_ct_frag6_secret_interval; | ||
| 876 | add_timer(&nf_ct_frag6_secret_timer); | ||
| 877 | |||
| 878 | return 0; | ||
| 879 | } | ||
| 880 | |||
| 881 | void nf_ct_frag6_cleanup(void) | ||
| 882 | { | ||
| 883 | del_timer(&nf_ct_frag6_secret_timer); | ||
| 884 | nf_ct_frag6_evictor(); | ||
| 885 | } | ||
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index a1265a320b11..651c79b41eeb 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c | |||
| @@ -174,8 +174,10 @@ int ipv6_raw_deliver(struct sk_buff *skb, int nexthdr) | |||
| 174 | struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC); | 174 | struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC); |
| 175 | 175 | ||
| 176 | /* Not releasing hash table! */ | 176 | /* Not releasing hash table! */ |
| 177 | if (clone) | 177 | if (clone) { |
| 178 | nf_reset(clone); | ||
| 178 | rawv6_rcv(sk, clone); | 179 | rawv6_rcv(sk, clone); |
| 180 | } | ||
| 179 | } | 181 | } |
| 180 | sk = __raw_v6_lookup(sk_next(sk), nexthdr, daddr, saddr, | 182 | sk = __raw_v6_lookup(sk_next(sk), nexthdr, daddr, saddr, |
| 181 | IP6CB(skb)->iif); | 183 | IP6CB(skb)->iif); |
diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 227e99ed510c..f7f42c3e96cb 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c | |||
| @@ -1710,7 +1710,7 @@ static void fib6_dump_end(struct netlink_callback *cb) | |||
| 1710 | static int fib6_dump_done(struct netlink_callback *cb) | 1710 | static int fib6_dump_done(struct netlink_callback *cb) |
| 1711 | { | 1711 | { |
| 1712 | fib6_dump_end(cb); | 1712 | fib6_dump_end(cb); |
| 1713 | return cb->done(cb); | 1713 | return cb->done ? cb->done(cb) : 0; |
| 1714 | } | 1714 | } |
| 1715 | 1715 | ||
| 1716 | int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) | 1716 | int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) |
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index d693cb988b78..d746d3b27efb 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c | |||
| @@ -114,16 +114,9 @@ static int tcp_v6_get_port(struct sock *sk, unsigned short snum) | |||
| 114 | int low = sysctl_local_port_range[0]; | 114 | int low = sysctl_local_port_range[0]; |
| 115 | int high = sysctl_local_port_range[1]; | 115 | int high = sysctl_local_port_range[1]; |
| 116 | int remaining = (high - low) + 1; | 116 | int remaining = (high - low) + 1; |
| 117 | int rover; | 117 | int rover = net_random() % (high - low) + low; |
| 118 | 118 | ||
| 119 | spin_lock(&tcp_hashinfo.portalloc_lock); | 119 | do { |
| 120 | if (tcp_hashinfo.port_rover < low) | ||
| 121 | rover = low; | ||
| 122 | else | ||
| 123 | rover = tcp_hashinfo.port_rover; | ||
| 124 | do { rover++; | ||
| 125 | if (rover > high) | ||
| 126 | rover = low; | ||
| 127 | head = &tcp_hashinfo.bhash[inet_bhashfn(rover, tcp_hashinfo.bhash_size)]; | 120 | head = &tcp_hashinfo.bhash[inet_bhashfn(rover, tcp_hashinfo.bhash_size)]; |
| 128 | spin_lock(&head->lock); | 121 | spin_lock(&head->lock); |
| 129 | inet_bind_bucket_for_each(tb, node, &head->chain) | 122 | inet_bind_bucket_for_each(tb, node, &head->chain) |
| @@ -132,9 +125,9 @@ static int tcp_v6_get_port(struct sock *sk, unsigned short snum) | |||
| 132 | break; | 125 | break; |
| 133 | next: | 126 | next: |
| 134 | spin_unlock(&head->lock); | 127 | spin_unlock(&head->lock); |
| 128 | if (++rover > high) | ||
| 129 | rover = low; | ||
| 135 | } while (--remaining > 0); | 130 | } while (--remaining > 0); |
| 136 | tcp_hashinfo.port_rover = rover; | ||
| 137 | spin_unlock(&tcp_hashinfo.portalloc_lock); | ||
| 138 | 131 | ||
| 139 | /* Exhausted local port range during search? It is not | 132 | /* Exhausted local port range during search? It is not |
| 140 | * possible for us to be holding one of the bind hash | 133 | * possible for us to be holding one of the bind hash |
