diff options
Diffstat (limited to 'net/dccp/proto.c')
-rw-r--r-- | net/dccp/proto.c | 88 |
1 files changed, 58 insertions, 30 deletions
diff --git a/net/dccp/proto.c b/net/dccp/proto.c index 3489d3f21f50..60f40ec72ff3 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c | |||
@@ -71,7 +71,8 @@ void dccp_set_state(struct sock *sk, const int state) | |||
71 | break; | 71 | break; |
72 | 72 | ||
73 | case DCCP_CLOSED: | 73 | case DCCP_CLOSED: |
74 | if (oldstate == DCCP_CLOSING || oldstate == DCCP_OPEN) | 74 | if (oldstate == DCCP_OPEN || oldstate == DCCP_ACTIVE_CLOSEREQ || |
75 | oldstate == DCCP_CLOSING) | ||
75 | DCCP_INC_STATS(DCCP_MIB_ESTABRESETS); | 76 | DCCP_INC_STATS(DCCP_MIB_ESTABRESETS); |
76 | 77 | ||
77 | sk->sk_prot->unhash(sk); | 78 | sk->sk_prot->unhash(sk); |
@@ -92,6 +93,24 @@ void dccp_set_state(struct sock *sk, const int state) | |||
92 | 93 | ||
93 | EXPORT_SYMBOL_GPL(dccp_set_state); | 94 | EXPORT_SYMBOL_GPL(dccp_set_state); |
94 | 95 | ||
96 | static void dccp_finish_passive_close(struct sock *sk) | ||
97 | { | ||
98 | switch (sk->sk_state) { | ||
99 | case DCCP_PASSIVE_CLOSE: | ||
100 | /* Node (client or server) has received Close packet. */ | ||
101 | dccp_send_reset(sk, DCCP_RESET_CODE_CLOSED); | ||
102 | dccp_set_state(sk, DCCP_CLOSED); | ||
103 | break; | ||
104 | case DCCP_PASSIVE_CLOSEREQ: | ||
105 | /* | ||
106 | * Client received CloseReq. We set the `active' flag so that | ||
107 | * dccp_send_close() retransmits the Close as per RFC 4340, 8.3. | ||
108 | */ | ||
109 | dccp_send_close(sk, 1); | ||
110 | dccp_set_state(sk, DCCP_CLOSING); | ||
111 | } | ||
112 | } | ||
113 | |||
95 | void dccp_done(struct sock *sk) | 114 | void dccp_done(struct sock *sk) |
96 | { | 115 | { |
97 | dccp_set_state(sk, DCCP_CLOSED); | 116 | dccp_set_state(sk, DCCP_CLOSED); |
@@ -762,19 +781,26 @@ int dccp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, | |||
762 | 781 | ||
763 | dh = dccp_hdr(skb); | 782 | dh = dccp_hdr(skb); |
764 | 783 | ||
765 | if (dh->dccph_type == DCCP_PKT_DATA || | 784 | switch (dh->dccph_type) { |
766 | dh->dccph_type == DCCP_PKT_DATAACK) | 785 | case DCCP_PKT_DATA: |
786 | case DCCP_PKT_DATAACK: | ||
767 | goto found_ok_skb; | 787 | goto found_ok_skb; |
768 | 788 | ||
769 | if (dh->dccph_type == DCCP_PKT_RESET || | 789 | case DCCP_PKT_CLOSE: |
770 | dh->dccph_type == DCCP_PKT_CLOSE) { | 790 | case DCCP_PKT_CLOSEREQ: |
771 | dccp_pr_debug("found fin ok!\n"); | 791 | if (!(flags & MSG_PEEK)) |
792 | dccp_finish_passive_close(sk); | ||
793 | /* fall through */ | ||
794 | case DCCP_PKT_RESET: | ||
795 | dccp_pr_debug("found fin (%s) ok!\n", | ||
796 | dccp_packet_name(dh->dccph_type)); | ||
772 | len = 0; | 797 | len = 0; |
773 | goto found_fin_ok; | 798 | goto found_fin_ok; |
799 | default: | ||
800 | dccp_pr_debug("packet_type=%s\n", | ||
801 | dccp_packet_name(dh->dccph_type)); | ||
802 | sk_eat_skb(sk, skb, 0); | ||
774 | } | 803 | } |
775 | dccp_pr_debug("packet_type=%s\n", | ||
776 | dccp_packet_name(dh->dccph_type)); | ||
777 | sk_eat_skb(sk, skb, 0); | ||
778 | verify_sock_status: | 804 | verify_sock_status: |
779 | if (sock_flag(sk, SOCK_DONE)) { | 805 | if (sock_flag(sk, SOCK_DONE)) { |
780 | len = 0; | 806 | len = 0; |
@@ -876,28 +902,30 @@ out: | |||
876 | 902 | ||
877 | EXPORT_SYMBOL_GPL(inet_dccp_listen); | 903 | EXPORT_SYMBOL_GPL(inet_dccp_listen); |
878 | 904 | ||
879 | static const unsigned char dccp_new_state[] = { | 905 | static void dccp_terminate_connection(struct sock *sk) |
880 | /* current state: new state: action: */ | ||
881 | [0] = DCCP_CLOSED, | ||
882 | [DCCP_OPEN] = DCCP_CLOSING | DCCP_ACTION_FIN, | ||
883 | [DCCP_REQUESTING] = DCCP_CLOSED, | ||
884 | [DCCP_PARTOPEN] = DCCP_CLOSING | DCCP_ACTION_FIN, | ||
885 | [DCCP_LISTEN] = DCCP_CLOSED, | ||
886 | [DCCP_RESPOND] = DCCP_CLOSED, | ||
887 | [DCCP_CLOSING] = DCCP_CLOSED, | ||
888 | [DCCP_TIME_WAIT] = DCCP_CLOSED, | ||
889 | [DCCP_CLOSED] = DCCP_CLOSED, | ||
890 | }; | ||
891 | |||
892 | static int dccp_close_state(struct sock *sk) | ||
893 | { | 906 | { |
894 | const int next = dccp_new_state[sk->sk_state]; | 907 | u8 next_state = DCCP_CLOSED; |
895 | const int ns = next & DCCP_STATE_MASK; | ||
896 | 908 | ||
897 | if (ns != sk->sk_state) | 909 | switch (sk->sk_state) { |
898 | dccp_set_state(sk, ns); | 910 | case DCCP_PASSIVE_CLOSE: |
911 | case DCCP_PASSIVE_CLOSEREQ: | ||
912 | dccp_finish_passive_close(sk); | ||
913 | break; | ||
914 | case DCCP_PARTOPEN: | ||
915 | dccp_pr_debug("Stop PARTOPEN timer (%p)\n", sk); | ||
916 | inet_csk_clear_xmit_timer(sk, ICSK_TIME_DACK); | ||
917 | /* fall through */ | ||
918 | case DCCP_OPEN: | ||
919 | dccp_send_close(sk, 1); | ||
899 | 920 | ||
900 | return next & DCCP_ACTION_FIN; | 921 | if (dccp_sk(sk)->dccps_role == DCCP_ROLE_SERVER) |
922 | next_state = DCCP_ACTIVE_CLOSEREQ; | ||
923 | else | ||
924 | next_state = DCCP_CLOSING; | ||
925 | /* fall through */ | ||
926 | default: | ||
927 | dccp_set_state(sk, next_state); | ||
928 | } | ||
901 | } | 929 | } |
902 | 930 | ||
903 | void dccp_close(struct sock *sk, long timeout) | 931 | void dccp_close(struct sock *sk, long timeout) |
@@ -940,8 +968,8 @@ void dccp_close(struct sock *sk, long timeout) | |||
940 | } else if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) { | 968 | } else if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) { |
941 | /* Check zero linger _after_ checking for unread data. */ | 969 | /* Check zero linger _after_ checking for unread data. */ |
942 | sk->sk_prot->disconnect(sk, 0); | 970 | sk->sk_prot->disconnect(sk, 0); |
943 | } else if (dccp_close_state(sk)) { | 971 | } else if (sk->sk_state != DCCP_CLOSED) { |
944 | dccp_send_close(sk, 1); | 972 | dccp_terminate_connection(sk); |
945 | } | 973 | } |
946 | 974 | ||
947 | sk_stream_wait_close(sk, timeout); | 975 | sk_stream_wait_close(sk, timeout); |