diff options
-rw-r--r-- | include/linux/udp.h | 6 | ||||
-rw-r--r-- | include/net/udp.h | 3 | ||||
-rw-r--r-- | net/ipv4/udp.c | 73 | ||||
-rw-r--r-- | net/ipv6/udp.c | 14 |
4 files changed, 80 insertions, 16 deletions
diff --git a/include/linux/udp.h b/include/linux/udp.h index 59f0ddf2d284..03f72a2ba028 100644 --- a/include/linux/udp.h +++ b/include/linux/udp.h | |||
@@ -88,6 +88,12 @@ static inline struct udp_sock *udp_sk(const struct sock *sk) | |||
88 | return (struct udp_sock *)sk; | 88 | return (struct udp_sock *)sk; |
89 | } | 89 | } |
90 | 90 | ||
91 | #define udp_portaddr_for_each_entry(__sk, node, list) \ | ||
92 | hlist_nulls_for_each_entry(__sk, node, list, __sk_common.skc_portaddr_node) | ||
93 | |||
94 | #define udp_portaddr_for_each_entry_rcu(__sk, node, list) \ | ||
95 | hlist_nulls_for_each_entry_rcu(__sk, node, list, __sk_common.skc_portaddr_node) | ||
96 | |||
91 | #define IS_UDPLITE(__sk) (udp_sk(__sk)->pcflag) | 97 | #define IS_UDPLITE(__sk) (udp_sk(__sk)->pcflag) |
92 | 98 | ||
93 | #endif | 99 | #endif |
diff --git a/include/net/udp.h b/include/net/udp.h index af41850f742a..5348d80b25bb 100644 --- a/include/net/udp.h +++ b/include/net/udp.h | |||
@@ -158,7 +158,8 @@ static inline void udp_lib_close(struct sock *sk, long timeout) | |||
158 | } | 158 | } |
159 | 159 | ||
160 | extern int udp_lib_get_port(struct sock *sk, unsigned short snum, | 160 | extern int udp_lib_get_port(struct sock *sk, unsigned short snum, |
161 | int (*)(const struct sock*,const struct sock*)); | 161 | int (*)(const struct sock *,const struct sock *), |
162 | unsigned int hash2_nulladdr); | ||
162 | 163 | ||
163 | /* net/ipv4/udp.c */ | 164 | /* net/ipv4/udp.c */ |
164 | extern int udp_get_port(struct sock *sk, unsigned short snum, | 165 | extern int udp_get_port(struct sock *sk, unsigned short snum, |
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index d73e9170536b..1eaf57567ebf 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c | |||
@@ -152,16 +152,49 @@ static int udp_lib_lport_inuse(struct net *net, __u16 num, | |||
152 | return 0; | 152 | return 0; |
153 | } | 153 | } |
154 | 154 | ||
155 | /* | ||
156 | * Note: we still hold spinlock of primary hash chain, so no other writer | ||
157 | * can insert/delete a socket with local_port == num | ||
158 | */ | ||
159 | static int udp_lib_lport_inuse2(struct net *net, __u16 num, | ||
160 | struct udp_hslot *hslot2, | ||
161 | struct sock *sk, | ||
162 | int (*saddr_comp)(const struct sock *sk1, | ||
163 | const struct sock *sk2)) | ||
164 | { | ||
165 | struct sock *sk2; | ||
166 | struct hlist_nulls_node *node; | ||
167 | int res = 0; | ||
168 | |||
169 | spin_lock(&hslot2->lock); | ||
170 | udp_portaddr_for_each_entry(sk2, node, &hslot2->head) | ||
171 | if (net_eq(sock_net(sk2), net) && | ||
172 | sk2 != sk && | ||
173 | (udp_sk(sk2)->udp_port_hash == num) && | ||
174 | (!sk2->sk_reuse || !sk->sk_reuse) && | ||
175 | (!sk2->sk_bound_dev_if || !sk->sk_bound_dev_if | ||
176 | || sk2->sk_bound_dev_if == sk->sk_bound_dev_if) && | ||
177 | (*saddr_comp)(sk, sk2)) { | ||
178 | res = 1; | ||
179 | break; | ||
180 | } | ||
181 | spin_unlock(&hslot2->lock); | ||
182 | return res; | ||
183 | } | ||
184 | |||
155 | /** | 185 | /** |
156 | * udp_lib_get_port - UDP/-Lite port lookup for IPv4 and IPv6 | 186 | * udp_lib_get_port - UDP/-Lite port lookup for IPv4 and IPv6 |
157 | * | 187 | * |
158 | * @sk: socket struct in question | 188 | * @sk: socket struct in question |
159 | * @snum: port number to look up | 189 | * @snum: port number to look up |
160 | * @saddr_comp: AF-dependent comparison of bound local IP addresses | 190 | * @saddr_comp: AF-dependent comparison of bound local IP addresses |
191 | * @hash2_nulladdr: AF-dependant hash value in secondary hash chains, | ||
192 | * with NULL address | ||
161 | */ | 193 | */ |
162 | int udp_lib_get_port(struct sock *sk, unsigned short snum, | 194 | int udp_lib_get_port(struct sock *sk, unsigned short snum, |
163 | int (*saddr_comp)(const struct sock *sk1, | 195 | int (*saddr_comp)(const struct sock *sk1, |
164 | const struct sock *sk2)) | 196 | const struct sock *sk2), |
197 | unsigned int hash2_nulladdr) | ||
165 | { | 198 | { |
166 | struct udp_hslot *hslot, *hslot2; | 199 | struct udp_hslot *hslot, *hslot2; |
167 | struct udp_table *udptable = sk->sk_prot->h.udp_table; | 200 | struct udp_table *udptable = sk->sk_prot->h.udp_table; |
@@ -210,6 +243,30 @@ int udp_lib_get_port(struct sock *sk, unsigned short snum, | |||
210 | } else { | 243 | } else { |
211 | hslot = udp_hashslot(udptable, net, snum); | 244 | hslot = udp_hashslot(udptable, net, snum); |
212 | spin_lock_bh(&hslot->lock); | 245 | spin_lock_bh(&hslot->lock); |
246 | if (hslot->count > 10) { | ||
247 | int exist; | ||
248 | unsigned int slot2 = udp_sk(sk)->udp_portaddr_hash ^ snum; | ||
249 | |||
250 | slot2 &= udptable->mask; | ||
251 | hash2_nulladdr &= udptable->mask; | ||
252 | |||
253 | hslot2 = udp_hashslot2(udptable, slot2); | ||
254 | if (hslot->count < hslot2->count) | ||
255 | goto scan_primary_hash; | ||
256 | |||
257 | exist = udp_lib_lport_inuse2(net, snum, hslot2, | ||
258 | sk, saddr_comp); | ||
259 | if (!exist && (hash2_nulladdr != slot2)) { | ||
260 | hslot2 = udp_hashslot2(udptable, hash2_nulladdr); | ||
261 | exist = udp_lib_lport_inuse2(net, snum, hslot2, | ||
262 | sk, saddr_comp); | ||
263 | } | ||
264 | if (exist) | ||
265 | goto fail_unlock; | ||
266 | else | ||
267 | goto found; | ||
268 | } | ||
269 | scan_primary_hash: | ||
213 | if (udp_lib_lport_inuse(net, snum, hslot, NULL, sk, | 270 | if (udp_lib_lport_inuse(net, snum, hslot, NULL, sk, |
214 | saddr_comp, 0)) | 271 | saddr_comp, 0)) |
215 | goto fail_unlock; | 272 | goto fail_unlock; |
@@ -255,12 +312,14 @@ static unsigned int udp4_portaddr_hash(struct net *net, __be32 saddr, | |||
255 | 312 | ||
256 | int udp_v4_get_port(struct sock *sk, unsigned short snum) | 313 | int udp_v4_get_port(struct sock *sk, unsigned short snum) |
257 | { | 314 | { |
315 | unsigned int hash2_nulladdr = | ||
316 | udp4_portaddr_hash(sock_net(sk), INADDR_ANY, snum); | ||
317 | unsigned int hash2_partial = | ||
318 | udp4_portaddr_hash(sock_net(sk), inet_sk(sk)->inet_rcv_saddr, 0); | ||
319 | |||
258 | /* precompute partial secondary hash */ | 320 | /* precompute partial secondary hash */ |
259 | udp_sk(sk)->udp_portaddr_hash = | 321 | udp_sk(sk)->udp_portaddr_hash = hash2_partial; |
260 | udp4_portaddr_hash(sock_net(sk), | 322 | return udp_lib_get_port(sk, snum, ipv4_rcv_saddr_equal, hash2_nulladdr); |
261 | inet_sk(sk)->inet_rcv_saddr, | ||
262 | 0); | ||
263 | return udp_lib_get_port(sk, snum, ipv4_rcv_saddr_equal); | ||
264 | } | 323 | } |
265 | 324 | ||
266 | static inline int compute_score(struct sock *sk, struct net *net, __be32 saddr, | 325 | static inline int compute_score(struct sock *sk, struct net *net, __be32 saddr, |
@@ -336,8 +395,6 @@ static inline int compute_score2(struct sock *sk, struct net *net, | |||
336 | return score; | 395 | return score; |
337 | } | 396 | } |
338 | 397 | ||
339 | #define udp_portaddr_for_each_entry_rcu(__sk, node, list) \ | ||
340 | hlist_nulls_for_each_entry_rcu(__sk, node, list, __sk_common.skc_portaddr_node) | ||
341 | 398 | ||
342 | /* called with read_rcu_lock() */ | 399 | /* called with read_rcu_lock() */ |
343 | static struct sock *udp4_lib_lookup2(struct net *net, | 400 | static struct sock *udp4_lib_lookup2(struct net *net, |
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 2915e1dad726..f4c85b200051 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c | |||
@@ -100,12 +100,14 @@ static unsigned int udp6_portaddr_hash(struct net *net, | |||
100 | 100 | ||
101 | int udp_v6_get_port(struct sock *sk, unsigned short snum) | 101 | int udp_v6_get_port(struct sock *sk, unsigned short snum) |
102 | { | 102 | { |
103 | unsigned int hash2_nulladdr = | ||
104 | udp6_portaddr_hash(sock_net(sk), &in6addr_any, snum); | ||
105 | unsigned int hash2_partial = | ||
106 | udp6_portaddr_hash(sock_net(sk), &inet6_sk(sk)->rcv_saddr, 0); | ||
107 | |||
103 | /* precompute partial secondary hash */ | 108 | /* precompute partial secondary hash */ |
104 | udp_sk(sk)->udp_portaddr_hash = | 109 | udp_sk(sk)->udp_portaddr_hash = hash2_partial; |
105 | udp6_portaddr_hash(sock_net(sk), | 110 | return udp_lib_get_port(sk, snum, ipv6_rcv_saddr_equal, hash2_nulladdr); |
106 | &inet6_sk(sk)->rcv_saddr, | ||
107 | 0); | ||
108 | return udp_lib_get_port(sk, snum, ipv6_rcv_saddr_equal); | ||
109 | } | 111 | } |
110 | 112 | ||
111 | static inline int compute_score(struct sock *sk, struct net *net, | 113 | static inline int compute_score(struct sock *sk, struct net *net, |
@@ -181,8 +183,6 @@ static inline int compute_score2(struct sock *sk, struct net *net, | |||
181 | return score; | 183 | return score; |
182 | } | 184 | } |
183 | 185 | ||
184 | #define udp_portaddr_for_each_entry_rcu(__sk, node, list) \ | ||
185 | hlist_nulls_for_each_entry_rcu(__sk, node, list, __sk_common.skc_portaddr_node) | ||
186 | 186 | ||
187 | /* called with read_rcu_lock() */ | 187 | /* called with read_rcu_lock() */ |
188 | static struct sock *udp6_lib_lookup2(struct net *net, | 188 | static struct sock *udp6_lib_lookup2(struct net *net, |