All of lore.kernel.org
 help / color / mirror / Atom feed
* RFC: Established connections hash function
@ 2007-03-22 15:39 Nikolaos D. Bougalis
  2007-03-22 15:52 ` Evgeniy Polyakov
  2007-03-27 14:11 ` Andi Kleen
  0 siblings, 2 replies; 34+ messages in thread
From: Nikolaos D. Bougalis @ 2007-03-22 15:39 UTC (permalink / raw)
  To: netdev

    Hello,

    I have noticed that the hash function that the kernel uses for
established TCP/IP connections is rather simplistic, specifically:

    h = (local address ^ local_port) ^ (remote_address ^ remote_port);
    h ^= h >> 16;
    h ^= h >> 8;

    Now, simple is great, but this has a number of issues, not the least of
which is that an attacker can very easily cause collisions and force
extremely long chain lengths, a situation that becomes worse the more
distinct IP addresses and listening ports a box has.

    Consider, for example, a box that has 20 ports open and 4 consecutive IP
addresses. An attacker that has an entire class C available can create
24,576 connections that hash to the same value, resulting in a ridiculously
overlong chain. With servers that do virtual hosting and have dozens of IPs,
the situation can become much worse very fast.

    This particular hash seems to be the odd-man out, since most other
network related hashes in the kernel seem to be Jenkins-based, and some use
tagged hashing to defeat algorithmic complexity attacks. For example, the
route hash uses this:

static unsigned int rt_hash_rnd;

static unsigned int rt_hash_code(u32 daddr, u32 saddr)
{
        return (jhash_2words(daddr, saddr, rt_hash_rnd)
                & rt_hash_mask);
}

    With this in mind, I propose the following replacement for inet_ehashfn,
which defeats algorithmic complexity attacks and achieves excellent
distribution:

unsigned int inet_ehashfn(const __be32 laddr, const __u16 lport,
                          const __be32 faddr, const __be16 fport)
{
    return jhash_3words((__force __u32)faddr, (__force __u32)laddr,
                        (((__force __u32)fport) << 16) + lport,
                        inet_ehash_rnd);
}

    where inet_ehash_rnd is initialized once in tcp_init to a random 32-bit
value.

    I will be more than happy to provide a patch for this, but I figured I
would solicit some input first.

    Nik B.



