diff options
-rw-r--r-- | include/net/inet_hashtables.h | 36 | ||||
-rw-r--r-- | net/ipv4/inet_hashtables.c | 41 | ||||
-rw-r--r-- | net/ipv4/tcp_ipv4.c | 81 |
3 files changed, 82 insertions, 76 deletions
diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h index 6731df2cea67..1c4fa0065a8e 100644 --- a/include/net/inet_hashtables.h +++ b/include/net/inet_hashtables.h | |||
@@ -16,8 +16,10 @@ | |||
16 | 16 | ||
17 | #include <linux/interrupt.h> | 17 | #include <linux/interrupt.h> |
18 | #include <linux/ip.h> | 18 | #include <linux/ip.h> |
19 | #include <linux/ipv6.h> | ||
19 | #include <linux/list.h> | 20 | #include <linux/list.h> |
20 | #include <linux/slab.h> | 21 | #include <linux/slab.h> |
22 | #include <linux/socket.h> | ||
21 | #include <linux/spinlock.h> | 23 | #include <linux/spinlock.h> |
22 | #include <linux/types.h> | 24 | #include <linux/types.h> |
23 | #include <linux/wait.h> | 25 | #include <linux/wait.h> |
@@ -274,4 +276,38 @@ out: | |||
274 | if (sk->sk_state == TCP_LISTEN) | 276 | if (sk->sk_state == TCP_LISTEN) |
275 | wake_up(&hashinfo->lhash_wait); | 277 | wake_up(&hashinfo->lhash_wait); |
276 | } | 278 | } |
279 | |||
280 | extern struct sock *__inet_lookup_listener(const struct hlist_head *head, | ||
281 | const u32 daddr, | ||
282 | const unsigned short hnum, | ||
283 | const int dif); | ||
284 | |||
285 | /* Optimize the common listener case. */ | ||
286 | static inline struct sock *inet_lookup_listener(struct inet_hashinfo *hashinfo, | ||
287 | const u32 daddr, | ||
288 | const unsigned short hnum, | ||
289 | const int dif) | ||
290 | { | ||
291 | struct sock *sk = NULL; | ||
292 | struct hlist_head *head; | ||
293 | |||
294 | read_lock(&hashinfo->lhash_lock); | ||
295 | head = &hashinfo->listening_hash[inet_lhashfn(hnum)]; | ||
296 | if (!hlist_empty(head)) { | ||
297 | const struct inet_sock *inet = inet_sk((sk = __sk_head(head))); | ||
298 | |||
299 | if (inet->num == hnum && !sk->sk_node.next && | ||
300 | (!inet->rcv_saddr || inet->rcv_saddr == daddr) && | ||
301 | (sk->sk_family == PF_INET || !ipv6_only_sock(sk)) && | ||
302 | !sk->sk_bound_dev_if) | ||
303 | goto sherry_cache; | ||
304 | sk = __inet_lookup_listener(head, daddr, hnum, dif); | ||
305 | } | ||
306 | if (sk) { | ||
307 | sherry_cache: | ||
308 | sock_hold(sk); | ||
309 | } | ||
310 | read_unlock(&hashinfo->lhash_lock); | ||
311 | return sk; | ||
312 | } | ||
277 | #endif /* _INET_HASHTABLES_H */ | 313 | #endif /* _INET_HASHTABLES_H */ |
diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c index 06cbc6f689c5..88fcba05b7d6 100644 --- a/net/ipv4/inet_hashtables.c +++ b/net/ipv4/inet_hashtables.c | |||
@@ -121,3 +121,44 @@ void inet_listen_wlock(struct inet_hashinfo *hashinfo) | |||
121 | } | 121 | } |
122 | 122 | ||
123 | EXPORT_SYMBOL(inet_listen_wlock); | 123 | EXPORT_SYMBOL(inet_listen_wlock); |
124 | |||
125 | /* | ||
126 | * Don't inline this cruft. Here are some nice properties to exploit here. The | ||
127 | * BSD API does not allow a listening sock to specify the remote port nor the | ||
128 | * remote address for the connection. So always assume those are both | ||
129 | * wildcarded during the search since they can never be otherwise. | ||
130 | */ | ||
131 | struct sock *__inet_lookup_listener(const struct hlist_head *head, const u32 daddr, | ||
132 | const unsigned short hnum, const int dif) | ||
133 | { | ||
134 | struct sock *result = NULL, *sk; | ||
135 | const struct hlist_node *node; | ||
136 | int hiscore = -1; | ||
137 | |||
138 | sk_for_each(sk, node, head) { | ||
139 | const struct inet_sock *inet = inet_sk(sk); | ||
140 | |||
141 | if (inet->num == hnum && !ipv6_only_sock(sk)) { | ||
142 | const __u32 rcv_saddr = inet->rcv_saddr; | ||
143 | int score = sk->sk_family == PF_INET ? 1 : 0; | ||
144 | |||
145 | if (rcv_saddr) { | ||
146 | if (rcv_saddr != daddr) | ||
147 | continue; | ||
148 | score += 2; | ||
149 | } | ||
150 | if (sk->sk_bound_dev_if) { | ||
151 | if (sk->sk_bound_dev_if != dif) | ||
152 | continue; | ||
153 | score += 2; | ||
154 | } | ||
155 | if (score == 5) | ||
156 | return sk; | ||
157 | if (score > hiscore) { | ||
158 | hiscore = score; | ||
159 | result = sk; | ||
160 | } | ||
161 | } | ||
162 | } | ||
163 | return result; | ||
164 | } | ||
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index dca1be67164b..a678709b36f6 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c | |||
@@ -238,78 +238,6 @@ void tcp_unhash(struct sock *sk) | |||
238 | inet_unhash(&tcp_hashinfo, sk); | 238 | inet_unhash(&tcp_hashinfo, sk); |
239 | } | 239 | } |
240 | 240 | ||
241 | /* Don't inline this cruft. Here are some nice properties to | ||
242 | * exploit here. The BSD API does not allow a listening TCP | ||
243 | * to specify the remote port nor the remote address for the | ||
244 | * connection. So always assume those are both wildcarded | ||
245 | * during the search since they can never be otherwise. | ||
246 | */ | ||
247 | static struct sock *__tcp_v4_lookup_listener(struct hlist_head *head, | ||
248 | const u32 daddr, | ||
249 | const unsigned short hnum, | ||
250 | const int dif) | ||
251 | { | ||
252 | struct sock *result = NULL, *sk; | ||
253 | struct hlist_node *node; | ||
254 | int score, hiscore; | ||
255 | |||
256 | hiscore=-1; | ||
257 | sk_for_each(sk, node, head) { | ||
258 | struct inet_sock *inet = inet_sk(sk); | ||
259 | |||
260 | if (inet->num == hnum && !ipv6_only_sock(sk)) { | ||
261 | __u32 rcv_saddr = inet->rcv_saddr; | ||
262 | |||
263 | score = (sk->sk_family == PF_INET ? 1 : 0); | ||
264 | if (rcv_saddr) { | ||
265 | if (rcv_saddr != daddr) | ||
266 | continue; | ||
267 | score+=2; | ||
268 | } | ||
269 | if (sk->sk_bound_dev_if) { | ||
270 | if (sk->sk_bound_dev_if != dif) | ||
271 | continue; | ||
272 | score+=2; | ||
273 | } | ||
274 | if (score == 5) | ||
275 | return sk; | ||
276 | if (score > hiscore) { | ||
277 | hiscore = score; | ||
278 | result = sk; | ||
279 | } | ||
280 | } | ||
281 | } | ||
282 | return result; | ||
283 | } | ||
284 | |||
285 | /* Optimize the common listener case. */ | ||
286 | static inline struct sock *tcp_v4_lookup_listener(const u32 daddr, | ||
287 | const unsigned short hnum, | ||
288 | const int dif) | ||
289 | { | ||
290 | struct sock *sk = NULL; | ||
291 | struct hlist_head *head; | ||
292 | |||
293 | read_lock(&tcp_hashinfo.lhash_lock); | ||
294 | head = &tcp_hashinfo.listening_hash[inet_lhashfn(hnum)]; | ||
295 | if (!hlist_empty(head)) { | ||
296 | struct inet_sock *inet = inet_sk((sk = __sk_head(head))); | ||
297 | |||
298 | if (inet->num == hnum && !sk->sk_node.next && | ||
299 | (!inet->rcv_saddr || inet->rcv_saddr == daddr) && | ||
300 | (sk->sk_family == PF_INET || !ipv6_only_sock(sk)) && | ||
301 | !sk->sk_bound_dev_if) | ||
302 | goto sherry_cache; | ||
303 | sk = __tcp_v4_lookup_listener(head, daddr, hnum, dif); | ||
304 | } | ||
305 | if (sk) { | ||
306 | sherry_cache: | ||
307 | sock_hold(sk); | ||
308 | } | ||
309 | read_unlock(&tcp_hashinfo.lhash_lock); | ||
310 | return sk; | ||
311 | } | ||
312 | |||
313 | /* Sockets in TCP_CLOSE state are _always_ taken out of the hash, so | 241 | /* Sockets in TCP_CLOSE state are _always_ taken out of the hash, so |
314 | * we need not check it for TCP lookups anymore, thanks Alexey. -DaveM | 242 | * we need not check it for TCP lookups anymore, thanks Alexey. -DaveM |
315 | * | 243 | * |
@@ -358,7 +286,7 @@ static inline struct sock *__tcp_v4_lookup(u32 saddr, u16 sport, | |||
358 | struct sock *sk = __tcp_v4_lookup_established(saddr, sport, | 286 | struct sock *sk = __tcp_v4_lookup_established(saddr, sport, |
359 | daddr, hnum, dif); | 287 | daddr, hnum, dif); |
360 | 288 | ||
361 | return sk ? : tcp_v4_lookup_listener(daddr, hnum, dif); | 289 | return sk ? : inet_lookup_listener(&tcp_hashinfo, daddr, hnum, dif); |
362 | } | 290 | } |
363 | 291 | ||
364 | inline struct sock *tcp_v4_lookup(u32 saddr, u16 sport, u32 daddr, | 292 | inline struct sock *tcp_v4_lookup(u32 saddr, u16 sport, u32 daddr, |
@@ -1641,9 +1569,10 @@ do_time_wait: | |||
1641 | switch (tcp_timewait_state_process((struct tcp_tw_bucket *)sk, | 1569 | switch (tcp_timewait_state_process((struct tcp_tw_bucket *)sk, |
1642 | skb, th, skb->len)) { | 1570 | skb, th, skb->len)) { |
1643 | case TCP_TW_SYN: { | 1571 | case TCP_TW_SYN: { |
1644 | struct sock *sk2 = tcp_v4_lookup_listener(skb->nh.iph->daddr, | 1572 | struct sock *sk2 = inet_lookup_listener(&tcp_hashinfo, |
1645 | ntohs(th->dest), | 1573 | skb->nh.iph->daddr, |
1646 | tcp_v4_iif(skb)); | 1574 | ntohs(th->dest), |
1575 | tcp_v4_iif(skb)); | ||
1647 | if (sk2) { | 1576 | if (sk2) { |
1648 | tcp_tw_deschedule((struct tcp_tw_bucket *)sk); | 1577 | tcp_tw_deschedule((struct tcp_tw_bucket *)sk); |
1649 | tcp_tw_put((struct tcp_tw_bucket *)sk); | 1578 | tcp_tw_put((struct tcp_tw_bucket *)sk); |