diff options
Diffstat (limited to 'net/ipv4/udp.c')
-rw-r--r-- | net/ipv4/udp.c | 123 |
1 files changed, 52 insertions, 71 deletions
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 57e26fa66185..eacf4cfef146 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c | |||
@@ -108,9 +108,6 @@ | |||
108 | * Snmp MIB for the UDP layer | 108 | * Snmp MIB for the UDP layer |
109 | */ | 109 | */ |
110 | 110 | ||
111 | DEFINE_SNMP_STAT(struct udp_mib, udp_stats_in6) __read_mostly; | ||
112 | EXPORT_SYMBOL(udp_stats_in6); | ||
113 | |||
114 | struct hlist_head udp_hash[UDP_HTABLE_SIZE]; | 111 | struct hlist_head udp_hash[UDP_HTABLE_SIZE]; |
115 | DEFINE_RWLOCK(udp_hash_lock); | 112 | DEFINE_RWLOCK(udp_hash_lock); |
116 | 113 | ||
@@ -125,14 +122,23 @@ EXPORT_SYMBOL(sysctl_udp_wmem_min); | |||
125 | atomic_t udp_memory_allocated; | 122 | atomic_t udp_memory_allocated; |
126 | EXPORT_SYMBOL(udp_memory_allocated); | 123 | EXPORT_SYMBOL(udp_memory_allocated); |
127 | 124 | ||
128 | static inline int __udp_lib_lport_inuse(struct net *net, __u16 num, | 125 | static int udp_lib_lport_inuse(struct net *net, __u16 num, |
129 | const struct hlist_head udptable[]) | 126 | const struct hlist_head udptable[], |
127 | struct sock *sk, | ||
128 | int (*saddr_comp)(const struct sock *sk1, | ||
129 | const struct sock *sk2)) | ||
130 | { | 130 | { |
131 | struct sock *sk; | 131 | struct sock *sk2; |
132 | struct hlist_node *node; | 132 | struct hlist_node *node; |
133 | 133 | ||
134 | sk_for_each(sk, node, &udptable[udp_hashfn(net, num)]) | 134 | sk_for_each(sk2, node, &udptable[udp_hashfn(net, num)]) |
135 | if (net_eq(sock_net(sk), net) && sk->sk_hash == num) | 135 | if (net_eq(sock_net(sk2), net) && |
136 | sk2 != sk && | ||
137 | sk2->sk_hash == num && | ||
138 | (!sk2->sk_reuse || !sk->sk_reuse) && | ||
139 | (!sk2->sk_bound_dev_if || !sk->sk_bound_dev_if | ||
140 | || sk2->sk_bound_dev_if == sk->sk_bound_dev_if) && | ||
141 | (*saddr_comp)(sk, sk2)) | ||
136 | return 1; | 142 | return 1; |
137 | return 0; | 143 | return 0; |
138 | } | 144 | } |
@@ -149,83 +155,37 @@ int udp_lib_get_port(struct sock *sk, unsigned short snum, | |||
149 | const struct sock *sk2 ) ) | 155 | const struct sock *sk2 ) ) |
150 | { | 156 | { |
151 | struct hlist_head *udptable = sk->sk_prot->h.udp_hash; | 157 | struct hlist_head *udptable = sk->sk_prot->h.udp_hash; |
152 | struct hlist_node *node; | ||
153 | struct hlist_head *head; | ||
154 | struct sock *sk2; | ||
155 | int error = 1; | 158 | int error = 1; |
156 | struct net *net = sock_net(sk); | 159 | struct net *net = sock_net(sk); |
157 | 160 | ||
158 | write_lock_bh(&udp_hash_lock); | 161 | write_lock_bh(&udp_hash_lock); |
159 | 162 | ||
160 | if (!snum) { | 163 | if (!snum) { |
161 | int i, low, high, remaining; | 164 | int low, high, remaining; |
162 | unsigned rover, best, best_size_so_far; | 165 | unsigned rand; |
166 | unsigned short first; | ||
163 | 167 | ||
164 | inet_get_local_port_range(&low, &high); | 168 | inet_get_local_port_range(&low, &high); |
165 | remaining = (high - low) + 1; | 169 | remaining = (high - low) + 1; |
166 | 170 | ||
167 | best_size_so_far = UINT_MAX; | 171 | rand = net_random(); |
168 | best = rover = net_random() % remaining + low; | 172 | snum = first = rand % remaining + low; |
169 | 173 | rand |= 1; | |
170 | /* 1st pass: look for empty (or shortest) hash chain */ | 174 | while (udp_lib_lport_inuse(net, snum, udptable, sk, |
171 | for (i = 0; i < UDP_HTABLE_SIZE; i++) { | 175 | saddr_comp)) { |
172 | int size = 0; | 176 | do { |
173 | 177 | snum = snum + rand; | |
174 | head = &udptable[udp_hashfn(net, rover)]; | 178 | } while (snum < low || snum > high); |
175 | if (hlist_empty(head)) | 179 | if (snum == first) |
176 | goto gotit; | 180 | goto fail; |
177 | |||
178 | sk_for_each(sk2, node, head) { | ||
179 | if (++size >= best_size_so_far) | ||
180 | goto next; | ||
181 | } | ||
182 | best_size_so_far = size; | ||
183 | best = rover; | ||
184 | next: | ||
185 | /* fold back if end of range */ | ||
186 | if (++rover > high) | ||
187 | rover = low + ((rover - low) | ||
188 | & (UDP_HTABLE_SIZE - 1)); | ||
189 | |||
190 | |||
191 | } | ||
192 | |||
193 | /* 2nd pass: find hole in shortest hash chain */ | ||
194 | rover = best; | ||
195 | for (i = 0; i < (1 << 16) / UDP_HTABLE_SIZE; i++) { | ||
196 | if (! __udp_lib_lport_inuse(net, rover, udptable)) | ||
197 | goto gotit; | ||
198 | rover += UDP_HTABLE_SIZE; | ||
199 | if (rover > high) | ||
200 | rover = low + ((rover - low) | ||
201 | & (UDP_HTABLE_SIZE - 1)); | ||
202 | } | 181 | } |
203 | 182 | } else if (udp_lib_lport_inuse(net, snum, udptable, sk, saddr_comp)) | |
204 | |||
205 | /* All ports in use! */ | ||
206 | goto fail; | 183 | goto fail; |
207 | 184 | ||
208 | gotit: | ||
209 | snum = rover; | ||
210 | } else { | ||
211 | head = &udptable[udp_hashfn(net, snum)]; | ||
212 | |||
213 | sk_for_each(sk2, node, head) | ||
214 | if (sk2->sk_hash == snum && | ||
215 | sk2 != sk && | ||
216 | net_eq(sock_net(sk2), net) && | ||
217 | (!sk2->sk_reuse || !sk->sk_reuse) && | ||
218 | (!sk2->sk_bound_dev_if || !sk->sk_bound_dev_if | ||
219 | || sk2->sk_bound_dev_if == sk->sk_bound_dev_if) && | ||
220 | (*saddr_comp)(sk, sk2) ) | ||
221 | goto fail; | ||
222 | } | ||
223 | |||
224 | inet_sk(sk)->num = snum; | 185 | inet_sk(sk)->num = snum; |
225 | sk->sk_hash = snum; | 186 | sk->sk_hash = snum; |
226 | if (sk_unhashed(sk)) { | 187 | if (sk_unhashed(sk)) { |
227 | head = &udptable[udp_hashfn(net, snum)]; | 188 | sk_add_node(sk, &udptable[udp_hashfn(net, snum)]); |
228 | sk_add_node(sk, head); | ||
229 | sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); | 189 | sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); |
230 | } | 190 | } |
231 | error = 0; | 191 | error = 0; |
@@ -302,6 +262,28 @@ static struct sock *__udp4_lib_lookup(struct net *net, __be32 saddr, | |||
302 | return result; | 262 | return result; |
303 | } | 263 | } |
304 | 264 | ||
265 | static inline struct sock *__udp4_lib_lookup_skb(struct sk_buff *skb, | ||
266 | __be16 sport, __be16 dport, | ||
267 | struct hlist_head udptable[]) | ||
268 | { | ||
269 | struct sock *sk; | ||
270 | const struct iphdr *iph = ip_hdr(skb); | ||
271 | |||
272 | if (unlikely(sk = skb_steal_sock(skb))) | ||
273 | return sk; | ||
274 | else | ||
275 | return __udp4_lib_lookup(dev_net(skb->dst->dev), iph->saddr, sport, | ||
276 | iph->daddr, dport, inet_iif(skb), | ||
277 | udptable); | ||
278 | } | ||
279 | |||
280 | struct sock *udp4_lib_lookup(struct net *net, __be32 saddr, __be16 sport, | ||
281 | __be32 daddr, __be16 dport, int dif) | ||
282 | { | ||
283 | return __udp4_lib_lookup(net, saddr, sport, daddr, dport, dif, udp_hash); | ||
284 | } | ||
285 | EXPORT_SYMBOL_GPL(udp4_lib_lookup); | ||
286 | |||
305 | static inline struct sock *udp_v4_mcast_next(struct sock *sk, | 287 | static inline struct sock *udp_v4_mcast_next(struct sock *sk, |
306 | __be16 loc_port, __be32 loc_addr, | 288 | __be16 loc_port, __be32 loc_addr, |
307 | __be16 rmt_port, __be32 rmt_addr, | 289 | __be16 rmt_port, __be32 rmt_addr, |
@@ -1201,8 +1183,7 @@ int __udp4_lib_rcv(struct sk_buff *skb, struct hlist_head udptable[], | |||
1201 | return __udp4_lib_mcast_deliver(net, skb, uh, | 1183 | return __udp4_lib_mcast_deliver(net, skb, uh, |
1202 | saddr, daddr, udptable); | 1184 | saddr, daddr, udptable); |
1203 | 1185 | ||
1204 | sk = __udp4_lib_lookup(net, saddr, uh->source, daddr, | 1186 | sk = __udp4_lib_lookup_skb(skb, uh->source, uh->dest, udptable); |
1205 | uh->dest, inet_iif(skb), udptable); | ||
1206 | 1187 | ||
1207 | if (sk != NULL) { | 1188 | if (sk != NULL) { |
1208 | int ret = udp_queue_rcv_skb(sk, skb); | 1189 | int ret = udp_queue_rcv_skb(sk, skb); |