^ permalink raw reply	[flat|nested] 34+ messages in thread
* Re: RFC: Established connections hash function
@ 2007-03-24 12:26 linux
  2007-03-24 13:29 ` Evgeniy Polyakov
  0 siblings, 1 reply; 34+ messages in thread
From: linux @ 2007-03-24 12:26 UTC (permalink / raw)
  To: johnpol.2ka.mipt.ru, netdev; +Cc: linux

> Result with jenkins:
> 1 23880
> 2 12108
> 3 4040
> 4 1019
> 5 200
> 6 30
> 7 8
> 8 1
> 
> Xor:
> 1 65536

Precisely.  This means that the Xor hash SUCKS, because its output is conspicuously
non-random.

What you expect is a Poisson distribution, where the chance that a chain will contain
k elements is
	P(lambda,k) = e^-lambda * lambda^k / k!
lambda is the average loading per chain.  In your example, it's 1 (65536 inputs, 65536 outputs).
(^ is exponentiation, ! is factorial)

So the distribution I expect to get is:
 0 24109.347656
 1 24109.347656
 2 12054.673828
 3 4018.224609
 4 1004.556152
 5 200.911224
 6 33.485203
 7 4.783601
 8 0.597950
 9 0.066439
10 0.006644

Whick looks a HELL of a lot like what you observed.
(The jenkins result above has 24250 chains with no entries.)


Now, you can sometimes use properties of the inputs to get a distribution
that is more uniform than random, by letting the distribution of the input
"show through" the hash funciton.  Which the xor hash does.  But this
depends on making assumptions about the input distribution, which means
that you're assuming that they're not being chosen maliciously.

If an attacker is choosing maliciously, which is a required assumption
in today's Internet, the best you can do is random.

Now, the core Jenkins hash mix function basically takes three inputs.
What jhash_3words does with it is:
	a += K
	b += K
	c += seed
	__jhash_mix(a, b, c)
	return c;

Now, the ipv4 hash operation fundamentally involves 96 bits of input,
which is a good match for jhash.  If you want to add a salt, perhaps the
simplest thing would be to just replace those constants K with a 96-bit
salt and be done with it:

	a = (lport<<16) + rport + salt[0];
	Xb = laddr + salt[1];
	c = raddr + salt[2];
	__jhash_mix(a,b,c)
	return c;

Regarding control by attackers, let's consider the four inputs and see
how much information an attacker can insert into each one:

remote port: An attacker has complete control over this.  16 bits.
remote address: Depends on the size of the bit-net.  Can vary from 0 bits
	(one machine) to 20 bits for a large bot-net.
local address: Limited to the number of addresses the local machine has.
	Typically 0 bits, rarely more than 2 bits.
	May be much larger (8 bits or more) for stateful firewalls and
	other sorts of proxies.
local port: Limited to the number of open server ports.  Typically 3-6
	bits, but may be lower on heavily firewalled machines.

Certainly combining any two of these in a predictable way without some
non-linear salting makes an attacker's job easier.  While folding the
local and remote addresses before hashing is usually safe because the
local address is usually unique, there are situations in which there are
a large number of possible local addresses.

For example, it allows an attacker with a /24 to attack, say, a stateful
firewall guarding a /24.  If I have my machine at address a.b.c.d connect
to remote machine x.y.z.~d, then they always fold to a^x.b^y.c^z.255,
and I can, by making the local and remote paorts identical for all the
attacks, force 254-entry hash chains without knowing anything else about
the hash function, salt, or whatever.


An interesting question is whether it's better to mix salt into the
bits an attacker has most or least control over.  It's not immediately
obvious to me; does anyone else have insight?  Just mixing in 96 bits
over everything does seem to render the question moot.

^ permalink raw reply	[flat|nested] 34+ messages in thread

end of thread, other threads:[~2007-03-29  9:19 UTC | newest]

Thread overview: 34+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-03-22 15:39 RFC: Established connections hash function Nikolaos D. Bougalis
2007-03-22 15:52 ` Evgeniy Polyakov
2007-03-22 17:32   ` Nikolaos D. Bougalis
2007-03-22 18:21     ` Evgeniy Polyakov
2007-03-22 19:44       ` Nikolaos D. Bougalis
2007-03-22 19:56         ` Evgeniy Polyakov
2007-03-22 20:53           ` Nikolaos D. Bougalis
2007-03-23  7:52             ` Evgeniy Polyakov
2007-03-22 20:58         ` David Miller
2007-03-22 22:03           ` Eric Dumazet
2007-03-23  7:11             ` David Miller
2007-03-23  8:00               ` Eric Dumazet
2007-03-23 18:46                 ` David Miller
2007-03-23  8:07           ` Evgeniy Polyakov
2007-03-23  8:17             ` Eric Dumazet
2007-03-23  8:33               ` Evgeniy Polyakov
2007-03-23  9:10                 ` Evgeniy Polyakov
2007-03-23 11:58             ` XOR hash beauty solved [Was: RFC: Established connections hash function] Evgeniy Polyakov
2007-03-23 12:51               ` Nikolaos D. Bougalis
2007-03-23 12:45             ` RFC: Established connections hash function Nikolaos D. Bougalis
2007-03-27 14:11 ` Andi Kleen
2007-03-28  5:01   ` Nikolaos D. Bougalis
2007-03-28  6:29     ` David Miller
2007-03-28  9:29     ` Andi Kleen
2007-03-28 10:45       ` Evgeniy Polyakov
2007-03-28 14:14         ` Andi Kleen
2007-03-28 13:50           ` Eric Dumazet
2007-03-28 14:52             ` Andi Kleen
2007-03-29  9:18               ` Evgeniy Polyakov
2007-03-28 14:17           ` RFC: Established connections hash function II Andi Kleen
2007-03-28 19:04           ` RFC: Established connections hash function David Miller
2007-03-28 20:12             ` Andi Kleen
2007-03-24 12:26 linux
2007-03-24 13:29 ` Evgeniy Polyakov

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.