Red Hat Bugzilla – Bug 176173
The hash.h hash_long function, when used on a 64 bit machine, ignores many of the middle-order bits.
Last modified: 2007-11-30 17:07:22 EST
Description of problem:
(Input from Partner HP - Eric Whitney (firstname.lastname@example.org)
In the svcauth code there are places that do:
hash_long((unsigned long)item->m_addr.s_addr, IP_HASHBITS);
That seems reasonable, but then again...maybe not....
I believe that s_addr is an IP address in Network Neutral format (Big Endian)
When one is on a Little Endian system, the hash_long
function gets handed a Big Endian value as a long, and
later, via the magic of being a Little Endian system,
gets byte swapped.
Version-Release number of selected component (if applicable):
RHEL4 U2 IPF 2.6.9-22.ELsmp
Steps to Reproduce:
1. 192.168.1.2 becomes 188.8.131.52 (byte swap)
2. The 32 bit IP address becomes a 64 bit long when
this code is compiled and run on an Opteron, or
an IA-64 system.
184.108.40.206 -> 0.0.0.0.220.127.116.11
3. Call the hash_long() and get back a hash value
that is IP_HASHBITS (8) in size.
You'll notice that the hash distribution is not nearly as
good as one might believe. If one would have done:
The hash_long function would have done a nice job. Since
one is not converting the network neutral IP address into a
host binary format.
With the patch below, we the hash can work better in either direction.
Not the only fix, just one suggestion.
Additional info: (one proposed patch is included)
Thanks to "Iozone" < email@example.com> for identifying this problem.
Signed-off-by: Neil Brown < firstname.lastname@example.org>
### Diffstat output
./net/sunrpc/svcauth_unix.c | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff ./net/sunrpc/svcauth_unix.c~current~ ./net/sunrpc/svcauth_unix.c
--- ./net/sunrpc/svcauth_unix.c~current~ 2005-12-16 15:10:12.000000000 +1100
+++ ./net/sunrpc/svcauth_unix.c 2005-12-16 15:12:00.000000000 +1100
@@ -101,10 +101,22 @@ static void ip_map_put(struct cache_head
+#if IP_HASHBITS == 8
+/* hash_long on a 64 bit machine is currently REALLY BAD for
+ * IP addresses in reverse-endian (i.e. on a little-endian machine).
+ * So use a trivial but reliable hash instead
+static inline int hash_ip(unsigned long ip)
+ int hash = ip ^ (ip>>16);
+ return (hash ^ (hash>>8)) & 0xff;
static inline int ip_map_hash(struct ip_map *item)
return hash_str(item->m_class, IP_HASHBITS) ^
- hash_long((unsigned long)item->m_addr.s_addr, IP_HASHBITS);
+ hash_ip((unsigned long)item->m_addr.s_addr);
static inline int ip_map_match(struct ip_map *item, struct ip_map *tmp)
committed in stream u4 build 34.5. A test kernel with this patch is available
An advisory has been issued which should help the problem
described in this bug report. This report is therefore being
closed with a resolution of ERRATA. For more information
on the solution and/or where to find the updated files,
please follow the link below. You may reopen this bug report
if the solution does not work for you.