diff options
author | Xin Long <lucien.xin@gmail.com> | 2017-01-17 11:44:47 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-01-18 14:55:11 -0500 |
commit | 7f9d68ac944e24ee5f9ac8d059ca00b1c1d34137 (patch) | |
tree | 08007191a5e6a018f08dd26919305175b5a14b23 | |
parent | 9fb657aec0e20b4ed4401c44a4140f8d7b7a9ca0 (diff) |
sctp: implement sender-side procedures for SSN Reset Request Parameter
This patch is to implement sender-side procedures for the Outgoing
and Incoming SSN Reset Request Parameter described in rfc6525 section
5.1.2 and 5.1.3.
It is also add sockopt SCTP_RESET_STREAMS in rfc6525 section 6.3.2
for users.
Note that the new asoc member strreset_outstanding is to make sure
only one reconf request chunk on the fly as rfc6525 section 5.1.1
demands.
Signed-off-by: Xin Long <lucien.xin@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/net/sctp/sctp.h | 6 | ||||
-rw-r--r-- | include/net/sctp/structs.h | 1 | ||||
-rw-r--r-- | include/uapi/linux/sctp.h | 11 | ||||
-rw-r--r-- | net/sctp/outqueue.c | 33 | ||||
-rw-r--r-- | net/sctp/socket.c | 29 | ||||
-rw-r--r-- | net/sctp/stream.c | 79 |
6 files changed, 149 insertions, 10 deletions
diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index bc0e049b1474..3cfd365bcfbc 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h | |||
@@ -194,6 +194,12 @@ void sctp_remaddr_proc_exit(struct net *net); | |||
194 | int sctp_offload_init(void); | 194 | int sctp_offload_init(void); |
195 | 195 | ||
196 | /* | 196 | /* |
197 | * sctp/stream.c | ||
198 | */ | ||
199 | int sctp_send_reset_streams(struct sctp_association *asoc, | ||
200 | struct sctp_reset_streams *params); | ||
201 | |||
202 | /* | ||
197 | * Module global variables | 203 | * Module global variables |
198 | */ | 204 | */ |
199 | 205 | ||
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index d99b76e33b2e..231fa9ac50bd 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h | |||
@@ -1875,6 +1875,7 @@ struct sctp_association { | |||
1875 | reconf_enable:1; | 1875 | reconf_enable:1; |
1876 | 1876 | ||
1877 | __u8 strreset_enable; | 1877 | __u8 strreset_enable; |
1878 | __u8 strreset_outstanding; /* request param count on the fly */ | ||
1878 | 1879 | ||
1879 | __u32 strreset_outseq; /* Update after receiving response */ | 1880 | __u32 strreset_outseq; /* Update after receiving response */ |
1880 | __u32 strreset_inseq; /* Update after receiving request */ | 1881 | __u32 strreset_inseq; /* Update after receiving request */ |
diff --git a/include/uapi/linux/sctp.h b/include/uapi/linux/sctp.h index 867be0f32fd7..03c27cefffb1 100644 --- a/include/uapi/linux/sctp.h +++ b/include/uapi/linux/sctp.h | |||
@@ -116,6 +116,7 @@ typedef __s32 sctp_assoc_t; | |||
116 | #define SCTP_DEFAULT_PRINFO 114 | 116 | #define SCTP_DEFAULT_PRINFO 114 |
117 | #define SCTP_PR_ASSOC_STATUS 115 | 117 | #define SCTP_PR_ASSOC_STATUS 115 |
118 | #define SCTP_ENABLE_STREAM_RESET 118 | 118 | #define SCTP_ENABLE_STREAM_RESET 118 |
119 | #define SCTP_RESET_STREAMS 119 | ||
119 | 120 | ||
120 | /* PR-SCTP policies */ | 121 | /* PR-SCTP policies */ |
121 | #define SCTP_PR_SCTP_NONE 0x0000 | 122 | #define SCTP_PR_SCTP_NONE 0x0000 |
@@ -145,6 +146,9 @@ typedef __s32 sctp_assoc_t; | |||
145 | #define SCTP_ENABLE_CHANGE_ASSOC_REQ 0x04 | 146 | #define SCTP_ENABLE_CHANGE_ASSOC_REQ 0x04 |
146 | #define SCTP_ENABLE_STRRESET_MASK 0x07 | 147 | #define SCTP_ENABLE_STRRESET_MASK 0x07 |
147 | 148 | ||
149 | #define SCTP_STREAM_RESET_INCOMING 0x01 | ||
150 | #define SCTP_STREAM_RESET_OUTGOING 0x02 | ||
151 | |||
148 | /* These are bit fields for msghdr->msg_flags. See section 5.1. */ | 152 | /* These are bit fields for msghdr->msg_flags. See section 5.1. */ |
149 | /* On user space Linux, these live in <bits/socket.h> as an enum. */ | 153 | /* On user space Linux, these live in <bits/socket.h> as an enum. */ |
150 | enum sctp_msg_flags { | 154 | enum sctp_msg_flags { |
@@ -1015,4 +1019,11 @@ struct sctp_info { | |||
1015 | __u32 __reserved3; | 1019 | __u32 __reserved3; |
1016 | }; | 1020 | }; |
1017 | 1021 | ||
1022 | struct sctp_reset_streams { | ||
1023 | sctp_assoc_t srs_assoc_id; | ||
1024 | uint16_t srs_flags; | ||
1025 | uint16_t srs_number_streams; /* 0 == ALL */ | ||
1026 | uint16_t srs_stream_list[]; /* list if srs_num_streams is not 0 */ | ||
1027 | }; | ||
1028 | |||
1018 | #endif /* _UAPI_SCTP_H */ | 1029 | #endif /* _UAPI_SCTP_H */ |
diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index 34efaa4ef2f6..65abe22d8691 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c | |||
@@ -915,22 +915,28 @@ static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp) | |||
915 | case SCTP_CID_ECN_ECNE: | 915 | case SCTP_CID_ECN_ECNE: |
916 | case SCTP_CID_ASCONF: | 916 | case SCTP_CID_ASCONF: |
917 | case SCTP_CID_FWD_TSN: | 917 | case SCTP_CID_FWD_TSN: |
918 | case SCTP_CID_RECONF: | ||
918 | status = sctp_packet_transmit_chunk(packet, chunk, | 919 | status = sctp_packet_transmit_chunk(packet, chunk, |
919 | one_packet, gfp); | 920 | one_packet, gfp); |
920 | if (status != SCTP_XMIT_OK) { | 921 | if (status != SCTP_XMIT_OK) { |
921 | /* put the chunk back */ | 922 | /* put the chunk back */ |
922 | list_add(&chunk->list, &q->control_chunk_list); | 923 | list_add(&chunk->list, &q->control_chunk_list); |
923 | } else { | 924 | break; |
924 | asoc->stats.octrlchunks++; | 925 | } |
925 | /* PR-SCTP C5) If a FORWARD TSN is sent, the | 926 | |
926 | * sender MUST assure that at least one T3-rtx | 927 | asoc->stats.octrlchunks++; |
927 | * timer is running. | 928 | /* PR-SCTP C5) If a FORWARD TSN is sent, the |
928 | */ | 929 | * sender MUST assure that at least one T3-rtx |
929 | if (chunk->chunk_hdr->type == SCTP_CID_FWD_TSN) { | 930 | * timer is running. |
930 | sctp_transport_reset_t3_rtx(transport); | 931 | */ |
931 | transport->last_time_sent = jiffies; | 932 | if (chunk->chunk_hdr->type == SCTP_CID_FWD_TSN) { |
932 | } | 933 | sctp_transport_reset_t3_rtx(transport); |
934 | transport->last_time_sent = jiffies; | ||
933 | } | 935 | } |
936 | |||
937 | if (chunk == asoc->strreset_chunk) | ||
938 | sctp_transport_reset_reconf_timer(transport); | ||
939 | |||
934 | break; | 940 | break; |
935 | 941 | ||
936 | default: | 942 | default: |
@@ -1016,6 +1022,8 @@ static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp) | |||
1016 | 1022 | ||
1017 | /* Finally, transmit new packets. */ | 1023 | /* Finally, transmit new packets. */ |
1018 | while ((chunk = sctp_outq_dequeue_data(q)) != NULL) { | 1024 | while ((chunk = sctp_outq_dequeue_data(q)) != NULL) { |
1025 | __u32 sid = ntohs(chunk->subh.data_hdr->stream); | ||
1026 | |||
1019 | /* RFC 2960 6.5 Every DATA chunk MUST carry a valid | 1027 | /* RFC 2960 6.5 Every DATA chunk MUST carry a valid |
1020 | * stream identifier. | 1028 | * stream identifier. |
1021 | */ | 1029 | */ |
@@ -1038,6 +1046,11 @@ static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp) | |||
1038 | continue; | 1046 | continue; |
1039 | } | 1047 | } |
1040 | 1048 | ||
1049 | if (asoc->stream->out[sid].state == SCTP_STREAM_CLOSED) { | ||
1050 | sctp_outq_head_data(q, chunk); | ||
1051 | goto sctp_flush_out; | ||
1052 | } | ||
1053 | |||
1041 | /* If there is a specified transport, use it. | 1054 | /* If there is a specified transport, use it. |
1042 | * Otherwise, we want to use the active path. | 1055 | * Otherwise, we want to use the active path. |
1043 | */ | 1056 | */ |
diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 0a9bc984b6c8..bee4dd3feabb 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c | |||
@@ -3786,6 +3786,32 @@ out: | |||
3786 | return retval; | 3786 | return retval; |
3787 | } | 3787 | } |
3788 | 3788 | ||
3789 | static int sctp_setsockopt_reset_streams(struct sock *sk, | ||
3790 | char __user *optval, | ||
3791 | unsigned int optlen) | ||
3792 | { | ||
3793 | struct sctp_reset_streams *params; | ||
3794 | struct sctp_association *asoc; | ||
3795 | int retval = -EINVAL; | ||
3796 | |||
3797 | if (optlen < sizeof(struct sctp_reset_streams)) | ||
3798 | return -EINVAL; | ||
3799 | |||
3800 | params = memdup_user(optval, optlen); | ||
3801 | if (IS_ERR(params)) | ||
3802 | return PTR_ERR(params); | ||
3803 | |||
3804 | asoc = sctp_id2assoc(sk, params->srs_assoc_id); | ||
3805 | if (!asoc) | ||
3806 | goto out; | ||
3807 | |||
3808 | retval = sctp_send_reset_streams(asoc, params); | ||
3809 | |||
3810 | out: | ||
3811 | kfree(params); | ||
3812 | return retval; | ||
3813 | } | ||
3814 | |||
3789 | /* API 6.2 setsockopt(), getsockopt() | 3815 | /* API 6.2 setsockopt(), getsockopt() |
3790 | * | 3816 | * |
3791 | * Applications use setsockopt() and getsockopt() to set or retrieve | 3817 | * Applications use setsockopt() and getsockopt() to set or retrieve |
@@ -3955,6 +3981,9 @@ static int sctp_setsockopt(struct sock *sk, int level, int optname, | |||
3955 | case SCTP_ENABLE_STREAM_RESET: | 3981 | case SCTP_ENABLE_STREAM_RESET: |
3956 | retval = sctp_setsockopt_enable_strreset(sk, optval, optlen); | 3982 | retval = sctp_setsockopt_enable_strreset(sk, optval, optlen); |
3957 | break; | 3983 | break; |
3984 | case SCTP_RESET_STREAMS: | ||
3985 | retval = sctp_setsockopt_reset_streams(sk, optval, optlen); | ||
3986 | break; | ||
3958 | default: | 3987 | default: |
3959 | retval = -ENOPROTOOPT; | 3988 | retval = -ENOPROTOOPT; |
3960 | break; | 3989 | break; |
diff --git a/net/sctp/stream.c b/net/sctp/stream.c index f86de43cbbe5..13d5e07dcd7d 100644 --- a/net/sctp/stream.c +++ b/net/sctp/stream.c | |||
@@ -33,6 +33,7 @@ | |||
33 | */ | 33 | */ |
34 | 34 | ||
35 | #include <net/sctp/sctp.h> | 35 | #include <net/sctp/sctp.h> |
36 | #include <net/sctp/sm.h> | ||
36 | 37 | ||
37 | struct sctp_stream *sctp_stream_new(__u16 incnt, __u16 outcnt, gfp_t gfp) | 38 | struct sctp_stream *sctp_stream_new(__u16 incnt, __u16 outcnt, gfp_t gfp) |
38 | { | 39 | { |
@@ -83,3 +84,81 @@ void sctp_stream_clear(struct sctp_stream *stream) | |||
83 | for (i = 0; i < stream->incnt; i++) | 84 | for (i = 0; i < stream->incnt; i++) |
84 | stream->in[i].ssn = 0; | 85 | stream->in[i].ssn = 0; |
85 | } | 86 | } |
87 | |||
88 | static int sctp_send_reconf(struct sctp_association *asoc, | ||
89 | struct sctp_chunk *chunk) | ||
90 | { | ||
91 | struct net *net = sock_net(asoc->base.sk); | ||
92 | int retval = 0; | ||
93 | |||
94 | retval = sctp_primitive_RECONF(net, asoc, chunk); | ||
95 | if (retval) | ||
96 | sctp_chunk_free(chunk); | ||
97 | |||
98 | return retval; | ||
99 | } | ||
100 | |||
101 | int sctp_send_reset_streams(struct sctp_association *asoc, | ||
102 | struct sctp_reset_streams *params) | ||
103 | { | ||
104 | struct sctp_stream *stream = asoc->stream; | ||
105 | __u16 i, str_nums, *str_list; | ||
106 | struct sctp_chunk *chunk; | ||
107 | int retval = -EINVAL; | ||
108 | bool out, in; | ||
109 | |||
110 | if (!asoc->peer.reconf_capable || | ||
111 | !(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ)) { | ||
112 | retval = -ENOPROTOOPT; | ||
113 | goto out; | ||
114 | } | ||
115 | |||
116 | if (asoc->strreset_outstanding) { | ||
117 | retval = -EINPROGRESS; | ||
118 | goto out; | ||
119 | } | ||
120 | |||
121 | out = params->srs_flags & SCTP_STREAM_RESET_OUTGOING; | ||
122 | in = params->srs_flags & SCTP_STREAM_RESET_INCOMING; | ||
123 | if (!out && !in) | ||
124 | goto out; | ||
125 | |||
126 | str_nums = params->srs_number_streams; | ||
127 | str_list = params->srs_stream_list; | ||
128 | if (out && str_nums) | ||
129 | for (i = 0; i < str_nums; i++) | ||
130 | if (str_list[i] >= stream->outcnt) | ||
131 | goto out; | ||
132 | |||
133 | if (in && str_nums) | ||
134 | for (i = 0; i < str_nums; i++) | ||
135 | if (str_list[i] >= stream->incnt) | ||
136 | goto out; | ||
137 | |||
138 | chunk = sctp_make_strreset_req(asoc, str_nums, str_list, out, in); | ||
139 | if (!chunk) | ||
140 | goto out; | ||
141 | |||
142 | if (out) { | ||
143 | if (str_nums) | ||
144 | for (i = 0; i < str_nums; i++) | ||
145 | stream->out[str_list[i]].state = | ||
146 | SCTP_STREAM_CLOSED; | ||
147 | else | ||
148 | for (i = 0; i < stream->outcnt; i++) | ||
149 | stream->out[i].state = SCTP_STREAM_CLOSED; | ||
150 | } | ||
151 | |||
152 | asoc->strreset_outstanding = out + in; | ||
153 | asoc->strreset_chunk = chunk; | ||
154 | sctp_chunk_hold(asoc->strreset_chunk); | ||
155 | |||
156 | retval = sctp_send_reconf(asoc, chunk); | ||
157 | if (retval) { | ||
158 | sctp_chunk_put(asoc->strreset_chunk); | ||
159 | asoc->strreset_chunk = NULL; | ||
160 | } | ||
161 | |||
162 | out: | ||
163 | return retval; | ||
164 | } | ||