Commit 9088c560 authored by Eric Dumazet's avatar Eric Dumazet Committed by David S. Miller

udp: Improve port randomization

Current UDP port allocation is suboptimal.
We select the shortest chain to chose a port (out of 512)
that will hash in this shortest chain.

First, it can lead to give not so ramdom ports and ease
give attackers more opportunities to break the system.

Second, it can consume a lot of CPU to scan all table
in order to find the shortest chain.

Third, in some pathological cases we can fail to find
a free port even if they are plenty of them.

This patch zap the search for a short chain and only
use one random seed. Problem of getting long chains
should be addressed in another way, since we can
obtain long chains with non random ports.

Based on a report and patch from Vitaly Mayatskikh
Signed-off-by: default avatarEric Dumazet <dada1@cosmosbay.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 53e91503
...@@ -155,55 +155,23 @@ int udp_lib_get_port(struct sock *sk, unsigned short snum, ...@@ -155,55 +155,23 @@ int udp_lib_get_port(struct sock *sk, unsigned short snum,
write_lock_bh(&udp_hash_lock); write_lock_bh(&udp_hash_lock);
if (!snum) { if (!snum) {
int i, low, high, remaining; int low, high, remaining;
unsigned rover, best, best_size_so_far; unsigned rand;
unsigned short first;
inet_get_local_port_range(&low, &high); inet_get_local_port_range(&low, &high);
remaining = (high - low) + 1; remaining = (high - low) + 1;
best_size_so_far = UINT_MAX; rand = net_random();
best = rover = net_random() % remaining + low; snum = first = rand % remaining + low;
rand |= 1;
/* 1st pass: look for empty (or shortest) hash chain */ while (__udp_lib_lport_inuse(net, snum, udptable)) {
for (i = 0; i < UDP_HTABLE_SIZE; i++) { do {
int size = 0; snum = snum + rand;
} while (snum < low || snum > high);
head = &udptable[udp_hashfn(net, rover)]; if (snum == first)
if (hlist_empty(head)) goto fail;
goto gotit;
sk_for_each(sk2, node, head) {
if (++size >= best_size_so_far)
goto next;
}
best_size_so_far = size;
best = rover;
next:
/* fold back if end of range */
if (++rover > high)
rover = low + ((rover - low)
& (UDP_HTABLE_SIZE - 1));
}
/* 2nd pass: find hole in shortest hash chain */
rover = best;
for (i = 0; i < (1 << 16) / UDP_HTABLE_SIZE; i++) {
if (! __udp_lib_lport_inuse(net, rover, udptable))
goto gotit;
rover += UDP_HTABLE_SIZE;
if (rover > high)
rover = low + ((rover - low)
& (UDP_HTABLE_SIZE - 1));
} }
/* All ports in use! */
goto fail;
gotit:
snum = rover;
} else { } else {
head = &udptable[udp_hashfn(net, snum)]; head = &udptable[udp_hashfn(net, snum)];
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment