aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4
diff options
context:
space:
mode:
authorEric Dumazet <dada1@cosmosbay.com>2008-10-08 14:44:17 -0400
committerDavid S. Miller <davem@davemloft.net>2008-10-08 14:44:17 -0400
commit9088c5609584684149f3fb5b065aa7f18dcb03ff (patch)
tree270a8d4d853825b7ff169b4153816372fca00ea1 /net/ipv4
parent53e915034970935596703a6005cde27c2128b5c3 (diff)
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: Eric Dumazet <dada1@cosmosbay.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4')
-rw-r--r--net/ipv4/udp.c56
1 files changed, 12 insertions, 44 deletions
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 85f8e8e10b1b..67d8430b4a2a 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -155,55 +155,23 @@ int udp_lib_get_port(struct sock *sk, unsigned short snum,
155 write_lock_bh(&udp_hash_lock); 155 write_lock_bh(&udp_hash_lock);
156 156
157 if (!snum) { 157 if (!snum) {
158 int i, low, high, remaining; 158 int low, high, remaining;
159 unsigned rover, best, best_size_so_far; 159 unsigned rand;
160 unsigned short first;
160 161
161 inet_get_local_port_range(&low, &high); 162 inet_get_local_port_range(&low, &high);
162 remaining = (high - low) + 1; 163 remaining = (high - low) + 1;
163 164
164 best_size_so_far = UINT_MAX; 165 rand = net_random();
165 best = rover = net_random() % remaining + low; 166 snum = first = rand % remaining + low;
166 167 rand |= 1;
167 /* 1st pass: look for empty (or shortest) hash chain */ 168 while (__udp_lib_lport_inuse(net, snum, udptable)) {
168 for (i = 0; i < UDP_HTABLE_SIZE; i++) { 169 do {
169 int size = 0; 170 snum = snum + rand;
170 171 } while (snum < low || snum > high);
171 head = &udptable[udp_hashfn(net, rover)]; 172 if (snum == first)
172 if (hlist_empty(head)) 173 goto fail;
173 goto gotit;
174
175 sk_for_each(sk2, node, head) {
176 if (++size >= best_size_so_far)
177 goto next;
178 }
179 best_size_so_far = size;
180 best = rover;
181 next:
182 /* fold back if end of range */
183 if (++rover > high)
184 rover = low + ((rover - low)
185 & (UDP_HTABLE_SIZE - 1));
186
187
188 }
189
190 /* 2nd pass: find hole in shortest hash chain */
191 rover = best;
192 for (i = 0; i < (1 << 16) / UDP_HTABLE_SIZE; i++) {
193 if (! __udp_lib_lport_inuse(net, rover, udptable))
194 goto gotit;
195 rover += UDP_HTABLE_SIZE;
196 if (rover > high)
197 rover = low + ((rover - low)
198 & (UDP_HTABLE_SIZE - 1));
199 } 174 }
200
201
202 /* All ports in use! */
203 goto fail;
204
205gotit:
206 snum = rover;
207 } else { 175 } else {
208 head = &udptable[udp_hashfn(net, snum)]; 176 head = &udptable[udp_hashfn(net, snum)];
209 177