diff options
Diffstat (limited to 'net/ipv4/inet_connection_sock.c')
-rw-r--r-- | net/ipv4/inet_connection_sock.c | 64 |
1 files changed, 57 insertions, 7 deletions
diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index db0cf17c00f7..f0c5b9c1a957 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c | |||
@@ -283,7 +283,9 @@ static int inet_csk_wait_for_connect(struct sock *sk, long timeo) | |||
283 | struct sock *inet_csk_accept(struct sock *sk, int flags, int *err) | 283 | struct sock *inet_csk_accept(struct sock *sk, int flags, int *err) |
284 | { | 284 | { |
285 | struct inet_connection_sock *icsk = inet_csk(sk); | 285 | struct inet_connection_sock *icsk = inet_csk(sk); |
286 | struct request_sock_queue *queue = &icsk->icsk_accept_queue; | ||
286 | struct sock *newsk; | 287 | struct sock *newsk; |
288 | struct request_sock *req; | ||
287 | int error; | 289 | int error; |
288 | 290 | ||
289 | lock_sock(sk); | 291 | lock_sock(sk); |
@@ -296,7 +298,7 @@ struct sock *inet_csk_accept(struct sock *sk, int flags, int *err) | |||
296 | goto out_err; | 298 | goto out_err; |
297 | 299 | ||
298 | /* Find already established connection */ | 300 | /* Find already established connection */ |
299 | if (reqsk_queue_empty(&icsk->icsk_accept_queue)) { | 301 | if (reqsk_queue_empty(queue)) { |
300 | long timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK); | 302 | long timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK); |
301 | 303 | ||
302 | /* If this is a non blocking socket don't sleep */ | 304 | /* If this is a non blocking socket don't sleep */ |
@@ -308,14 +310,32 @@ struct sock *inet_csk_accept(struct sock *sk, int flags, int *err) | |||
308 | if (error) | 310 | if (error) |
309 | goto out_err; | 311 | goto out_err; |
310 | } | 312 | } |
311 | 313 | req = reqsk_queue_remove(queue); | |
312 | newsk = reqsk_queue_get_child(&icsk->icsk_accept_queue, sk); | 314 | newsk = req->sk; |
313 | WARN_ON(newsk->sk_state == TCP_SYN_RECV); | 315 | |
316 | sk_acceptq_removed(sk); | ||
317 | if (sk->sk_protocol == IPPROTO_TCP && queue->fastopenq != NULL) { | ||
318 | spin_lock_bh(&queue->fastopenq->lock); | ||
319 | if (tcp_rsk(req)->listener) { | ||
320 | /* We are still waiting for the final ACK from 3WHS | ||
321 | * so can't free req now. Instead, we set req->sk to | ||
322 | * NULL to signify that the child socket is taken | ||
323 | * so reqsk_fastopen_remove() will free the req | ||
324 | * when 3WHS finishes (or is aborted). | ||
325 | */ | ||
326 | req->sk = NULL; | ||
327 | req = NULL; | ||
328 | } | ||
329 | spin_unlock_bh(&queue->fastopenq->lock); | ||
330 | } | ||
314 | out: | 331 | out: |
315 | release_sock(sk); | 332 | release_sock(sk); |
333 | if (req) | ||
334 | __reqsk_free(req); | ||
316 | return newsk; | 335 | return newsk; |
317 | out_err: | 336 | out_err: |
318 | newsk = NULL; | 337 | newsk = NULL; |
338 | req = NULL; | ||
319 | *err = error; | 339 | *err = error; |
320 | goto out; | 340 | goto out; |
321 | } | 341 | } |
@@ -404,12 +424,15 @@ struct dst_entry *inet_csk_route_child_sock(struct sock *sk, | |||
404 | { | 424 | { |
405 | const struct inet_request_sock *ireq = inet_rsk(req); | 425 | const struct inet_request_sock *ireq = inet_rsk(req); |
406 | struct inet_sock *newinet = inet_sk(newsk); | 426 | struct inet_sock *newinet = inet_sk(newsk); |
407 | struct ip_options_rcu *opt = ireq->opt; | 427 | struct ip_options_rcu *opt; |
408 | struct net *net = sock_net(sk); | 428 | struct net *net = sock_net(sk); |
409 | struct flowi4 *fl4; | 429 | struct flowi4 *fl4; |
410 | struct rtable *rt; | 430 | struct rtable *rt; |
411 | 431 | ||
412 | fl4 = &newinet->cork.fl.u.ip4; | 432 | fl4 = &newinet->cork.fl.u.ip4; |
433 | |||
434 | rcu_read_lock(); | ||
435 | opt = rcu_dereference(newinet->inet_opt); | ||
413 | flowi4_init_output(fl4, sk->sk_bound_dev_if, sk->sk_mark, | 436 | flowi4_init_output(fl4, sk->sk_bound_dev_if, sk->sk_mark, |
414 | RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, | 437 | RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, |
415 | sk->sk_protocol, inet_sk_flowi_flags(sk), | 438 | sk->sk_protocol, inet_sk_flowi_flags(sk), |
@@ -421,11 +444,13 @@ struct dst_entry *inet_csk_route_child_sock(struct sock *sk, | |||
421 | goto no_route; | 444 | goto no_route; |
422 | if (opt && opt->opt.is_strictroute && rt->rt_gateway) | 445 | if (opt && opt->opt.is_strictroute && rt->rt_gateway) |
423 | goto route_err; | 446 | goto route_err; |
447 | rcu_read_unlock(); | ||
424 | return &rt->dst; | 448 | return &rt->dst; |
425 | 449 | ||
426 | route_err: | 450 | route_err: |
427 | ip_rt_put(rt); | 451 | ip_rt_put(rt); |
428 | no_route: | 452 | no_route: |
453 | rcu_read_unlock(); | ||
429 | IP_INC_STATS_BH(net, IPSTATS_MIB_OUTNOROUTES); | 454 | IP_INC_STATS_BH(net, IPSTATS_MIB_OUTNOROUTES); |
430 | return NULL; | 455 | return NULL; |
431 | } | 456 | } |
@@ -715,13 +740,14 @@ EXPORT_SYMBOL_GPL(inet_csk_listen_start); | |||
715 | void inet_csk_listen_stop(struct sock *sk) | 740 | void inet_csk_listen_stop(struct sock *sk) |
716 | { | 741 | { |
717 | struct inet_connection_sock *icsk = inet_csk(sk); | 742 | struct inet_connection_sock *icsk = inet_csk(sk); |
743 | struct request_sock_queue *queue = &icsk->icsk_accept_queue; | ||
718 | struct request_sock *acc_req; | 744 | struct request_sock *acc_req; |
719 | struct request_sock *req; | 745 | struct request_sock *req; |
720 | 746 | ||
721 | inet_csk_delete_keepalive_timer(sk); | 747 | inet_csk_delete_keepalive_timer(sk); |
722 | 748 | ||
723 | /* make all the listen_opt local to us */ | 749 | /* make all the listen_opt local to us */ |
724 | acc_req = reqsk_queue_yank_acceptq(&icsk->icsk_accept_queue); | 750 | acc_req = reqsk_queue_yank_acceptq(queue); |
725 | 751 | ||
726 | /* Following specs, it would be better either to send FIN | 752 | /* Following specs, it would be better either to send FIN |
727 | * (and enter FIN-WAIT-1, it is normal close) | 753 | * (and enter FIN-WAIT-1, it is normal close) |
@@ -731,7 +757,7 @@ void inet_csk_listen_stop(struct sock *sk) | |||
731 | * To be honest, we are not able to make either | 757 | * To be honest, we are not able to make either |
732 | * of the variants now. --ANK | 758 | * of the variants now. --ANK |
733 | */ | 759 | */ |
734 | reqsk_queue_destroy(&icsk->icsk_accept_queue); | 760 | reqsk_queue_destroy(queue); |
735 | 761 | ||
736 | while ((req = acc_req) != NULL) { | 762 | while ((req = acc_req) != NULL) { |
737 | struct sock *child = req->sk; | 763 | struct sock *child = req->sk; |
@@ -749,6 +775,19 @@ void inet_csk_listen_stop(struct sock *sk) | |||
749 | 775 | ||
750 | percpu_counter_inc(sk->sk_prot->orphan_count); | 776 | percpu_counter_inc(sk->sk_prot->orphan_count); |
751 | 777 | ||
778 | if (sk->sk_protocol == IPPROTO_TCP && tcp_rsk(req)->listener) { | ||
779 | BUG_ON(tcp_sk(child)->fastopen_rsk != req); | ||
780 | BUG_ON(sk != tcp_rsk(req)->listener); | ||
781 | |||
782 | /* Paranoid, to prevent race condition if | ||
783 | * an inbound pkt destined for child is | ||
784 | * blocked by sock lock in tcp_v4_rcv(). | ||
785 | * Also to satisfy an assertion in | ||
786 | * tcp_v4_destroy_sock(). | ||
787 | */ | ||
788 | tcp_sk(child)->fastopen_rsk = NULL; | ||
789 | sock_put(sk); | ||
790 | } | ||
752 | inet_csk_destroy_sock(child); | 791 | inet_csk_destroy_sock(child); |
753 | 792 | ||
754 | bh_unlock_sock(child); | 793 | bh_unlock_sock(child); |
@@ -758,6 +797,17 @@ void inet_csk_listen_stop(struct sock *sk) | |||
758 | sk_acceptq_removed(sk); | 797 | sk_acceptq_removed(sk); |
759 | __reqsk_free(req); | 798 | __reqsk_free(req); |
760 | } | 799 | } |
800 | if (queue->fastopenq != NULL) { | ||
801 | /* Free all the reqs queued in rskq_rst_head. */ | ||
802 | spin_lock_bh(&queue->fastopenq->lock); | ||
803 | acc_req = queue->fastopenq->rskq_rst_head; | ||
804 | queue->fastopenq->rskq_rst_head = NULL; | ||
805 | spin_unlock_bh(&queue->fastopenq->lock); | ||
806 | while ((req = acc_req) != NULL) { | ||
807 | acc_req = req->dl_next; | ||
808 | __reqsk_free(req); | ||
809 | } | ||
810 | } | ||
761 | WARN_ON(sk->sk_ack_backlog); | 811 | WARN_ON(sk->sk_ack_backlog); |
762 | } | 812 | } |
763 | EXPORT_SYMBOL_GPL(inet_csk_listen_stop); | 813 | EXPORT_SYMBOL_GPL(inet_csk_listen_stop); |