aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/udp.h6
-rw-r--r--include/net/udp.h3
-rw-r--r--net/ipv4/udp.c73
-rw-r--r--net/ipv6/udp.c14
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
160extern int udp_lib_get_port(struct sock *sk, unsigned short snum, 160extern 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 */
164extern int udp_get_port(struct sock *sk, unsigned short snum, 165extern 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 */
159static 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 */
162int udp_lib_get_port(struct sock *sk, unsigned short snum, 194int 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 }
269scan_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
256int udp_v4_get_port(struct sock *sk, unsigned short snum) 313int 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
266static inline int compute_score(struct sock *sk, struct net *net, __be32 saddr, 325static 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() */
343static struct sock *udp4_lib_lookup2(struct net *net, 400static 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
101int udp_v6_get_port(struct sock *sk, unsigned short snum) 101int 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
111static inline int compute_score(struct sock *sk, struct net *net, 113static 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() */
188static struct sock *udp6_lib_lookup2(struct net *net, 188static struct sock *udp6_lib_lookup2(struct net *net,