aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/udp.c
diff options
context:
space:
mode:
authorStephen Hemminger <shemminger@linux-foundation.org>2007-08-25 02:09:41 -0400
committerDavid S. Miller <davem@sunset.davemloft.net>2007-10-10 19:48:31 -0400
commit32c1da70810017a98aa6c431a5494a302b6b9a30 (patch)
tree8583fe356829dd0265c673798262453d4bc36869 /net/ipv4/udp.c
parent356f89e12e301376f26795643f3b5931c81c9cd5 (diff)
[UDP]: Randomize port selection.
This patch causes UDP port allocation to be randomized like TCP. The earlier code would always choose same port (ie first empty list). Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/udp.c')
-rw-r--r--net/ipv4/udp.c84
1 files changed, 45 insertions, 39 deletions
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 69d4bd10f9c6..a581b543bff7 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -113,9 +113,8 @@ DEFINE_SNMP_STAT(struct udp_mib, udp_statistics) __read_mostly;
113struct hlist_head udp_hash[UDP_HTABLE_SIZE]; 113struct hlist_head udp_hash[UDP_HTABLE_SIZE];
114DEFINE_RWLOCK(udp_hash_lock); 114DEFINE_RWLOCK(udp_hash_lock);
115 115
116static int udp_port_rover; 116static inline int __udp_lib_lport_inuse(__u16 num,
117 117 const struct hlist_head udptable[])
118static inline int __udp_lib_lport_inuse(__u16 num, struct hlist_head udptable[])
119{ 118{
120 struct sock *sk; 119 struct sock *sk;
121 struct hlist_node *node; 120 struct hlist_node *node;
@@ -132,11 +131,10 @@ static inline int __udp_lib_lport_inuse(__u16 num, struct hlist_head udptable[])
132 * @sk: socket struct in question 131 * @sk: socket struct in question
133 * @snum: port number to look up 132 * @snum: port number to look up
134 * @udptable: hash list table, must be of UDP_HTABLE_SIZE 133 * @udptable: hash list table, must be of UDP_HTABLE_SIZE
135 * @port_rover: pointer to record of last unallocated port
136 * @saddr_comp: AF-dependent comparison of bound local IP addresses 134 * @saddr_comp: AF-dependent comparison of bound local IP addresses
137 */ 135 */
138int __udp_lib_get_port(struct sock *sk, unsigned short snum, 136int __udp_lib_get_port(struct sock *sk, unsigned short snum,
139 struct hlist_head udptable[], int *port_rover, 137 struct hlist_head udptable[],
140 int (*saddr_comp)(const struct sock *sk1, 138 int (*saddr_comp)(const struct sock *sk1,
141 const struct sock *sk2 ) ) 139 const struct sock *sk2 ) )
142{ 140{
@@ -146,49 +144,56 @@ int __udp_lib_get_port(struct sock *sk, unsigned short snum,
146 int error = 1; 144 int error = 1;
147 145
148 write_lock_bh(&udp_hash_lock); 146 write_lock_bh(&udp_hash_lock);
149 if (snum == 0) { 147
150 int best_size_so_far, best, result, i; 148 if (!snum) {
151 149 int i;
152 if (*port_rover > sysctl_local_port_range[1] || 150 int low = sysctl_local_port_range[0];
153 *port_rover < sysctl_local_port_range[0]) 151 int high = sysctl_local_port_range[1];
154 *port_rover = sysctl_local_port_range[0]; 152 unsigned rover, best, best_size_so_far;
155 best_size_so_far = 32767; 153
156 best = result = *port_rover; 154 best_size_so_far = UINT_MAX;
157 for (i = 0; i < UDP_HTABLE_SIZE; i++, result++) { 155 best = rover = net_random() % (high - low) + low;
158 int size; 156
159 157 /* 1st pass: look for empty (or shortest) hash chain */
160 head = &udptable[result & (UDP_HTABLE_SIZE - 1)]; 158 for (i = 0; i < UDP_HTABLE_SIZE; i++) {
161 if (hlist_empty(head)) { 159 int size = 0;
162 if (result > sysctl_local_port_range[1]) 160
163 result = sysctl_local_port_range[0] + 161 head = &udptable[rover & (UDP_HTABLE_SIZE - 1)];
164 ((result - sysctl_local_port_range[0]) & 162 if (hlist_empty(head))
165 (UDP_HTABLE_SIZE - 1));
166 goto gotit; 163 goto gotit;
167 } 164
168 size = 0;
169 sk_for_each(sk2, node, head) { 165 sk_for_each(sk2, node, head) {
170 if (++size >= best_size_so_far) 166 if (++size >= best_size_so_far)
171 goto next; 167 goto next;
172 } 168 }
173 best_size_so_far = size; 169 best_size_so_far = size;
174 best = result; 170 best = rover;
175 next: 171 next:
176 ; 172 /* fold back if end of range */
173 if (++rover > high)
174 rover = low + ((rover - low)
175 & (UDP_HTABLE_SIZE - 1));
176
177
177 } 178 }
178 result = best; 179
179 for (i = 0; i < (1 << 16) / UDP_HTABLE_SIZE; 180 /* 2nd pass: find hole in shortest hash chain */
180 i++, result += UDP_HTABLE_SIZE) { 181 rover = best;
181 if (result > sysctl_local_port_range[1]) 182 for (i = 0; i < (1 << 16) / UDP_HTABLE_SIZE; i++) {
182 result = sysctl_local_port_range[0] 183 if (! __udp_lib_lport_inuse(rover, udptable))
183 + ((result - sysctl_local_port_range[0]) & 184 goto gotit;
184 (UDP_HTABLE_SIZE - 1)); 185 rover += UDP_HTABLE_SIZE;
185 if (! __udp_lib_lport_inuse(result, udptable)) 186 if (rover > high)
186 break; 187 rover = low + ((rover - low)
188 & (UDP_HTABLE_SIZE - 1));
187 } 189 }
188 if (i >= (1 << 16) / UDP_HTABLE_SIZE) 190
189 goto fail; 191
192 /* All ports in use! */
193 goto fail;
194
190gotit: 195gotit:
191 *port_rover = snum = result; 196 snum = rover;
192 } else { 197 } else {
193 head = &udptable[snum & (UDP_HTABLE_SIZE - 1)]; 198 head = &udptable[snum & (UDP_HTABLE_SIZE - 1)];
194 199
@@ -201,6 +206,7 @@ gotit:
201 (*saddr_comp)(sk, sk2) ) 206 (*saddr_comp)(sk, sk2) )
202 goto fail; 207 goto fail;
203 } 208 }
209
204 inet_sk(sk)->num = snum; 210 inet_sk(sk)->num = snum;
205 sk->sk_hash = snum; 211 sk->sk_hash = snum;
206 if (sk_unhashed(sk)) { 212 if (sk_unhashed(sk)) {
@@ -217,7 +223,7 @@ fail:
217int udp_get_port(struct sock *sk, unsigned short snum, 223int udp_get_port(struct sock *sk, unsigned short snum,
218 int (*scmp)(const struct sock *, const struct sock *)) 224 int (*scmp)(const struct sock *, const struct sock *))
219{ 225{
220 return __udp_lib_get_port(sk, snum, udp_hash, &udp_port_rover, scmp); 226 return __udp_lib_get_port(sk, snum, udp_hash, scmp);
221} 227}
222 228
223int ipv4_rcv_saddr_equal(const struct sock *sk1, const struct sock *sk2) 229int ipv4_rcv_saddr_equal(const struct sock *sk1, const struct sock *sk2)