diff options
Diffstat (limited to 'net/ipv4/udp.c')
-rw-r--r-- | net/ipv4/udp.c | 84 |
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; | |||
113 | struct hlist_head udp_hash[UDP_HTABLE_SIZE]; | 113 | struct hlist_head udp_hash[UDP_HTABLE_SIZE]; |
114 | DEFINE_RWLOCK(udp_hash_lock); | 114 | DEFINE_RWLOCK(udp_hash_lock); |
115 | 115 | ||
116 | static int udp_port_rover; | 116 | static inline int __udp_lib_lport_inuse(__u16 num, |
117 | 117 | const struct hlist_head udptable[]) | |
118 | static 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 | */ |
138 | int __udp_lib_get_port(struct sock *sk, unsigned short snum, | 136 | int __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 | |||
190 | gotit: | 195 | gotit: |
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: | |||
217 | int udp_get_port(struct sock *sk, unsigned short snum, | 223 | int 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 | ||
223 | int ipv4_rcv_saddr_equal(const struct sock *sk1, const struct sock *sk2) | 229 | int ipv4_rcv_saddr_equal(const struct sock *sk1, const struct sock *sk2) |