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