Merge branch 'akpm' (patches from Andrew)
[cascardo/linux.git] / net / xfrm / xfrm_hash.h
index 0622d31..666c5ff 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <linux/xfrm.h>
 #include <linux/socket.h>
+#include <linux/jhash.h>
 
 static inline unsigned int __xfrm4_addr_hash(const xfrm_address_t *addr)
 {
@@ -28,6 +29,58 @@ static inline unsigned int __xfrm6_daddr_saddr_hash(const xfrm_address_t *daddr,
                     saddr->a6[2] ^ saddr->a6[3]);
 }
 
+static inline u32 __bits2mask32(__u8 bits)
+{
+       u32 mask32 = 0xffffffff;
+
+       if (bits == 0)
+               mask32 = 0;
+       else if (bits < 32)
+               mask32 <<= (32 - bits);
+
+       return mask32;
+}
+
+static inline unsigned int __xfrm4_dpref_spref_hash(const xfrm_address_t *daddr,
+                                                   const xfrm_address_t *saddr,
+                                                   __u8 dbits,
+                                                   __u8 sbits)
+{
+       return jhash_2words(ntohl(daddr->a4) & __bits2mask32(dbits),
+                           ntohl(saddr->a4) & __bits2mask32(sbits),
+                           0);
+}
+
+static inline unsigned int __xfrm6_pref_hash(const xfrm_address_t *addr,
+                                            __u8 prefixlen)
+{
+       int pdw;
+       int pbi;
+       u32 initval = 0;
+
+       pdw = prefixlen >> 5;     /* num of whole u32 in prefix */
+       pbi = prefixlen &  0x1f;  /* num of bits in incomplete u32 in prefix */
+
+       if (pbi) {
+               __be32 mask;
+
+               mask = htonl((0xffffffff) << (32 - pbi));
+
+               initval = (__force u32)(addr->a6[pdw] & mask);
+       }
+
+       return jhash2((__force u32 *)addr->a6, pdw, initval);
+}
+
+static inline unsigned int __xfrm6_dpref_spref_hash(const xfrm_address_t *daddr,
+                                                   const xfrm_address_t *saddr,
+                                                   __u8 dbits,
+                                                   __u8 sbits)
+{
+       return __xfrm6_pref_hash(daddr, dbits) ^
+              __xfrm6_pref_hash(saddr, sbits);
+}
+
 static inline unsigned int __xfrm_dst_hash(const xfrm_address_t *daddr,
                                           const xfrm_address_t *saddr,
                                           u32 reqid, unsigned short family,
@@ -84,7 +137,8 @@ static inline unsigned int __idx_hash(u32 index, unsigned int hmask)
 }
 
 static inline unsigned int __sel_hash(const struct xfrm_selector *sel,
-                                     unsigned short family, unsigned int hmask)
+                                     unsigned short family, unsigned int hmask,
+                                     u8 dbits, u8 sbits)
 {
        const xfrm_address_t *daddr = &sel->daddr;
        const xfrm_address_t *saddr = &sel->saddr;
@@ -92,19 +146,19 @@ static inline unsigned int __sel_hash(const struct xfrm_selector *sel,
 
        switch (family) {
        case AF_INET:
-               if (sel->prefixlen_d != 32 ||
-                   sel->prefixlen_s != 32)
+               if (sel->prefixlen_d < dbits ||
+                   sel->prefixlen_s < sbits)
                        return hmask + 1;
 
-               h = __xfrm4_daddr_saddr_hash(daddr, saddr);
+               h = __xfrm4_dpref_spref_hash(daddr, saddr, dbits, sbits);
                break;
 
        case AF_INET6:
-               if (sel->prefixlen_d != 128 ||
-                   sel->prefixlen_s != 128)
+               if (sel->prefixlen_d < dbits ||
+                   sel->prefixlen_s < sbits)
                        return hmask + 1;
 
-               h = __xfrm6_daddr_saddr_hash(daddr, saddr);
+               h = __xfrm6_dpref_spref_hash(daddr, saddr, dbits, sbits);
                break;
        }
        h ^= (h >> 16);
@@ -113,17 +167,19 @@ static inline unsigned int __sel_hash(const struct xfrm_selector *sel,
 
 static inline unsigned int __addr_hash(const xfrm_address_t *daddr,
                                       const xfrm_address_t *saddr,
-                                      unsigned short family, unsigned int hmask)
+                                      unsigned short family,
+                                      unsigned int hmask,
+                                      u8 dbits, u8 sbits)
 {
        unsigned int h = 0;
 
        switch (family) {
        case AF_INET:
-               h = __xfrm4_daddr_saddr_hash(daddr, saddr);
+               h = __xfrm4_dpref_spref_hash(daddr, saddr, dbits, sbits);
                break;
 
        case AF_INET6:
-               h = __xfrm6_daddr_saddr_hash(daddr, saddr);
+               h = __xfrm6_dpref_spref_hash(daddr, saddr, dbits, sbits);
                break;
        }
        h ^= (h >> 16);