Skip to content

Commit 7f9d68a

Browse files
lxindavem330
authored andcommitted
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>
1 parent 9fb657a commit 7f9d68a

File tree

6 files changed

+149
-10
lines changed

6 files changed

+149
-10
lines changed

include/net/sctp/sctp.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,12 @@ void sctp_remaddr_proc_exit(struct net *net);
193193
*/
194194
int sctp_offload_init(void);
195195

196+
/*
197+
* sctp/stream.c
198+
*/
199+
int sctp_send_reset_streams(struct sctp_association *asoc,
200+
struct sctp_reset_streams *params);
201+
196202
/*
197203
* Module global variables
198204
*/

include/net/sctp/structs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1875,6 +1875,7 @@ struct sctp_association {
18751875
reconf_enable:1;
18761876

18771877
__u8 strreset_enable;
1878+
__u8 strreset_outstanding; /* request param count on the fly */
18781879

18791880
__u32 strreset_outseq; /* Update after receiving response */
18801881
__u32 strreset_inseq; /* Update after receiving request */

include/uapi/linux/sctp.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ typedef __s32 sctp_assoc_t;
116116
#define SCTP_DEFAULT_PRINFO 114
117117
#define SCTP_PR_ASSOC_STATUS 115
118118
#define SCTP_ENABLE_STREAM_RESET 118
119+
#define SCTP_RESET_STREAMS 119
119120

120121
/* PR-SCTP policies */
121122
#define SCTP_PR_SCTP_NONE 0x0000
@@ -145,6 +146,9 @@ typedef __s32 sctp_assoc_t;
145146
#define SCTP_ENABLE_CHANGE_ASSOC_REQ 0x04
146147
#define SCTP_ENABLE_STRRESET_MASK 0x07
147148

149+
#define SCTP_STREAM_RESET_INCOMING 0x01
150+
#define SCTP_STREAM_RESET_OUTGOING 0x02
151+
148152
/* These are bit fields for msghdr->msg_flags. See section 5.1. */
149153
/* On user space Linux, these live in <bits/socket.h> as an enum. */
150154
enum sctp_msg_flags {
@@ -1015,4 +1019,11 @@ struct sctp_info {
10151019
__u32 __reserved3;
10161020
};
10171021

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+
10181029
#endif /* _UAPI_SCTP_H */

net/sctp/outqueue.c

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -915,22 +915,28 @@ static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp)
915915
case SCTP_CID_ECN_ECNE:
916916
case SCTP_CID_ASCONF:
917917
case SCTP_CID_FWD_TSN:
918+
case SCTP_CID_RECONF:
918919
status = sctp_packet_transmit_chunk(packet, chunk,
919920
one_packet, gfp);
920921
if (status != SCTP_XMIT_OK) {
921922
/* put the chunk back */
922923
list_add(&chunk->list, &q->control_chunk_list);
923-
} else {
924-
asoc->stats.octrlchunks++;
925-
/* PR-SCTP C5) If a FORWARD TSN is sent, the
926-
* sender MUST assure that at least one T3-rtx
927-
* timer is running.
928-
*/
929-
if (chunk->chunk_hdr->type == SCTP_CID_FWD_TSN) {
930-
sctp_transport_reset_t3_rtx(transport);
931-
transport->last_time_sent = jiffies;
932-
}
924+
break;
925+
}
926+
927+
asoc->stats.octrlchunks++;
928+
/* PR-SCTP C5) If a FORWARD TSN is sent, the
929+
* sender MUST assure that at least one T3-rtx
930+
* timer is running.
931+
*/
932+
if (chunk->chunk_hdr->type == SCTP_CID_FWD_TSN) {
933+
sctp_transport_reset_t3_rtx(transport);
934+
transport->last_time_sent = jiffies;
933935
}
936+
937+
if (chunk == asoc->strreset_chunk)
938+
sctp_transport_reset_reconf_timer(transport);
939+
934940
break;
935941

936942
default:
@@ -1016,6 +1022,8 @@ static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp)
10161022

10171023
/* Finally, transmit new packets. */
10181024
while ((chunk = sctp_outq_dequeue_data(q)) != NULL) {
1025+
__u32 sid = ntohs(chunk->subh.data_hdr->stream);
1026+
10191027
/* RFC 2960 6.5 Every DATA chunk MUST carry a valid
10201028
* stream identifier.
10211029
*/
@@ -1038,6 +1046,11 @@ static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp)
10381046
continue;
10391047
}
10401048

1049+
if (asoc->stream->out[sid].state == SCTP_STREAM_CLOSED) {
1050+
sctp_outq_head_data(q, chunk);
1051+
goto sctp_flush_out;
1052+
}
1053+
10411054
/* If there is a specified transport, use it.
10421055
* Otherwise, we want to use the active path.
10431056
*/

net/sctp/socket.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3786,6 +3786,32 @@ static int sctp_setsockopt_enable_strreset(struct sock *sk,
37863786
return retval;
37873787
}
37883788

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+
37893815
/* API 6.2 setsockopt(), getsockopt()
37903816
*
37913817
* Applications use setsockopt() and getsockopt() to set or retrieve
@@ -3955,6 +3981,9 @@ static int sctp_setsockopt(struct sock *sk, int level, int optname,
39553981
case SCTP_ENABLE_STREAM_RESET:
39563982
retval = sctp_setsockopt_enable_strreset(sk, optval, optlen);
39573983
break;
3984+
case SCTP_RESET_STREAMS:
3985+
retval = sctp_setsockopt_reset_streams(sk, optval, optlen);
3986+
break;
39583987
default:
39593988
retval = -ENOPROTOOPT;
39603989
break;

net/sctp/stream.c

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
*/
3434

3535
#include <net/sctp/sctp.h>
36+
#include <net/sctp/sm.h>
3637

3738
struct sctp_stream *sctp_stream_new(__u16 incnt, __u16 outcnt, gfp_t gfp)
3839
{
@@ -83,3 +84,81 @@ void sctp_stream_clear(struct sctp_stream *stream)
8384
for (i = 0; i < stream->incnt; i++)
8485
stream->in[i].ssn = 0;
8586
}
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+
}

0 commit comments

Comments
 (0)