blob: cd20638b6151471dd3abec7e175fb84631383885 [file] [log] [blame]
David Brazdil0f672f62019-12-10 10:32:29 +00001// SPDX-License-Identifier: GPL-2.0-or-later
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002/* SCTP kernel implementation
3 * (C) Copyright IBM Corp. 2001, 2004
4 * Copyright (c) 1999-2000 Cisco, Inc.
5 * Copyright (c) 1999-2001 Motorola, Inc.
6 * Copyright (c) 2001 Intel Corp.
7 *
8 * This file is part of the SCTP kernel implementation
9 *
10 * This file contains sctp stream maniuplation primitives and helpers.
11 *
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000012 * Please send any bug reports or fixes you make to the
13 * email address(es):
14 * lksctp developers <linux-sctp@vger.kernel.org>
15 *
16 * Written or modified by:
17 * Xin Long <lucien.xin@gmail.com>
18 */
19
20#include <linux/list.h>
21#include <net/sctp/sctp.h>
22#include <net/sctp/sm.h>
23#include <net/sctp/stream_sched.h>
24
Olivier Deprez0e641232021-09-23 10:07:05 +020025static void sctp_stream_shrink_out(struct sctp_stream *stream, __u16 outcnt)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000026{
27 struct sctp_association *asoc;
28 struct sctp_chunk *ch, *temp;
29 struct sctp_outq *outq;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000030
31 asoc = container_of(stream, struct sctp_association, stream);
32 outq = &asoc->outqueue;
33
34 list_for_each_entry_safe(ch, temp, &outq->out_chunk_list, list) {
35 __u16 sid = sctp_chunk_stream_no(ch);
36
37 if (sid < outcnt)
38 continue;
39
40 sctp_sched_dequeue_common(outq, ch);
41 /* No need to call dequeue_done here because
42 * the chunks are not scheduled by now.
43 */
44
45 /* Mark as failed send. */
46 sctp_chunk_fail(ch, (__force __u32)SCTP_ERROR_INV_STRM);
47 if (asoc->peer.prsctp_capable &&
48 SCTP_PR_PRIO_ENABLED(ch->sinfo.sinfo_flags))
49 asoc->sent_cnt_removable--;
50
51 sctp_chunk_free(ch);
52 }
Olivier Deprez0e641232021-09-23 10:07:05 +020053}
54
55/* Migrates chunks from stream queues to new stream queues if needed,
56 * but not across associations. Also, removes those chunks to streams
57 * higher than the new max.
58 */
59static void sctp_stream_outq_migrate(struct sctp_stream *stream,
60 struct sctp_stream *new, __u16 outcnt)
61{
62 int i;
63
64 if (stream->outcnt > outcnt)
65 sctp_stream_shrink_out(stream, outcnt);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000066
67 if (new) {
68 /* Here we actually move the old ext stuff into the new
69 * buffer, because we want to keep it. Then
70 * sctp_stream_update will swap ->out pointers.
71 */
72 for (i = 0; i < outcnt; i++) {
73 kfree(SCTP_SO(new, i)->ext);
74 SCTP_SO(new, i)->ext = SCTP_SO(stream, i)->ext;
75 SCTP_SO(stream, i)->ext = NULL;
76 }
77 }
78
David Brazdil0f672f62019-12-10 10:32:29 +000079 for (i = outcnt; i < stream->outcnt; i++) {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000080 kfree(SCTP_SO(stream, i)->ext);
David Brazdil0f672f62019-12-10 10:32:29 +000081 SCTP_SO(stream, i)->ext = NULL;
82 }
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000083}
84
85static int sctp_stream_alloc_out(struct sctp_stream *stream, __u16 outcnt,
86 gfp_t gfp)
87{
David Brazdil0f672f62019-12-10 10:32:29 +000088 int ret;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000089
David Brazdil0f672f62019-12-10 10:32:29 +000090 if (outcnt <= stream->outcnt)
Olivier Deprez0e641232021-09-23 10:07:05 +020091 goto out;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000092
David Brazdil0f672f62019-12-10 10:32:29 +000093 ret = genradix_prealloc(&stream->out, outcnt, gfp);
94 if (ret)
95 return ret;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000096
Olivier Deprez0e641232021-09-23 10:07:05 +020097out:
David Brazdil0f672f62019-12-10 10:32:29 +000098 stream->outcnt = outcnt;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000099 return 0;
100}
101
102static int sctp_stream_alloc_in(struct sctp_stream *stream, __u16 incnt,
103 gfp_t gfp)
104{
David Brazdil0f672f62019-12-10 10:32:29 +0000105 int ret;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000106
David Brazdil0f672f62019-12-10 10:32:29 +0000107 if (incnt <= stream->incnt)
Olivier Deprez0e641232021-09-23 10:07:05 +0200108 goto out;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000109
David Brazdil0f672f62019-12-10 10:32:29 +0000110 ret = genradix_prealloc(&stream->in, incnt, gfp);
111 if (ret)
112 return ret;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000113
Olivier Deprez0e641232021-09-23 10:07:05 +0200114out:
David Brazdil0f672f62019-12-10 10:32:29 +0000115 stream->incnt = incnt;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000116 return 0;
117}
118
119int sctp_stream_init(struct sctp_stream *stream, __u16 outcnt, __u16 incnt,
120 gfp_t gfp)
121{
122 struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream);
123 int i, ret = 0;
124
125 gfp |= __GFP_NOWARN;
126
127 /* Initial stream->out size may be very big, so free it and alloc
128 * a new one with new outcnt to save memory if needed.
129 */
130 if (outcnt == stream->outcnt)
Olivier Deprez0e641232021-09-23 10:07:05 +0200131 goto handle_in;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000132
133 /* Filter out chunks queued on streams that won't exist anymore */
134 sched->unsched_all(stream);
135 sctp_stream_outq_migrate(stream, NULL, outcnt);
136 sched->sched_all(stream);
137
138 ret = sctp_stream_alloc_out(stream, outcnt, gfp);
139 if (ret)
Olivier Deprez0e641232021-09-23 10:07:05 +0200140 goto out_err;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000141
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000142 for (i = 0; i < stream->outcnt; i++)
143 SCTP_SO(stream, i)->state = SCTP_STREAM_OPEN;
144
Olivier Deprez0e641232021-09-23 10:07:05 +0200145handle_in:
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000146 sctp_stream_interleave_init(stream);
147 if (!incnt)
148 goto out;
149
150 ret = sctp_stream_alloc_in(stream, incnt, gfp);
Olivier Deprez0e641232021-09-23 10:07:05 +0200151 if (ret)
152 goto in_err;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000153
Olivier Deprez0e641232021-09-23 10:07:05 +0200154 goto out;
155
156in_err:
157 sched->free(stream);
158 genradix_free(&stream->in);
159out_err:
160 genradix_free(&stream->out);
161 stream->outcnt = 0;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000162out:
163 return ret;
164}
165
166int sctp_stream_init_ext(struct sctp_stream *stream, __u16 sid)
167{
168 struct sctp_stream_out_ext *soute;
David Brazdil0f672f62019-12-10 10:32:29 +0000169 int ret;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000170
171 soute = kzalloc(sizeof(*soute), GFP_KERNEL);
172 if (!soute)
173 return -ENOMEM;
174 SCTP_SO(stream, sid)->ext = soute;
175
David Brazdil0f672f62019-12-10 10:32:29 +0000176 ret = sctp_sched_init_sid(stream, sid, GFP_KERNEL);
177 if (ret) {
178 kfree(SCTP_SO(stream, sid)->ext);
179 SCTP_SO(stream, sid)->ext = NULL;
180 }
181
182 return ret;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000183}
184
185void sctp_stream_free(struct sctp_stream *stream)
186{
187 struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream);
188 int i;
189
190 sched->free(stream);
191 for (i = 0; i < stream->outcnt; i++)
192 kfree(SCTP_SO(stream, i)->ext);
David Brazdil0f672f62019-12-10 10:32:29 +0000193 genradix_free(&stream->out);
194 genradix_free(&stream->in);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000195}
196
197void sctp_stream_clear(struct sctp_stream *stream)
198{
199 int i;
200
201 for (i = 0; i < stream->outcnt; i++) {
202 SCTP_SO(stream, i)->mid = 0;
203 SCTP_SO(stream, i)->mid_uo = 0;
204 }
205
206 for (i = 0; i < stream->incnt; i++)
207 SCTP_SI(stream, i)->mid = 0;
208}
209
210void sctp_stream_update(struct sctp_stream *stream, struct sctp_stream *new)
211{
212 struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream);
213
214 sched->unsched_all(stream);
215 sctp_stream_outq_migrate(stream, new, new->outcnt);
216 sctp_stream_free(stream);
217
218 stream->out = new->out;
219 stream->in = new->in;
220 stream->outcnt = new->outcnt;
221 stream->incnt = new->incnt;
222
223 sched->sched_all(stream);
224
David Brazdil0f672f62019-12-10 10:32:29 +0000225 new->out.tree.root = NULL;
226 new->in.tree.root = NULL;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000227 new->outcnt = 0;
228 new->incnt = 0;
229}
230
231static int sctp_send_reconf(struct sctp_association *asoc,
232 struct sctp_chunk *chunk)
233{
234 struct net *net = sock_net(asoc->base.sk);
235 int retval = 0;
236
237 retval = sctp_primitive_RECONF(net, asoc, chunk);
238 if (retval)
239 sctp_chunk_free(chunk);
240
241 return retval;
242}
243
244static bool sctp_stream_outq_is_empty(struct sctp_stream *stream,
245 __u16 str_nums, __be16 *str_list)
246{
247 struct sctp_association *asoc;
248 __u16 i;
249
250 asoc = container_of(stream, struct sctp_association, stream);
251 if (!asoc->outqueue.out_qlen)
252 return true;
253
254 if (!str_nums)
255 return false;
256
257 for (i = 0; i < str_nums; i++) {
258 __u16 sid = ntohs(str_list[i]);
259
260 if (SCTP_SO(stream, sid)->ext &&
261 !list_empty(&SCTP_SO(stream, sid)->ext->outq))
262 return false;
263 }
264
265 return true;
266}
267
268int sctp_send_reset_streams(struct sctp_association *asoc,
269 struct sctp_reset_streams *params)
270{
271 struct sctp_stream *stream = &asoc->stream;
272 __u16 i, str_nums, *str_list;
273 struct sctp_chunk *chunk;
274 int retval = -EINVAL;
275 __be16 *nstr_list;
276 bool out, in;
277
278 if (!asoc->peer.reconf_capable ||
279 !(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ)) {
280 retval = -ENOPROTOOPT;
281 goto out;
282 }
283
284 if (asoc->strreset_outstanding) {
285 retval = -EINPROGRESS;
286 goto out;
287 }
288
289 out = params->srs_flags & SCTP_STREAM_RESET_OUTGOING;
290 in = params->srs_flags & SCTP_STREAM_RESET_INCOMING;
291 if (!out && !in)
292 goto out;
293
294 str_nums = params->srs_number_streams;
295 str_list = params->srs_stream_list;
296 if (str_nums) {
297 int param_len = 0;
298
299 if (out) {
300 for (i = 0; i < str_nums; i++)
301 if (str_list[i] >= stream->outcnt)
302 goto out;
303
304 param_len = str_nums * sizeof(__u16) +
305 sizeof(struct sctp_strreset_outreq);
306 }
307
308 if (in) {
309 for (i = 0; i < str_nums; i++)
310 if (str_list[i] >= stream->incnt)
311 goto out;
312
313 param_len += str_nums * sizeof(__u16) +
314 sizeof(struct sctp_strreset_inreq);
315 }
316
317 if (param_len > SCTP_MAX_CHUNK_LEN -
318 sizeof(struct sctp_reconf_chunk))
319 goto out;
320 }
321
322 nstr_list = kcalloc(str_nums, sizeof(__be16), GFP_KERNEL);
323 if (!nstr_list) {
324 retval = -ENOMEM;
325 goto out;
326 }
327
328 for (i = 0; i < str_nums; i++)
329 nstr_list[i] = htons(str_list[i]);
330
331 if (out && !sctp_stream_outq_is_empty(stream, str_nums, nstr_list)) {
David Brazdil0f672f62019-12-10 10:32:29 +0000332 kfree(nstr_list);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000333 retval = -EAGAIN;
334 goto out;
335 }
336
337 chunk = sctp_make_strreset_req(asoc, str_nums, nstr_list, out, in);
338
339 kfree(nstr_list);
340
341 if (!chunk) {
342 retval = -ENOMEM;
343 goto out;
344 }
345
346 if (out) {
347 if (str_nums)
348 for (i = 0; i < str_nums; i++)
349 SCTP_SO(stream, str_list[i])->state =
350 SCTP_STREAM_CLOSED;
351 else
352 for (i = 0; i < stream->outcnt; i++)
353 SCTP_SO(stream, i)->state = SCTP_STREAM_CLOSED;
354 }
355
356 asoc->strreset_chunk = chunk;
357 sctp_chunk_hold(asoc->strreset_chunk);
358
359 retval = sctp_send_reconf(asoc, chunk);
360 if (retval) {
361 sctp_chunk_put(asoc->strreset_chunk);
362 asoc->strreset_chunk = NULL;
363 if (!out)
364 goto out;
365
366 if (str_nums)
367 for (i = 0; i < str_nums; i++)
368 SCTP_SO(stream, str_list[i])->state =
369 SCTP_STREAM_OPEN;
370 else
371 for (i = 0; i < stream->outcnt; i++)
372 SCTP_SO(stream, i)->state = SCTP_STREAM_OPEN;
373
374 goto out;
375 }
376
377 asoc->strreset_outstanding = out + in;
378
379out:
380 return retval;
381}
382
383int sctp_send_reset_assoc(struct sctp_association *asoc)
384{
385 struct sctp_stream *stream = &asoc->stream;
386 struct sctp_chunk *chunk = NULL;
387 int retval;
388 __u16 i;
389
390 if (!asoc->peer.reconf_capable ||
391 !(asoc->strreset_enable & SCTP_ENABLE_RESET_ASSOC_REQ))
392 return -ENOPROTOOPT;
393
394 if (asoc->strreset_outstanding)
395 return -EINPROGRESS;
396
397 if (!sctp_outq_is_empty(&asoc->outqueue))
398 return -EAGAIN;
399
400 chunk = sctp_make_strreset_tsnreq(asoc);
401 if (!chunk)
402 return -ENOMEM;
403
404 /* Block further xmit of data until this request is completed */
405 for (i = 0; i < stream->outcnt; i++)
406 SCTP_SO(stream, i)->state = SCTP_STREAM_CLOSED;
407
408 asoc->strreset_chunk = chunk;
409 sctp_chunk_hold(asoc->strreset_chunk);
410
411 retval = sctp_send_reconf(asoc, chunk);
412 if (retval) {
413 sctp_chunk_put(asoc->strreset_chunk);
414 asoc->strreset_chunk = NULL;
415
416 for (i = 0; i < stream->outcnt; i++)
417 SCTP_SO(stream, i)->state = SCTP_STREAM_OPEN;
418
419 return retval;
420 }
421
422 asoc->strreset_outstanding = 1;
423
424 return 0;
425}
426
427int sctp_send_add_streams(struct sctp_association *asoc,
428 struct sctp_add_streams *params)
429{
430 struct sctp_stream *stream = &asoc->stream;
431 struct sctp_chunk *chunk = NULL;
432 int retval;
433 __u32 outcnt, incnt;
434 __u16 out, in;
435
436 if (!asoc->peer.reconf_capable ||
437 !(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ)) {
438 retval = -ENOPROTOOPT;
439 goto out;
440 }
441
442 if (asoc->strreset_outstanding) {
443 retval = -EINPROGRESS;
444 goto out;
445 }
446
447 out = params->sas_outstrms;
448 in = params->sas_instrms;
449 outcnt = stream->outcnt + out;
450 incnt = stream->incnt + in;
451 if (outcnt > SCTP_MAX_STREAM || incnt > SCTP_MAX_STREAM ||
452 (!out && !in)) {
453 retval = -EINVAL;
454 goto out;
455 }
456
457 if (out) {
458 retval = sctp_stream_alloc_out(stream, outcnt, GFP_KERNEL);
459 if (retval)
460 goto out;
461 }
462
463 chunk = sctp_make_strreset_addstrm(asoc, out, in);
464 if (!chunk) {
465 retval = -ENOMEM;
466 goto out;
467 }
468
469 asoc->strreset_chunk = chunk;
470 sctp_chunk_hold(asoc->strreset_chunk);
471
472 retval = sctp_send_reconf(asoc, chunk);
473 if (retval) {
474 sctp_chunk_put(asoc->strreset_chunk);
475 asoc->strreset_chunk = NULL;
476 goto out;
477 }
478
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000479 asoc->strreset_outstanding = !!out + !!in;
480
481out:
482 return retval;
483}
484
485static struct sctp_paramhdr *sctp_chunk_lookup_strreset_param(
486 struct sctp_association *asoc, __be32 resp_seq,
487 __be16 type)
488{
489 struct sctp_chunk *chunk = asoc->strreset_chunk;
490 struct sctp_reconf_chunk *hdr;
491 union sctp_params param;
492
493 if (!chunk)
494 return NULL;
495
496 hdr = (struct sctp_reconf_chunk *)chunk->chunk_hdr;
497 sctp_walk_params(param, hdr, params) {
498 /* sctp_strreset_tsnreq is actually the basic structure
499 * of all stream reconf params, so it's safe to use it
500 * to access request_seq.
501 */
502 struct sctp_strreset_tsnreq *req = param.v;
503
504 if ((!resp_seq || req->request_seq == resp_seq) &&
505 (!type || type == req->param_hdr.type))
506 return param.v;
507 }
508
509 return NULL;
510}
511
512static void sctp_update_strreset_result(struct sctp_association *asoc,
513 __u32 result)
514{
515 asoc->strreset_result[1] = asoc->strreset_result[0];
516 asoc->strreset_result[0] = result;
517}
518
519struct sctp_chunk *sctp_process_strreset_outreq(
520 struct sctp_association *asoc,
521 union sctp_params param,
522 struct sctp_ulpevent **evp)
523{
524 struct sctp_strreset_outreq *outreq = param.v;
525 struct sctp_stream *stream = &asoc->stream;
526 __u32 result = SCTP_STRRESET_DENIED;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000527 __be16 *str_p = NULL;
528 __u32 request_seq;
David Brazdil0f672f62019-12-10 10:32:29 +0000529 __u16 i, nums;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000530
531 request_seq = ntohl(outreq->request_seq);
532
533 if (ntohl(outreq->send_reset_at_tsn) >
534 sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map)) {
535 result = SCTP_STRRESET_IN_PROGRESS;
536 goto err;
537 }
538
539 if (TSN_lt(asoc->strreset_inseq, request_seq) ||
540 TSN_lt(request_seq, asoc->strreset_inseq - 2)) {
541 result = SCTP_STRRESET_ERR_BAD_SEQNO;
542 goto err;
543 } else if (TSN_lt(request_seq, asoc->strreset_inseq)) {
544 i = asoc->strreset_inseq - request_seq - 1;
545 result = asoc->strreset_result[i];
546 goto err;
547 }
548 asoc->strreset_inseq++;
549
550 /* Check strreset_enable after inseq inc, as sender cannot tell
551 * the peer doesn't enable strreset after receiving response with
552 * result denied, as well as to keep consistent with bsd.
553 */
554 if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ))
555 goto out;
556
David Brazdil0f672f62019-12-10 10:32:29 +0000557 nums = (ntohs(param.p->length) - sizeof(*outreq)) / sizeof(__u16);
558 str_p = outreq->list_of_streams;
559 for (i = 0; i < nums; i++) {
560 if (ntohs(str_p[i]) >= stream->incnt) {
561 result = SCTP_STRRESET_ERR_WRONG_SSN;
562 goto out;
563 }
564 }
565
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000566 if (asoc->strreset_chunk) {
567 if (!sctp_chunk_lookup_strreset_param(
568 asoc, outreq->response_seq,
569 SCTP_PARAM_RESET_IN_REQUEST)) {
570 /* same process with outstanding isn't 0 */
571 result = SCTP_STRRESET_ERR_IN_PROGRESS;
572 goto out;
573 }
574
575 asoc->strreset_outstanding--;
576 asoc->strreset_outseq++;
577
578 if (!asoc->strreset_outstanding) {
579 struct sctp_transport *t;
580
581 t = asoc->strreset_chunk->transport;
582 if (del_timer(&t->reconf_timer))
583 sctp_transport_put(t);
584
585 sctp_chunk_put(asoc->strreset_chunk);
586 asoc->strreset_chunk = NULL;
587 }
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000588 }
589
David Brazdil0f672f62019-12-10 10:32:29 +0000590 if (nums)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000591 for (i = 0; i < nums; i++)
592 SCTP_SI(stream, ntohs(str_p[i]))->mid = 0;
David Brazdil0f672f62019-12-10 10:32:29 +0000593 else
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000594 for (i = 0; i < stream->incnt; i++)
595 SCTP_SI(stream, i)->mid = 0;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000596
597 result = SCTP_STRRESET_PERFORMED;
598
599 *evp = sctp_ulpevent_make_stream_reset_event(asoc,
David Brazdil0f672f62019-12-10 10:32:29 +0000600 SCTP_STREAM_RESET_INCOMING_SSN, nums, str_p, GFP_ATOMIC);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000601
602out:
603 sctp_update_strreset_result(asoc, result);
604err:
605 return sctp_make_strreset_resp(asoc, result, request_seq);
606}
607
608struct sctp_chunk *sctp_process_strreset_inreq(
609 struct sctp_association *asoc,
610 union sctp_params param,
611 struct sctp_ulpevent **evp)
612{
613 struct sctp_strreset_inreq *inreq = param.v;
614 struct sctp_stream *stream = &asoc->stream;
615 __u32 result = SCTP_STRRESET_DENIED;
616 struct sctp_chunk *chunk = NULL;
617 __u32 request_seq;
618 __u16 i, nums;
619 __be16 *str_p;
620
621 request_seq = ntohl(inreq->request_seq);
622 if (TSN_lt(asoc->strreset_inseq, request_seq) ||
623 TSN_lt(request_seq, asoc->strreset_inseq - 2)) {
624 result = SCTP_STRRESET_ERR_BAD_SEQNO;
625 goto err;
626 } else if (TSN_lt(request_seq, asoc->strreset_inseq)) {
627 i = asoc->strreset_inseq - request_seq - 1;
628 result = asoc->strreset_result[i];
629 if (result == SCTP_STRRESET_PERFORMED)
630 return NULL;
631 goto err;
632 }
633 asoc->strreset_inseq++;
634
635 if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ))
636 goto out;
637
638 if (asoc->strreset_outstanding) {
639 result = SCTP_STRRESET_ERR_IN_PROGRESS;
640 goto out;
641 }
642
643 nums = (ntohs(param.p->length) - sizeof(*inreq)) / sizeof(__u16);
644 str_p = inreq->list_of_streams;
645 for (i = 0; i < nums; i++) {
646 if (ntohs(str_p[i]) >= stream->outcnt) {
647 result = SCTP_STRRESET_ERR_WRONG_SSN;
648 goto out;
649 }
650 }
651
652 if (!sctp_stream_outq_is_empty(stream, nums, str_p)) {
653 result = SCTP_STRRESET_IN_PROGRESS;
654 asoc->strreset_inseq--;
655 goto err;
656 }
657
658 chunk = sctp_make_strreset_req(asoc, nums, str_p, 1, 0);
659 if (!chunk)
660 goto out;
661
662 if (nums)
663 for (i = 0; i < nums; i++)
664 SCTP_SO(stream, ntohs(str_p[i]))->state =
665 SCTP_STREAM_CLOSED;
666 else
667 for (i = 0; i < stream->outcnt; i++)
668 SCTP_SO(stream, i)->state = SCTP_STREAM_CLOSED;
669
670 asoc->strreset_chunk = chunk;
671 asoc->strreset_outstanding = 1;
672 sctp_chunk_hold(asoc->strreset_chunk);
673
674 result = SCTP_STRRESET_PERFORMED;
675
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000676out:
677 sctp_update_strreset_result(asoc, result);
678err:
679 if (!chunk)
680 chunk = sctp_make_strreset_resp(asoc, result, request_seq);
681
682 return chunk;
683}
684
685struct sctp_chunk *sctp_process_strreset_tsnreq(
686 struct sctp_association *asoc,
687 union sctp_params param,
688 struct sctp_ulpevent **evp)
689{
690 __u32 init_tsn = 0, next_tsn = 0, max_tsn_seen;
691 struct sctp_strreset_tsnreq *tsnreq = param.v;
692 struct sctp_stream *stream = &asoc->stream;
693 __u32 result = SCTP_STRRESET_DENIED;
694 __u32 request_seq;
695 __u16 i;
696
697 request_seq = ntohl(tsnreq->request_seq);
698 if (TSN_lt(asoc->strreset_inseq, request_seq) ||
699 TSN_lt(request_seq, asoc->strreset_inseq - 2)) {
700 result = SCTP_STRRESET_ERR_BAD_SEQNO;
701 goto err;
702 } else if (TSN_lt(request_seq, asoc->strreset_inseq)) {
703 i = asoc->strreset_inseq - request_seq - 1;
704 result = asoc->strreset_result[i];
705 if (result == SCTP_STRRESET_PERFORMED) {
706 next_tsn = asoc->ctsn_ack_point + 1;
707 init_tsn =
708 sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map) + 1;
709 }
710 goto err;
711 }
712
713 if (!sctp_outq_is_empty(&asoc->outqueue)) {
714 result = SCTP_STRRESET_IN_PROGRESS;
715 goto err;
716 }
717
718 asoc->strreset_inseq++;
719
720 if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_ASSOC_REQ))
721 goto out;
722
723 if (asoc->strreset_outstanding) {
724 result = SCTP_STRRESET_ERR_IN_PROGRESS;
725 goto out;
726 }
727
728 /* G4: The same processing as though a FWD-TSN chunk (as defined in
729 * [RFC3758]) with all streams affected and a new cumulative TSN
730 * ACK of the Receiver's Next TSN minus 1 were received MUST be
731 * performed.
732 */
733 max_tsn_seen = sctp_tsnmap_get_max_tsn_seen(&asoc->peer.tsn_map);
734 asoc->stream.si->report_ftsn(&asoc->ulpq, max_tsn_seen);
735
736 /* G1: Compute an appropriate value for the Receiver's Next TSN -- the
737 * TSN that the peer should use to send the next DATA chunk. The
738 * value SHOULD be the smallest TSN not acknowledged by the
739 * receiver of the request plus 2^31.
740 */
741 init_tsn = sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map) + (1 << 31);
742 sctp_tsnmap_init(&asoc->peer.tsn_map, SCTP_TSN_MAP_INITIAL,
743 init_tsn, GFP_ATOMIC);
744
745 /* G3: The same processing as though a SACK chunk with no gap report
746 * and a cumulative TSN ACK of the Sender's Next TSN minus 1 were
747 * received MUST be performed.
748 */
749 sctp_outq_free(&asoc->outqueue);
750
751 /* G2: Compute an appropriate value for the local endpoint's next TSN,
752 * i.e., the next TSN assigned by the receiver of the SSN/TSN reset
753 * chunk. The value SHOULD be the highest TSN sent by the receiver
754 * of the request plus 1.
755 */
756 next_tsn = asoc->next_tsn;
757 asoc->ctsn_ack_point = next_tsn - 1;
758 asoc->adv_peer_ack_point = asoc->ctsn_ack_point;
759
760 /* G5: The next expected and outgoing SSNs MUST be reset to 0 for all
761 * incoming and outgoing streams.
762 */
763 for (i = 0; i < stream->outcnt; i++) {
764 SCTP_SO(stream, i)->mid = 0;
765 SCTP_SO(stream, i)->mid_uo = 0;
766 }
767 for (i = 0; i < stream->incnt; i++)
768 SCTP_SI(stream, i)->mid = 0;
769
770 result = SCTP_STRRESET_PERFORMED;
771
772 *evp = sctp_ulpevent_make_assoc_reset_event(asoc, 0, init_tsn,
773 next_tsn, GFP_ATOMIC);
774
775out:
776 sctp_update_strreset_result(asoc, result);
777err:
778 return sctp_make_strreset_tsnresp(asoc, result, request_seq,
779 next_tsn, init_tsn);
780}
781
782struct sctp_chunk *sctp_process_strreset_addstrm_out(
783 struct sctp_association *asoc,
784 union sctp_params param,
785 struct sctp_ulpevent **evp)
786{
787 struct sctp_strreset_addstrm *addstrm = param.v;
788 struct sctp_stream *stream = &asoc->stream;
789 __u32 result = SCTP_STRRESET_DENIED;
790 __u32 request_seq, incnt;
791 __u16 in, i;
792
793 request_seq = ntohl(addstrm->request_seq);
794 if (TSN_lt(asoc->strreset_inseq, request_seq) ||
795 TSN_lt(request_seq, asoc->strreset_inseq - 2)) {
796 result = SCTP_STRRESET_ERR_BAD_SEQNO;
797 goto err;
798 } else if (TSN_lt(request_seq, asoc->strreset_inseq)) {
799 i = asoc->strreset_inseq - request_seq - 1;
800 result = asoc->strreset_result[i];
801 goto err;
802 }
803 asoc->strreset_inseq++;
804
805 if (!(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ))
806 goto out;
807
David Brazdil0f672f62019-12-10 10:32:29 +0000808 in = ntohs(addstrm->number_of_streams);
809 incnt = stream->incnt + in;
810 if (!in || incnt > SCTP_MAX_STREAM)
811 goto out;
812
813 if (sctp_stream_alloc_in(stream, incnt, GFP_ATOMIC))
814 goto out;
815
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000816 if (asoc->strreset_chunk) {
817 if (!sctp_chunk_lookup_strreset_param(
818 asoc, 0, SCTP_PARAM_RESET_ADD_IN_STREAMS)) {
819 /* same process with outstanding isn't 0 */
820 result = SCTP_STRRESET_ERR_IN_PROGRESS;
821 goto out;
822 }
823
824 asoc->strreset_outstanding--;
825 asoc->strreset_outseq++;
826
827 if (!asoc->strreset_outstanding) {
828 struct sctp_transport *t;
829
830 t = asoc->strreset_chunk->transport;
831 if (del_timer(&t->reconf_timer))
832 sctp_transport_put(t);
833
834 sctp_chunk_put(asoc->strreset_chunk);
835 asoc->strreset_chunk = NULL;
836 }
837 }
838
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000839 stream->incnt = incnt;
840
841 result = SCTP_STRRESET_PERFORMED;
842
843 *evp = sctp_ulpevent_make_stream_change_event(asoc,
844 0, ntohs(addstrm->number_of_streams), 0, GFP_ATOMIC);
845
846out:
847 sctp_update_strreset_result(asoc, result);
848err:
849 return sctp_make_strreset_resp(asoc, result, request_seq);
850}
851
852struct sctp_chunk *sctp_process_strreset_addstrm_in(
853 struct sctp_association *asoc,
854 union sctp_params param,
855 struct sctp_ulpevent **evp)
856{
857 struct sctp_strreset_addstrm *addstrm = param.v;
858 struct sctp_stream *stream = &asoc->stream;
859 __u32 result = SCTP_STRRESET_DENIED;
860 struct sctp_chunk *chunk = NULL;
861 __u32 request_seq, outcnt;
862 __u16 out, i;
863 int ret;
864
865 request_seq = ntohl(addstrm->request_seq);
866 if (TSN_lt(asoc->strreset_inseq, request_seq) ||
867 TSN_lt(request_seq, asoc->strreset_inseq - 2)) {
868 result = SCTP_STRRESET_ERR_BAD_SEQNO;
869 goto err;
870 } else if (TSN_lt(request_seq, asoc->strreset_inseq)) {
871 i = asoc->strreset_inseq - request_seq - 1;
872 result = asoc->strreset_result[i];
873 if (result == SCTP_STRRESET_PERFORMED)
874 return NULL;
875 goto err;
876 }
877 asoc->strreset_inseq++;
878
879 if (!(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ))
880 goto out;
881
882 if (asoc->strreset_outstanding) {
883 result = SCTP_STRRESET_ERR_IN_PROGRESS;
884 goto out;
885 }
886
887 out = ntohs(addstrm->number_of_streams);
888 outcnt = stream->outcnt + out;
889 if (!out || outcnt > SCTP_MAX_STREAM)
890 goto out;
891
892 ret = sctp_stream_alloc_out(stream, outcnt, GFP_ATOMIC);
893 if (ret)
894 goto out;
895
896 chunk = sctp_make_strreset_addstrm(asoc, out, 0);
897 if (!chunk)
898 goto out;
899
900 asoc->strreset_chunk = chunk;
901 asoc->strreset_outstanding = 1;
902 sctp_chunk_hold(asoc->strreset_chunk);
903
904 stream->outcnt = outcnt;
905
906 result = SCTP_STRRESET_PERFORMED;
907
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000908out:
909 sctp_update_strreset_result(asoc, result);
910err:
911 if (!chunk)
912 chunk = sctp_make_strreset_resp(asoc, result, request_seq);
913
914 return chunk;
915}
916
917struct sctp_chunk *sctp_process_strreset_resp(
918 struct sctp_association *asoc,
919 union sctp_params param,
920 struct sctp_ulpevent **evp)
921{
922 struct sctp_stream *stream = &asoc->stream;
923 struct sctp_strreset_resp *resp = param.v;
924 struct sctp_transport *t;
925 __u16 i, nums, flags = 0;
926 struct sctp_paramhdr *req;
927 __u32 result;
928
929 req = sctp_chunk_lookup_strreset_param(asoc, resp->response_seq, 0);
930 if (!req)
931 return NULL;
932
933 result = ntohl(resp->result);
934 if (result != SCTP_STRRESET_PERFORMED) {
935 /* if in progress, do nothing but retransmit */
936 if (result == SCTP_STRRESET_IN_PROGRESS)
937 return NULL;
938 else if (result == SCTP_STRRESET_DENIED)
939 flags = SCTP_STREAM_RESET_DENIED;
940 else
941 flags = SCTP_STREAM_RESET_FAILED;
942 }
943
944 if (req->type == SCTP_PARAM_RESET_OUT_REQUEST) {
945 struct sctp_strreset_outreq *outreq;
946 __be16 *str_p;
947
948 outreq = (struct sctp_strreset_outreq *)req;
949 str_p = outreq->list_of_streams;
950 nums = (ntohs(outreq->param_hdr.length) - sizeof(*outreq)) /
951 sizeof(__u16);
952
953 if (result == SCTP_STRRESET_PERFORMED) {
954 struct sctp_stream_out *sout;
955 if (nums) {
956 for (i = 0; i < nums; i++) {
957 sout = SCTP_SO(stream, ntohs(str_p[i]));
958 sout->mid = 0;
959 sout->mid_uo = 0;
960 }
961 } else {
962 for (i = 0; i < stream->outcnt; i++) {
963 sout = SCTP_SO(stream, i);
964 sout->mid = 0;
965 sout->mid_uo = 0;
966 }
967 }
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000968 }
969
David Brazdil0f672f62019-12-10 10:32:29 +0000970 flags |= SCTP_STREAM_RESET_OUTGOING_SSN;
971
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000972 for (i = 0; i < stream->outcnt; i++)
973 SCTP_SO(stream, i)->state = SCTP_STREAM_OPEN;
974
975 *evp = sctp_ulpevent_make_stream_reset_event(asoc, flags,
976 nums, str_p, GFP_ATOMIC);
977 } else if (req->type == SCTP_PARAM_RESET_IN_REQUEST) {
978 struct sctp_strreset_inreq *inreq;
979 __be16 *str_p;
980
981 /* if the result is performed, it's impossible for inreq */
982 if (result == SCTP_STRRESET_PERFORMED)
983 return NULL;
984
985 inreq = (struct sctp_strreset_inreq *)req;
986 str_p = inreq->list_of_streams;
987 nums = (ntohs(inreq->param_hdr.length) - sizeof(*inreq)) /
988 sizeof(__u16);
989
David Brazdil0f672f62019-12-10 10:32:29 +0000990 flags |= SCTP_STREAM_RESET_INCOMING_SSN;
991
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000992 *evp = sctp_ulpevent_make_stream_reset_event(asoc, flags,
993 nums, str_p, GFP_ATOMIC);
994 } else if (req->type == SCTP_PARAM_RESET_TSN_REQUEST) {
995 struct sctp_strreset_resptsn *resptsn;
996 __u32 stsn, rtsn;
997
998 /* check for resptsn, as sctp_verify_reconf didn't do it*/
999 if (ntohs(param.p->length) != sizeof(*resptsn))
1000 return NULL;
1001
1002 resptsn = (struct sctp_strreset_resptsn *)resp;
1003 stsn = ntohl(resptsn->senders_next_tsn);
1004 rtsn = ntohl(resptsn->receivers_next_tsn);
1005
1006 if (result == SCTP_STRRESET_PERFORMED) {
1007 __u32 mtsn = sctp_tsnmap_get_max_tsn_seen(
1008 &asoc->peer.tsn_map);
1009 LIST_HEAD(temp);
1010
1011 asoc->stream.si->report_ftsn(&asoc->ulpq, mtsn);
1012
1013 sctp_tsnmap_init(&asoc->peer.tsn_map,
1014 SCTP_TSN_MAP_INITIAL,
1015 stsn, GFP_ATOMIC);
1016
1017 /* Clean up sacked and abandoned queues only. As the
1018 * out_chunk_list may not be empty, splice it to temp,
1019 * then get it back after sctp_outq_free is done.
1020 */
1021 list_splice_init(&asoc->outqueue.out_chunk_list, &temp);
1022 sctp_outq_free(&asoc->outqueue);
1023 list_splice_init(&temp, &asoc->outqueue.out_chunk_list);
1024
1025 asoc->next_tsn = rtsn;
1026 asoc->ctsn_ack_point = asoc->next_tsn - 1;
1027 asoc->adv_peer_ack_point = asoc->ctsn_ack_point;
1028
1029 for (i = 0; i < stream->outcnt; i++) {
1030 SCTP_SO(stream, i)->mid = 0;
1031 SCTP_SO(stream, i)->mid_uo = 0;
1032 }
1033 for (i = 0; i < stream->incnt; i++)
1034 SCTP_SI(stream, i)->mid = 0;
1035 }
1036
1037 for (i = 0; i < stream->outcnt; i++)
1038 SCTP_SO(stream, i)->state = SCTP_STREAM_OPEN;
1039
1040 *evp = sctp_ulpevent_make_assoc_reset_event(asoc, flags,
1041 stsn, rtsn, GFP_ATOMIC);
1042 } else if (req->type == SCTP_PARAM_RESET_ADD_OUT_STREAMS) {
1043 struct sctp_strreset_addstrm *addstrm;
1044 __u16 number;
1045
1046 addstrm = (struct sctp_strreset_addstrm *)req;
1047 nums = ntohs(addstrm->number_of_streams);
1048 number = stream->outcnt - nums;
1049
Olivier Deprez0e641232021-09-23 10:07:05 +02001050 if (result == SCTP_STRRESET_PERFORMED) {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001051 for (i = number; i < stream->outcnt; i++)
1052 SCTP_SO(stream, i)->state = SCTP_STREAM_OPEN;
Olivier Deprez0e641232021-09-23 10:07:05 +02001053 } else {
1054 sctp_stream_shrink_out(stream, number);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001055 stream->outcnt = number;
Olivier Deprez0e641232021-09-23 10:07:05 +02001056 }
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001057
1058 *evp = sctp_ulpevent_make_stream_change_event(asoc, flags,
1059 0, nums, GFP_ATOMIC);
1060 } else if (req->type == SCTP_PARAM_RESET_ADD_IN_STREAMS) {
1061 struct sctp_strreset_addstrm *addstrm;
1062
1063 /* if the result is performed, it's impossible for addstrm in
1064 * request.
1065 */
1066 if (result == SCTP_STRRESET_PERFORMED)
1067 return NULL;
1068
1069 addstrm = (struct sctp_strreset_addstrm *)req;
1070 nums = ntohs(addstrm->number_of_streams);
1071
1072 *evp = sctp_ulpevent_make_stream_change_event(asoc, flags,
1073 nums, 0, GFP_ATOMIC);
1074 }
1075
1076 asoc->strreset_outstanding--;
1077 asoc->strreset_outseq++;
1078
1079 /* remove everything for this reconf request */
1080 if (!asoc->strreset_outstanding) {
1081 t = asoc->strreset_chunk->transport;
1082 if (del_timer(&t->reconf_timer))
1083 sctp_transport_put(t);
1084
1085 sctp_chunk_put(asoc->strreset_chunk);
1086 asoc->strreset_chunk = NULL;
1087 }
1088
1089 return NULL;
1090}