]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - net/sctp/stream.c
sctp: merge sctp_stream_new and sctp_stream_init
[mirror_ubuntu-artful-kernel.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
35#include <net/sctp/sctp.h>
7f9d68ac 36#include <net/sctp/sm.h>
a8386317 37
ff356414
XL
38int sctp_stream_init(struct sctp_stream *stream, __u16 outcnt, __u16 incnt,
39 gfp_t gfp)
a8386317 40{
3dbcc105
XL
41 int i;
42
43 /* Initial stream->out size may be very big, so free it and alloc
44 * a new one with new outcnt to save memory.
45 */
46 kfree(stream->out);
ff356414
XL
47
48 stream->out = kcalloc(outcnt, sizeof(*stream->out), gfp);
3dbcc105 49 if (!stream->out)
cee360ab 50 return -ENOMEM;
3dbcc105 51
ff356414 52 stream->outcnt = outcnt;
3dbcc105
XL
53 for (i = 0; i < stream->outcnt; i++)
54 stream->out[i].state = SCTP_STREAM_OPEN;
55
ff356414
XL
56 if (!incnt)
57 return 0;
58
59 stream->in = kcalloc(incnt, sizeof(*stream->in), gfp);
a8386317
XL
60 if (!stream->in) {
61 kfree(stream->out);
cee360ab
XL
62 stream->out = NULL;
63 return -ENOMEM;
a8386317
XL
64 }
65
ff356414
XL
66 stream->incnt = incnt;
67
3dbcc105 68 return 0;
a8386317
XL
69}
70
71void sctp_stream_free(struct sctp_stream *stream)
72{
a8386317
XL
73 kfree(stream->out);
74 kfree(stream->in);
a8386317
XL
75}
76
77void sctp_stream_clear(struct sctp_stream *stream)
78{
79 int i;
80
81 for (i = 0; i < stream->outcnt; i++)
82 stream->out[i].ssn = 0;
83
84 for (i = 0; i < stream->incnt; i++)
85 stream->in[i].ssn = 0;
86}
7f9d68ac 87
cee360ab
XL
88void sctp_stream_update(struct sctp_stream *stream, struct sctp_stream *new)
89{
90 sctp_stream_free(stream);
91
92 stream->out = new->out;
93 stream->in = new->in;
94 stream->outcnt = new->outcnt;
95 stream->incnt = new->incnt;
96
97 new->out = NULL;
98 new->in = NULL;
99}
100
7f9d68ac
XL
101static int sctp_send_reconf(struct sctp_association *asoc,
102 struct sctp_chunk *chunk)
103{
104 struct net *net = sock_net(asoc->base.sk);
105 int retval = 0;
106
107 retval = sctp_primitive_RECONF(net, asoc, chunk);
108 if (retval)
109 sctp_chunk_free(chunk);
110
111 return retval;
112}
113
114int sctp_send_reset_streams(struct sctp_association *asoc,
115 struct sctp_reset_streams *params)
116{
cee360ab 117 struct sctp_stream *stream = &asoc->stream;
7f9d68ac
XL
118 __u16 i, str_nums, *str_list;
119 struct sctp_chunk *chunk;
120 int retval = -EINVAL;
121 bool out, in;
122
123 if (!asoc->peer.reconf_capable ||
124 !(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ)) {
125 retval = -ENOPROTOOPT;
126 goto out;
127 }
128
129 if (asoc->strreset_outstanding) {
130 retval = -EINPROGRESS;
131 goto out;
132 }
133
134 out = params->srs_flags & SCTP_STREAM_RESET_OUTGOING;
135 in = params->srs_flags & SCTP_STREAM_RESET_INCOMING;
136 if (!out && !in)
137 goto out;
138
139 str_nums = params->srs_number_streams;
140 str_list = params->srs_stream_list;
141 if (out && str_nums)
142 for (i = 0; i < str_nums; i++)
143 if (str_list[i] >= stream->outcnt)
144 goto out;
145
146 if (in && str_nums)
147 for (i = 0; i < str_nums; i++)
148 if (str_list[i] >= stream->incnt)
149 goto out;
150
16e1a919
XL
151 for (i = 0; i < str_nums; i++)
152 str_list[i] = htons(str_list[i]);
153
7f9d68ac 154 chunk = sctp_make_strreset_req(asoc, str_nums, str_list, out, in);
16e1a919
XL
155
156 for (i = 0; i < str_nums; i++)
157 str_list[i] = ntohs(str_list[i]);
158
119aecba
XL
159 if (!chunk) {
160 retval = -ENOMEM;
7f9d68ac 161 goto out;
119aecba 162 }
7f9d68ac
XL
163
164 if (out) {
165 if (str_nums)
166 for (i = 0; i < str_nums; i++)
167 stream->out[str_list[i]].state =
168 SCTP_STREAM_CLOSED;
169 else
170 for (i = 0; i < stream->outcnt; i++)
171 stream->out[i].state = SCTP_STREAM_CLOSED;
172 }
173
7f9d68ac
XL
174 asoc->strreset_chunk = chunk;
175 sctp_chunk_hold(asoc->strreset_chunk);
176
177 retval = sctp_send_reconf(asoc, chunk);
178 if (retval) {
179 sctp_chunk_put(asoc->strreset_chunk);
180 asoc->strreset_chunk = NULL;
119aecba
XL
181 if (!out)
182 goto out;
183
184 if (str_nums)
185 for (i = 0; i < str_nums; i++)
186 stream->out[str_list[i]].state =
187 SCTP_STREAM_OPEN;
188 else
189 for (i = 0; i < stream->outcnt; i++)
190 stream->out[i].state = SCTP_STREAM_OPEN;
191
192 goto out;
7f9d68ac
XL
193 }
194
119aecba
XL
195 asoc->strreset_outstanding = out + in;
196
7f9d68ac
XL
197out:
198 return retval;
199}
a92ce1a4
XL
200
201int sctp_send_reset_assoc(struct sctp_association *asoc)
202{
cee360ab 203 struct sctp_stream *stream = &asoc->stream;
a92ce1a4
XL
204 struct sctp_chunk *chunk = NULL;
205 int retval;
206 __u16 i;
207
208 if (!asoc->peer.reconf_capable ||
209 !(asoc->strreset_enable & SCTP_ENABLE_RESET_ASSOC_REQ))
210 return -ENOPROTOOPT;
211
212 if (asoc->strreset_outstanding)
213 return -EINPROGRESS;
214
215 chunk = sctp_make_strreset_tsnreq(asoc);
216 if (!chunk)
217 return -ENOMEM;
218
219 /* Block further xmit of data until this request is completed */
cee360ab
XL
220 for (i = 0; i < stream->outcnt; i++)
221 stream->out[i].state = SCTP_STREAM_CLOSED;
a92ce1a4
XL
222
223 asoc->strreset_chunk = chunk;
224 sctp_chunk_hold(asoc->strreset_chunk);
225
226 retval = sctp_send_reconf(asoc, chunk);
227 if (retval) {
228 sctp_chunk_put(asoc->strreset_chunk);
229 asoc->strreset_chunk = NULL;
230
cee360ab
XL
231 for (i = 0; i < stream->outcnt; i++)
232 stream->out[i].state = SCTP_STREAM_OPEN;
a92ce1a4
XL
233
234 return retval;
235 }
236
237 asoc->strreset_outstanding = 1;
238
239 return 0;
240}
242bd2d5
XL
241
242int sctp_send_add_streams(struct sctp_association *asoc,
243 struct sctp_add_streams *params)
244{
cee360ab 245 struct sctp_stream *stream = &asoc->stream;
242bd2d5
XL
246 struct sctp_chunk *chunk = NULL;
247 int retval = -ENOMEM;
248 __u32 outcnt, incnt;
249 __u16 out, in;
250
251 if (!asoc->peer.reconf_capable ||
252 !(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ)) {
253 retval = -ENOPROTOOPT;
254 goto out;
255 }
256
257 if (asoc->strreset_outstanding) {
258 retval = -EINPROGRESS;
259 goto out;
260 }
261
262 out = params->sas_outstrms;
263 in = params->sas_instrms;
264 outcnt = stream->outcnt + out;
265 incnt = stream->incnt + in;
266 if (outcnt > SCTP_MAX_STREAM || incnt > SCTP_MAX_STREAM ||
267 (!out && !in)) {
268 retval = -EINVAL;
269 goto out;
270 }
271
272 if (out) {
273 struct sctp_stream_out *streamout;
274
275 streamout = krealloc(stream->out, outcnt * sizeof(*streamout),
276 GFP_KERNEL);
277 if (!streamout)
278 goto out;
279
280 memset(streamout + stream->outcnt, 0, out * sizeof(*streamout));
281 stream->out = streamout;
282 }
283
242bd2d5
XL
284 chunk = sctp_make_strreset_addstrm(asoc, out, in);
285 if (!chunk)
286 goto out;
287
288 asoc->strreset_chunk = chunk;
289 sctp_chunk_hold(asoc->strreset_chunk);
290
291 retval = sctp_send_reconf(asoc, chunk);
292 if (retval) {
293 sctp_chunk_put(asoc->strreset_chunk);
294 asoc->strreset_chunk = NULL;
295 goto out;
296 }
297
298 stream->incnt = incnt;
299 stream->outcnt = outcnt;
300
301 asoc->strreset_outstanding = !!out + !!in;
302
303out:
304 return retval;
305}
81054476
XL
306
307static sctp_paramhdr_t *sctp_chunk_lookup_strreset_param(
50a41591
XL
308 struct sctp_association *asoc, __u32 resp_seq,
309 __be16 type)
81054476
XL
310{
311 struct sctp_chunk *chunk = asoc->strreset_chunk;
312 struct sctp_reconf_chunk *hdr;
313 union sctp_params param;
314
50a41591 315 if (!chunk)
81054476
XL
316 return NULL;
317
318 hdr = (struct sctp_reconf_chunk *)chunk->chunk_hdr;
319 sctp_walk_params(param, hdr, params) {
320 /* sctp_strreset_tsnreq is actually the basic structure
321 * of all stream reconf params, so it's safe to use it
322 * to access request_seq.
323 */
324 struct sctp_strreset_tsnreq *req = param.v;
325
50a41591
XL
326 if ((!resp_seq || req->request_seq == resp_seq) &&
327 (!type || type == req->param_hdr.type))
81054476
XL
328 return param.v;
329 }
330
331 return NULL;
332}
333
e4dc99c7
XL
334static void sctp_update_strreset_result(struct sctp_association *asoc,
335 __u32 result)
336{
337 asoc->strreset_result[1] = asoc->strreset_result[0];
338 asoc->strreset_result[0] = result;
339}
340
81054476
XL
341struct sctp_chunk *sctp_process_strreset_outreq(
342 struct sctp_association *asoc,
343 union sctp_params param,
344 struct sctp_ulpevent **evp)
345{
346 struct sctp_strreset_outreq *outreq = param.v;
cee360ab 347 struct sctp_stream *stream = &asoc->stream;
81054476
XL
348 __u16 i, nums, flags = 0, *str_p = NULL;
349 __u32 result = SCTP_STRRESET_DENIED;
350 __u32 request_seq;
351
352 request_seq = ntohl(outreq->request_seq);
353
354 if (ntohl(outreq->send_reset_at_tsn) >
355 sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map)) {
356 result = SCTP_STRRESET_IN_PROGRESS;
e4dc99c7 357 goto err;
81054476
XL
358 }
359
e4dc99c7
XL
360 if (TSN_lt(asoc->strreset_inseq, request_seq) ||
361 TSN_lt(request_seq, asoc->strreset_inseq - 2)) {
81054476 362 result = SCTP_STRRESET_ERR_BAD_SEQNO;
e4dc99c7
XL
363 goto err;
364 } else if (TSN_lt(request_seq, asoc->strreset_inseq)) {
365 i = asoc->strreset_inseq - request_seq - 1;
366 result = asoc->strreset_result[i];
367 goto err;
81054476 368 }
e4dc99c7 369 asoc->strreset_inseq++;
81054476
XL
370
371 /* Check strreset_enable after inseq inc, as sender cannot tell
372 * the peer doesn't enable strreset after receiving response with
373 * result denied, as well as to keep consistent with bsd.
374 */
375 if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ))
376 goto out;
377
378 if (asoc->strreset_chunk) {
50a41591
XL
379 if (!sctp_chunk_lookup_strreset_param(
380 asoc, outreq->response_seq,
381 SCTP_PARAM_RESET_IN_REQUEST)) {
81054476
XL
382 /* same process with outstanding isn't 0 */
383 result = SCTP_STRRESET_ERR_IN_PROGRESS;
384 goto out;
385 }
386
387 asoc->strreset_outstanding--;
388 asoc->strreset_outseq++;
389
390 if (!asoc->strreset_outstanding) {
50a41591
XL
391 struct sctp_transport *t;
392
81054476
XL
393 t = asoc->strreset_chunk->transport;
394 if (del_timer(&t->reconf_timer))
395 sctp_transport_put(t);
396
397 sctp_chunk_put(asoc->strreset_chunk);
398 asoc->strreset_chunk = NULL;
399 }
400
401 flags = SCTP_STREAM_RESET_INCOMING_SSN;
402 }
403
404 nums = (ntohs(param.p->length) - sizeof(*outreq)) / 2;
405 if (nums) {
406 str_p = outreq->list_of_streams;
407 for (i = 0; i < nums; i++) {
408 if (ntohs(str_p[i]) >= stream->incnt) {
409 result = SCTP_STRRESET_ERR_WRONG_SSN;
410 goto out;
411 }
412 }
413
414 for (i = 0; i < nums; i++)
415 stream->in[ntohs(str_p[i])].ssn = 0;
416 } else {
417 for (i = 0; i < stream->incnt; i++)
418 stream->in[i].ssn = 0;
419 }
420
421 result = SCTP_STRRESET_PERFORMED;
422
423 *evp = sctp_ulpevent_make_stream_reset_event(asoc,
424 flags | SCTP_STREAM_RESET_OUTGOING_SSN, nums, str_p,
425 GFP_ATOMIC);
426
427out:
e4dc99c7
XL
428 sctp_update_strreset_result(asoc, result);
429err:
81054476
XL
430 return sctp_make_strreset_resp(asoc, result, request_seq);
431}
16e1a919
XL
432
433struct sctp_chunk *sctp_process_strreset_inreq(
434 struct sctp_association *asoc,
435 union sctp_params param,
436 struct sctp_ulpevent **evp)
437{
438 struct sctp_strreset_inreq *inreq = param.v;
cee360ab 439 struct sctp_stream *stream = &asoc->stream;
16e1a919
XL
440 __u32 result = SCTP_STRRESET_DENIED;
441 struct sctp_chunk *chunk = NULL;
442 __u16 i, nums, *str_p;
443 __u32 request_seq;
444
445 request_seq = ntohl(inreq->request_seq);
d0f025e6
XL
446 if (TSN_lt(asoc->strreset_inseq, request_seq) ||
447 TSN_lt(request_seq, asoc->strreset_inseq - 2)) {
16e1a919 448 result = SCTP_STRRESET_ERR_BAD_SEQNO;
d0f025e6
XL
449 goto err;
450 } else if (TSN_lt(request_seq, asoc->strreset_inseq)) {
451 i = asoc->strreset_inseq - request_seq - 1;
452 result = asoc->strreset_result[i];
453 if (result == SCTP_STRRESET_PERFORMED)
454 return NULL;
455 goto err;
16e1a919 456 }
d0f025e6 457 asoc->strreset_inseq++;
16e1a919
XL
458
459 if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ))
460 goto out;
461
462 if (asoc->strreset_outstanding) {
463 result = SCTP_STRRESET_ERR_IN_PROGRESS;
464 goto out;
465 }
466
467 nums = (ntohs(param.p->length) - sizeof(*inreq)) / 2;
468 str_p = inreq->list_of_streams;
469 for (i = 0; i < nums; i++) {
470 if (ntohs(str_p[i]) >= stream->outcnt) {
471 result = SCTP_STRRESET_ERR_WRONG_SSN;
472 goto out;
473 }
474 }
475
476 chunk = sctp_make_strreset_req(asoc, nums, str_p, 1, 0);
477 if (!chunk)
478 goto out;
479
480 if (nums)
481 for (i = 0; i < nums; i++)
482 stream->out[ntohs(str_p[i])].state =
483 SCTP_STREAM_CLOSED;
484 else
485 for (i = 0; i < stream->outcnt; i++)
486 stream->out[i].state = SCTP_STREAM_CLOSED;
487
488 asoc->strreset_chunk = chunk;
489 asoc->strreset_outstanding = 1;
490 sctp_chunk_hold(asoc->strreset_chunk);
491
d0f025e6
XL
492 result = SCTP_STRRESET_PERFORMED;
493
16e1a919
XL
494 *evp = sctp_ulpevent_make_stream_reset_event(asoc,
495 SCTP_STREAM_RESET_INCOMING_SSN, nums, str_p, GFP_ATOMIC);
496
497out:
d0f025e6
XL
498 sctp_update_strreset_result(asoc, result);
499err:
16e1a919
XL
500 if (!chunk)
501 chunk = sctp_make_strreset_resp(asoc, result, request_seq);
502
503 return chunk;
504}
692787ce
XL
505
506struct sctp_chunk *sctp_process_strreset_tsnreq(
507 struct sctp_association *asoc,
508 union sctp_params param,
509 struct sctp_ulpevent **evp)
510{
511 __u32 init_tsn = 0, next_tsn = 0, max_tsn_seen;
512 struct sctp_strreset_tsnreq *tsnreq = param.v;
cee360ab 513 struct sctp_stream *stream = &asoc->stream;
692787ce
XL
514 __u32 result = SCTP_STRRESET_DENIED;
515 __u32 request_seq;
516 __u16 i;
517
518 request_seq = ntohl(tsnreq->request_seq);
6c801387
XL
519 if (TSN_lt(asoc->strreset_inseq, request_seq) ||
520 TSN_lt(request_seq, asoc->strreset_inseq - 2)) {
692787ce 521 result = SCTP_STRRESET_ERR_BAD_SEQNO;
6c801387
XL
522 goto err;
523 } else if (TSN_lt(request_seq, asoc->strreset_inseq)) {
524 i = asoc->strreset_inseq - request_seq - 1;
525 result = asoc->strreset_result[i];
526 if (result == SCTP_STRRESET_PERFORMED) {
527 next_tsn = asoc->next_tsn;
528 init_tsn =
529 sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map) + 1;
530 }
531 goto err;
692787ce 532 }
6c801387 533 asoc->strreset_inseq++;
692787ce
XL
534
535 if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_ASSOC_REQ))
536 goto out;
537
538 if (asoc->strreset_outstanding) {
539 result = SCTP_STRRESET_ERR_IN_PROGRESS;
540 goto out;
541 }
542
543 /* G3: The same processing as though a SACK chunk with no gap report
544 * and a cumulative TSN ACK of the Sender's Next TSN minus 1 were
545 * received MUST be performed.
546 */
547 max_tsn_seen = sctp_tsnmap_get_max_tsn_seen(&asoc->peer.tsn_map);
548 sctp_ulpq_reasm_flushtsn(&asoc->ulpq, max_tsn_seen);
549 sctp_ulpq_abort_pd(&asoc->ulpq, GFP_ATOMIC);
550
551 /* G1: Compute an appropriate value for the Receiver's Next TSN -- the
552 * TSN that the peer should use to send the next DATA chunk. The
553 * value SHOULD be the smallest TSN not acknowledged by the
554 * receiver of the request plus 2^31.
555 */
556 init_tsn = sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map) + (1 << 31);
557 sctp_tsnmap_init(&asoc->peer.tsn_map, SCTP_TSN_MAP_INITIAL,
558 init_tsn, GFP_ATOMIC);
559
560 /* G4: The same processing as though a FWD-TSN chunk (as defined in
561 * [RFC3758]) with all streams affected and a new cumulative TSN
562 * ACK of the Receiver's Next TSN minus 1 were received MUST be
563 * performed.
564 */
565 sctp_outq_free(&asoc->outqueue);
566
567 /* G2: Compute an appropriate value for the local endpoint's next TSN,
568 * i.e., the next TSN assigned by the receiver of the SSN/TSN reset
569 * chunk. The value SHOULD be the highest TSN sent by the receiver
570 * of the request plus 1.
571 */
572 next_tsn = asoc->next_tsn;
573 asoc->ctsn_ack_point = next_tsn - 1;
574 asoc->adv_peer_ack_point = asoc->ctsn_ack_point;
575
576 /* G5: The next expected and outgoing SSNs MUST be reset to 0 for all
577 * incoming and outgoing streams.
578 */
579 for (i = 0; i < stream->outcnt; i++)
580 stream->out[i].ssn = 0;
581 for (i = 0; i < stream->incnt; i++)
582 stream->in[i].ssn = 0;
583
584 result = SCTP_STRRESET_PERFORMED;
585
586 *evp = sctp_ulpevent_make_assoc_reset_event(asoc, 0, init_tsn,
587 next_tsn, GFP_ATOMIC);
588
589out:
6c801387
XL
590 sctp_update_strreset_result(asoc, result);
591err:
692787ce
XL
592 return sctp_make_strreset_tsnresp(asoc, result, request_seq,
593 next_tsn, init_tsn);
594}
50a41591
XL
595
596struct sctp_chunk *sctp_process_strreset_addstrm_out(
597 struct sctp_association *asoc,
598 union sctp_params param,
599 struct sctp_ulpevent **evp)
600{
601 struct sctp_strreset_addstrm *addstrm = param.v;
cee360ab 602 struct sctp_stream *stream = &asoc->stream;
50a41591
XL
603 __u32 result = SCTP_STRRESET_DENIED;
604 struct sctp_stream_in *streamin;
605 __u32 request_seq, incnt;
e4dc99c7 606 __u16 in, i;
50a41591
XL
607
608 request_seq = ntohl(addstrm->request_seq);
e4dc99c7
XL
609 if (TSN_lt(asoc->strreset_inseq, request_seq) ||
610 TSN_lt(request_seq, asoc->strreset_inseq - 2)) {
50a41591 611 result = SCTP_STRRESET_ERR_BAD_SEQNO;
e4dc99c7
XL
612 goto err;
613 } else if (TSN_lt(request_seq, asoc->strreset_inseq)) {
614 i = asoc->strreset_inseq - request_seq - 1;
615 result = asoc->strreset_result[i];
616 goto err;
50a41591 617 }
e4dc99c7 618 asoc->strreset_inseq++;
50a41591
XL
619
620 if (!(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ))
621 goto out;
622
623 if (asoc->strreset_chunk) {
624 if (!sctp_chunk_lookup_strreset_param(
625 asoc, 0, SCTP_PARAM_RESET_ADD_IN_STREAMS)) {
626 /* same process with outstanding isn't 0 */
627 result = SCTP_STRRESET_ERR_IN_PROGRESS;
628 goto out;
629 }
630
631 asoc->strreset_outstanding--;
632 asoc->strreset_outseq++;
633
634 if (!asoc->strreset_outstanding) {
635 struct sctp_transport *t;
636
637 t = asoc->strreset_chunk->transport;
638 if (del_timer(&t->reconf_timer))
639 sctp_transport_put(t);
640
641 sctp_chunk_put(asoc->strreset_chunk);
642 asoc->strreset_chunk = NULL;
643 }
644 }
645
646 in = ntohs(addstrm->number_of_streams);
647 incnt = stream->incnt + in;
648 if (!in || incnt > SCTP_MAX_STREAM)
649 goto out;
650
651 streamin = krealloc(stream->in, incnt * sizeof(*streamin),
652 GFP_ATOMIC);
653 if (!streamin)
654 goto out;
655
656 memset(streamin + stream->incnt, 0, in * sizeof(*streamin));
657 stream->in = streamin;
658 stream->incnt = incnt;
659
660 result = SCTP_STRRESET_PERFORMED;
661
662 *evp = sctp_ulpevent_make_stream_change_event(asoc,
663 0, ntohs(addstrm->number_of_streams), 0, GFP_ATOMIC);
664
665out:
e4dc99c7
XL
666 sctp_update_strreset_result(asoc, result);
667err:
50a41591
XL
668 return sctp_make_strreset_resp(asoc, result, request_seq);
669}
c5c4ebb3
XL
670
671struct sctp_chunk *sctp_process_strreset_addstrm_in(
672 struct sctp_association *asoc,
673 union sctp_params param,
674 struct sctp_ulpevent **evp)
675{
676 struct sctp_strreset_addstrm *addstrm = param.v;
cee360ab 677 struct sctp_stream *stream = &asoc->stream;
c5c4ebb3
XL
678 __u32 result = SCTP_STRRESET_DENIED;
679 struct sctp_stream_out *streamout;
680 struct sctp_chunk *chunk = NULL;
681 __u32 request_seq, outcnt;
d0f025e6 682 __u16 out, i;
c5c4ebb3
XL
683
684 request_seq = ntohl(addstrm->request_seq);
d0f025e6
XL
685 if (TSN_lt(asoc->strreset_inseq, request_seq) ||
686 TSN_lt(request_seq, asoc->strreset_inseq - 2)) {
c5c4ebb3 687 result = SCTP_STRRESET_ERR_BAD_SEQNO;
d0f025e6
XL
688 goto err;
689 } else if (TSN_lt(request_seq, asoc->strreset_inseq)) {
690 i = asoc->strreset_inseq - request_seq - 1;
691 result = asoc->strreset_result[i];
692 if (result == SCTP_STRRESET_PERFORMED)
693 return NULL;
694 goto err;
c5c4ebb3 695 }
d0f025e6 696 asoc->strreset_inseq++;
c5c4ebb3
XL
697
698 if (!(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ))
699 goto out;
700
701 if (asoc->strreset_outstanding) {
702 result = SCTP_STRRESET_ERR_IN_PROGRESS;
703 goto out;
704 }
705
706 out = ntohs(addstrm->number_of_streams);
707 outcnt = stream->outcnt + out;
708 if (!out || outcnt > SCTP_MAX_STREAM)
709 goto out;
710
711 streamout = krealloc(stream->out, outcnt * sizeof(*streamout),
712 GFP_ATOMIC);
713 if (!streamout)
714 goto out;
715
716 memset(streamout + stream->outcnt, 0, out * sizeof(*streamout));
717 stream->out = streamout;
718
719 chunk = sctp_make_strreset_addstrm(asoc, out, 0);
720 if (!chunk)
721 goto out;
722
723 asoc->strreset_chunk = chunk;
724 asoc->strreset_outstanding = 1;
725 sctp_chunk_hold(asoc->strreset_chunk);
726
727 stream->outcnt = outcnt;
728
d0f025e6
XL
729 result = SCTP_STRRESET_PERFORMED;
730
c5c4ebb3
XL
731 *evp = sctp_ulpevent_make_stream_change_event(asoc,
732 0, 0, ntohs(addstrm->number_of_streams), GFP_ATOMIC);
733
734out:
d0f025e6
XL
735 sctp_update_strreset_result(asoc, result);
736err:
c5c4ebb3
XL
737 if (!chunk)
738 chunk = sctp_make_strreset_resp(asoc, result, request_seq);
739
740 return chunk;
741}
11ae76e6
XL
742
743struct sctp_chunk *sctp_process_strreset_resp(
744 struct sctp_association *asoc,
745 union sctp_params param,
746 struct sctp_ulpevent **evp)
747{
cee360ab 748 struct sctp_stream *stream = &asoc->stream;
11ae76e6 749 struct sctp_strreset_resp *resp = param.v;
11ae76e6
XL
750 struct sctp_transport *t;
751 __u16 i, nums, flags = 0;
752 sctp_paramhdr_t *req;
753 __u32 result;
754
755 req = sctp_chunk_lookup_strreset_param(asoc, resp->response_seq, 0);
756 if (!req)
757 return NULL;
758
759 result = ntohl(resp->result);
760 if (result != SCTP_STRRESET_PERFORMED) {
761 /* if in progress, do nothing but retransmit */
762 if (result == SCTP_STRRESET_IN_PROGRESS)
763 return NULL;
764 else if (result == SCTP_STRRESET_DENIED)
765 flags = SCTP_STREAM_RESET_DENIED;
766 else
767 flags = SCTP_STREAM_RESET_FAILED;
768 }
769
770 if (req->type == SCTP_PARAM_RESET_OUT_REQUEST) {
771 struct sctp_strreset_outreq *outreq;
edb12f2d 772 __u16 *str_p;
11ae76e6
XL
773
774 outreq = (struct sctp_strreset_outreq *)req;
edb12f2d 775 str_p = outreq->list_of_streams;
11ae76e6
XL
776 nums = (ntohs(outreq->param_hdr.length) - sizeof(*outreq)) / 2;
777
778 if (result == SCTP_STRRESET_PERFORMED) {
779 if (nums) {
11ae76e6
XL
780 for (i = 0; i < nums; i++)
781 stream->out[ntohs(str_p[i])].ssn = 0;
782 } else {
783 for (i = 0; i < stream->outcnt; i++)
784 stream->out[i].ssn = 0;
785 }
786
787 flags = SCTP_STREAM_RESET_OUTGOING_SSN;
788 }
789
790 for (i = 0; i < stream->outcnt; i++)
791 stream->out[i].state = SCTP_STREAM_OPEN;
792
793 *evp = sctp_ulpevent_make_stream_reset_event(asoc, flags,
794 nums, str_p, GFP_ATOMIC);
795 } else if (req->type == SCTP_PARAM_RESET_IN_REQUEST) {
796 struct sctp_strreset_inreq *inreq;
edb12f2d 797 __u16 *str_p;
11ae76e6
XL
798
799 /* if the result is performed, it's impossible for inreq */
800 if (result == SCTP_STRRESET_PERFORMED)
801 return NULL;
802
803 inreq = (struct sctp_strreset_inreq *)req;
edb12f2d 804 str_p = inreq->list_of_streams;
11ae76e6
XL
805 nums = (ntohs(inreq->param_hdr.length) - sizeof(*inreq)) / 2;
806
11ae76e6
XL
807 *evp = sctp_ulpevent_make_stream_reset_event(asoc, flags,
808 nums, str_p, GFP_ATOMIC);
809 } else if (req->type == SCTP_PARAM_RESET_TSN_REQUEST) {
810 struct sctp_strreset_resptsn *resptsn;
811 __u32 stsn, rtsn;
812
813 /* check for resptsn, as sctp_verify_reconf didn't do it*/
814 if (ntohs(param.p->length) != sizeof(*resptsn))
815 return NULL;
816
817 resptsn = (struct sctp_strreset_resptsn *)resp;
818 stsn = ntohl(resptsn->senders_next_tsn);
819 rtsn = ntohl(resptsn->receivers_next_tsn);
820
821 if (result == SCTP_STRRESET_PERFORMED) {
822 __u32 mtsn = sctp_tsnmap_get_max_tsn_seen(
823 &asoc->peer.tsn_map);
824
825 sctp_ulpq_reasm_flushtsn(&asoc->ulpq, mtsn);
826 sctp_ulpq_abort_pd(&asoc->ulpq, GFP_ATOMIC);
827
828 sctp_tsnmap_init(&asoc->peer.tsn_map,
829 SCTP_TSN_MAP_INITIAL,
830 stsn, GFP_ATOMIC);
831
832 sctp_outq_free(&asoc->outqueue);
833
834 asoc->next_tsn = rtsn;
835 asoc->ctsn_ack_point = asoc->next_tsn - 1;
836 asoc->adv_peer_ack_point = asoc->ctsn_ack_point;
837
838 for (i = 0; i < stream->outcnt; i++)
839 stream->out[i].ssn = 0;
840 for (i = 0; i < stream->incnt; i++)
841 stream->in[i].ssn = 0;
842 }
843
844 for (i = 0; i < stream->outcnt; i++)
845 stream->out[i].state = SCTP_STREAM_OPEN;
846
847 *evp = sctp_ulpevent_make_assoc_reset_event(asoc, flags,
848 stsn, rtsn, GFP_ATOMIC);
849 } else if (req->type == SCTP_PARAM_RESET_ADD_OUT_STREAMS) {
850 struct sctp_strreset_addstrm *addstrm;
851 __u16 number;
852
853 addstrm = (struct sctp_strreset_addstrm *)req;
854 nums = ntohs(addstrm->number_of_streams);
855 number = stream->outcnt - nums;
856
857 if (result == SCTP_STRRESET_PERFORMED)
858 for (i = number; i < stream->outcnt; i++)
859 stream->out[i].state = SCTP_STREAM_OPEN;
860 else
861 stream->outcnt = number;
862
863 *evp = sctp_ulpevent_make_stream_change_event(asoc, flags,
864 0, nums, GFP_ATOMIC);
865 } else if (req->type == SCTP_PARAM_RESET_ADD_IN_STREAMS) {
866 struct sctp_strreset_addstrm *addstrm;
867
868 /* if the result is performed, it's impossible for addstrm in
869 * request.
870 */
871 if (result == SCTP_STRRESET_PERFORMED)
872 return NULL;
873
874 addstrm = (struct sctp_strreset_addstrm *)req;
875 nums = ntohs(addstrm->number_of_streams);
876
877 *evp = sctp_ulpevent_make_stream_change_event(asoc, flags,
878 nums, 0, GFP_ATOMIC);
879 }
880
881 asoc->strreset_outstanding--;
882 asoc->strreset_outseq++;
883
884 /* remove everything for this reconf request */
885 if (!asoc->strreset_outstanding) {
886 t = asoc->strreset_chunk->transport;
887 if (del_timer(&t->reconf_timer))
888 sctp_transport_put(t);
889
890 sctp_chunk_put(asoc->strreset_chunk);
891 asoc->strreset_chunk = NULL;
892 }
893
894 return NULL;
895}