Merge tag 'phy-for-4.14_v2' of git://git.kernel.org/pub/scm/linux/kernel/git/kishon...
[linux/fpc-iii.git] / net / sctp / stream.c
blob63ea1550371493ec8863627c7a43f46a22f4a4c9
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.
7 * This file is part of the SCTP kernel implementation
9 * These functions manipulate sctp tsn mapping array.
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.
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.
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/>.
27 * Please send any bug reports or fixes you make to the
28 * email address(es):
29 * lksctp developers <linux-sctp@vger.kernel.org>
31 * Written or modified by:
32 * Xin Long <lucien.xin@gmail.com>
35 #include <net/sctp/sctp.h>
36 #include <net/sctp/sm.h>
38 int sctp_stream_init(struct sctp_stream *stream, __u16 outcnt, __u16 incnt,
39 gfp_t gfp)
41 int i;
43 /* Initial stream->out size may be very big, so free it and alloc
44 * a new one with new outcnt to save memory.
46 kfree(stream->out);
48 stream->out = kcalloc(outcnt, sizeof(*stream->out), gfp);
49 if (!stream->out)
50 return -ENOMEM;
52 stream->outcnt = outcnt;
53 for (i = 0; i < stream->outcnt; i++)
54 stream->out[i].state = SCTP_STREAM_OPEN;
56 if (!incnt)
57 return 0;
59 stream->in = kcalloc(incnt, sizeof(*stream->in), gfp);
60 if (!stream->in) {
61 kfree(stream->out);
62 stream->out = NULL;
63 return -ENOMEM;
66 stream->incnt = incnt;
68 return 0;
71 void sctp_stream_free(struct sctp_stream *stream)
73 kfree(stream->out);
74 kfree(stream->in);
77 void sctp_stream_clear(struct sctp_stream *stream)
79 int i;
81 for (i = 0; i < stream->outcnt; i++)
82 stream->out[i].ssn = 0;
84 for (i = 0; i < stream->incnt; i++)
85 stream->in[i].ssn = 0;
88 void sctp_stream_update(struct sctp_stream *stream, struct sctp_stream *new)
90 sctp_stream_free(stream);
92 stream->out = new->out;
93 stream->in = new->in;
94 stream->outcnt = new->outcnt;
95 stream->incnt = new->incnt;
97 new->out = NULL;
98 new->in = NULL;
101 static int sctp_send_reconf(struct sctp_association *asoc,
102 struct sctp_chunk *chunk)
104 struct net *net = sock_net(asoc->base.sk);
105 int retval = 0;
107 retval = sctp_primitive_RECONF(net, asoc, chunk);
108 if (retval)
109 sctp_chunk_free(chunk);
111 return retval;
114 int sctp_send_reset_streams(struct sctp_association *asoc,
115 struct sctp_reset_streams *params)
117 struct sctp_stream *stream = &asoc->stream;
118 __u16 i, str_nums, *str_list;
119 struct sctp_chunk *chunk;
120 int retval = -EINVAL;
121 bool out, in;
123 if (!asoc->peer.reconf_capable ||
124 !(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ)) {
125 retval = -ENOPROTOOPT;
126 goto out;
129 if (asoc->strreset_outstanding) {
130 retval = -EINPROGRESS;
131 goto out;
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;
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;
146 if (in && str_nums)
147 for (i = 0; i < str_nums; i++)
148 if (str_list[i] >= stream->incnt)
149 goto out;
151 for (i = 0; i < str_nums; i++)
152 str_list[i] = htons(str_list[i]);
154 chunk = sctp_make_strreset_req(asoc, str_nums, str_list, out, in);
156 for (i = 0; i < str_nums; i++)
157 str_list[i] = ntohs(str_list[i]);
159 if (!chunk) {
160 retval = -ENOMEM;
161 goto out;
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;
174 asoc->strreset_chunk = chunk;
175 sctp_chunk_hold(asoc->strreset_chunk);
177 retval = sctp_send_reconf(asoc, chunk);
178 if (retval) {
179 sctp_chunk_put(asoc->strreset_chunk);
180 asoc->strreset_chunk = NULL;
181 if (!out)
182 goto out;
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;
192 goto out;
195 asoc->strreset_outstanding = out + in;
197 out:
198 return retval;
201 int sctp_send_reset_assoc(struct sctp_association *asoc)
203 struct sctp_stream *stream = &asoc->stream;
204 struct sctp_chunk *chunk = NULL;
205 int retval;
206 __u16 i;
208 if (!asoc->peer.reconf_capable ||
209 !(asoc->strreset_enable & SCTP_ENABLE_RESET_ASSOC_REQ))
210 return -ENOPROTOOPT;
212 if (asoc->strreset_outstanding)
213 return -EINPROGRESS;
215 chunk = sctp_make_strreset_tsnreq(asoc);
216 if (!chunk)
217 return -ENOMEM;
219 /* Block further xmit of data until this request is completed */
220 for (i = 0; i < stream->outcnt; i++)
221 stream->out[i].state = SCTP_STREAM_CLOSED;
223 asoc->strreset_chunk = chunk;
224 sctp_chunk_hold(asoc->strreset_chunk);
226 retval = sctp_send_reconf(asoc, chunk);
227 if (retval) {
228 sctp_chunk_put(asoc->strreset_chunk);
229 asoc->strreset_chunk = NULL;
231 for (i = 0; i < stream->outcnt; i++)
232 stream->out[i].state = SCTP_STREAM_OPEN;
234 return retval;
237 asoc->strreset_outstanding = 1;
239 return 0;
242 int sctp_send_add_streams(struct sctp_association *asoc,
243 struct sctp_add_streams *params)
245 struct sctp_stream *stream = &asoc->stream;
246 struct sctp_chunk *chunk = NULL;
247 int retval = -ENOMEM;
248 __u32 outcnt, incnt;
249 __u16 out, in;
251 if (!asoc->peer.reconf_capable ||
252 !(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ)) {
253 retval = -ENOPROTOOPT;
254 goto out;
257 if (asoc->strreset_outstanding) {
258 retval = -EINPROGRESS;
259 goto out;
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;
272 if (out) {
273 struct sctp_stream_out *streamout;
275 streamout = krealloc(stream->out, outcnt * sizeof(*streamout),
276 GFP_KERNEL);
277 if (!streamout)
278 goto out;
280 memset(streamout + stream->outcnt, 0, out * sizeof(*streamout));
281 stream->out = streamout;
284 chunk = sctp_make_strreset_addstrm(asoc, out, in);
285 if (!chunk)
286 goto out;
288 asoc->strreset_chunk = chunk;
289 sctp_chunk_hold(asoc->strreset_chunk);
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;
298 stream->incnt = incnt;
299 stream->outcnt = outcnt;
301 asoc->strreset_outstanding = !!out + !!in;
303 out:
304 return retval;
307 static struct sctp_paramhdr *sctp_chunk_lookup_strreset_param(
308 struct sctp_association *asoc, __u32 resp_seq,
309 __be16 type)
311 struct sctp_chunk *chunk = asoc->strreset_chunk;
312 struct sctp_reconf_chunk *hdr;
313 union sctp_params param;
315 if (!chunk)
316 return NULL;
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.
324 struct sctp_strreset_tsnreq *req = param.v;
326 if ((!resp_seq || req->request_seq == resp_seq) &&
327 (!type || type == req->param_hdr.type))
328 return param.v;
331 return NULL;
334 static void sctp_update_strreset_result(struct sctp_association *asoc,
335 __u32 result)
337 asoc->strreset_result[1] = asoc->strreset_result[0];
338 asoc->strreset_result[0] = result;
341 struct sctp_chunk *sctp_process_strreset_outreq(
342 struct sctp_association *asoc,
343 union sctp_params param,
344 struct sctp_ulpevent **evp)
346 struct sctp_strreset_outreq *outreq = param.v;
347 struct sctp_stream *stream = &asoc->stream;
348 __u16 i, nums, flags = 0, *str_p = NULL;
349 __u32 result = SCTP_STRRESET_DENIED;
350 __u32 request_seq;
352 request_seq = ntohl(outreq->request_seq);
354 if (ntohl(outreq->send_reset_at_tsn) >
355 sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map)) {
356 result = SCTP_STRRESET_IN_PROGRESS;
357 goto err;
360 if (TSN_lt(asoc->strreset_inseq, request_seq) ||
361 TSN_lt(request_seq, asoc->strreset_inseq - 2)) {
362 result = SCTP_STRRESET_ERR_BAD_SEQNO;
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;
369 asoc->strreset_inseq++;
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.
375 if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ))
376 goto out;
378 if (asoc->strreset_chunk) {
379 if (!sctp_chunk_lookup_strreset_param(
380 asoc, outreq->response_seq,
381 SCTP_PARAM_RESET_IN_REQUEST)) {
382 /* same process with outstanding isn't 0 */
383 result = SCTP_STRRESET_ERR_IN_PROGRESS;
384 goto out;
387 asoc->strreset_outstanding--;
388 asoc->strreset_outseq++;
390 if (!asoc->strreset_outstanding) {
391 struct sctp_transport *t;
393 t = asoc->strreset_chunk->transport;
394 if (del_timer(&t->reconf_timer))
395 sctp_transport_put(t);
397 sctp_chunk_put(asoc->strreset_chunk);
398 asoc->strreset_chunk = NULL;
401 flags = SCTP_STREAM_RESET_INCOMING_SSN;
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;
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;
421 result = SCTP_STRRESET_PERFORMED;
423 *evp = sctp_ulpevent_make_stream_reset_event(asoc,
424 flags | SCTP_STREAM_RESET_OUTGOING_SSN, nums, str_p,
425 GFP_ATOMIC);
427 out:
428 sctp_update_strreset_result(asoc, result);
429 err:
430 return sctp_make_strreset_resp(asoc, result, request_seq);
433 struct sctp_chunk *sctp_process_strreset_inreq(
434 struct sctp_association *asoc,
435 union sctp_params param,
436 struct sctp_ulpevent **evp)
438 struct sctp_strreset_inreq *inreq = param.v;
439 struct sctp_stream *stream = &asoc->stream;
440 __u32 result = SCTP_STRRESET_DENIED;
441 struct sctp_chunk *chunk = NULL;
442 __u16 i, nums, *str_p;
443 __u32 request_seq;
445 request_seq = ntohl(inreq->request_seq);
446 if (TSN_lt(asoc->strreset_inseq, request_seq) ||
447 TSN_lt(request_seq, asoc->strreset_inseq - 2)) {
448 result = SCTP_STRRESET_ERR_BAD_SEQNO;
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;
457 asoc->strreset_inseq++;
459 if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ))
460 goto out;
462 if (asoc->strreset_outstanding) {
463 result = SCTP_STRRESET_ERR_IN_PROGRESS;
464 goto out;
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;
476 chunk = sctp_make_strreset_req(asoc, nums, str_p, 1, 0);
477 if (!chunk)
478 goto out;
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;
488 asoc->strreset_chunk = chunk;
489 asoc->strreset_outstanding = 1;
490 sctp_chunk_hold(asoc->strreset_chunk);
492 result = SCTP_STRRESET_PERFORMED;
494 *evp = sctp_ulpevent_make_stream_reset_event(asoc,
495 SCTP_STREAM_RESET_INCOMING_SSN, nums, str_p, GFP_ATOMIC);
497 out:
498 sctp_update_strreset_result(asoc, result);
499 err:
500 if (!chunk)
501 chunk = sctp_make_strreset_resp(asoc, result, request_seq);
503 return chunk;
506 struct sctp_chunk *sctp_process_strreset_tsnreq(
507 struct sctp_association *asoc,
508 union sctp_params param,
509 struct sctp_ulpevent **evp)
511 __u32 init_tsn = 0, next_tsn = 0, max_tsn_seen;
512 struct sctp_strreset_tsnreq *tsnreq = param.v;
513 struct sctp_stream *stream = &asoc->stream;
514 __u32 result = SCTP_STRRESET_DENIED;
515 __u32 request_seq;
516 __u16 i;
518 request_seq = ntohl(tsnreq->request_seq);
519 if (TSN_lt(asoc->strreset_inseq, request_seq) ||
520 TSN_lt(request_seq, asoc->strreset_inseq - 2)) {
521 result = SCTP_STRRESET_ERR_BAD_SEQNO;
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;
531 goto err;
533 asoc->strreset_inseq++;
535 if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_ASSOC_REQ))
536 goto out;
538 if (asoc->strreset_outstanding) {
539 result = SCTP_STRRESET_ERR_IN_PROGRESS;
540 goto out;
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.
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);
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.
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);
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.
565 sctp_outq_free(&asoc->outqueue);
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.
572 next_tsn = asoc->next_tsn;
573 asoc->ctsn_ack_point = next_tsn - 1;
574 asoc->adv_peer_ack_point = asoc->ctsn_ack_point;
576 /* G5: The next expected and outgoing SSNs MUST be reset to 0 for all
577 * incoming and outgoing streams.
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;
584 result = SCTP_STRRESET_PERFORMED;
586 *evp = sctp_ulpevent_make_assoc_reset_event(asoc, 0, init_tsn,
587 next_tsn, GFP_ATOMIC);
589 out:
590 sctp_update_strreset_result(asoc, result);
591 err:
592 return sctp_make_strreset_tsnresp(asoc, result, request_seq,
593 next_tsn, init_tsn);
596 struct sctp_chunk *sctp_process_strreset_addstrm_out(
597 struct sctp_association *asoc,
598 union sctp_params param,
599 struct sctp_ulpevent **evp)
601 struct sctp_strreset_addstrm *addstrm = param.v;
602 struct sctp_stream *stream = &asoc->stream;
603 __u32 result = SCTP_STRRESET_DENIED;
604 struct sctp_stream_in *streamin;
605 __u32 request_seq, incnt;
606 __u16 in, i;
608 request_seq = ntohl(addstrm->request_seq);
609 if (TSN_lt(asoc->strreset_inseq, request_seq) ||
610 TSN_lt(request_seq, asoc->strreset_inseq - 2)) {
611 result = SCTP_STRRESET_ERR_BAD_SEQNO;
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;
618 asoc->strreset_inseq++;
620 if (!(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ))
621 goto out;
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;
631 asoc->strreset_outstanding--;
632 asoc->strreset_outseq++;
634 if (!asoc->strreset_outstanding) {
635 struct sctp_transport *t;
637 t = asoc->strreset_chunk->transport;
638 if (del_timer(&t->reconf_timer))
639 sctp_transport_put(t);
641 sctp_chunk_put(asoc->strreset_chunk);
642 asoc->strreset_chunk = NULL;
646 in = ntohs(addstrm->number_of_streams);
647 incnt = stream->incnt + in;
648 if (!in || incnt > SCTP_MAX_STREAM)
649 goto out;
651 streamin = krealloc(stream->in, incnt * sizeof(*streamin),
652 GFP_ATOMIC);
653 if (!streamin)
654 goto out;
656 memset(streamin + stream->incnt, 0, in * sizeof(*streamin));
657 stream->in = streamin;
658 stream->incnt = incnt;
660 result = SCTP_STRRESET_PERFORMED;
662 *evp = sctp_ulpevent_make_stream_change_event(asoc,
663 0, ntohs(addstrm->number_of_streams), 0, GFP_ATOMIC);
665 out:
666 sctp_update_strreset_result(asoc, result);
667 err:
668 return sctp_make_strreset_resp(asoc, result, request_seq);
671 struct sctp_chunk *sctp_process_strreset_addstrm_in(
672 struct sctp_association *asoc,
673 union sctp_params param,
674 struct sctp_ulpevent **evp)
676 struct sctp_strreset_addstrm *addstrm = param.v;
677 struct sctp_stream *stream = &asoc->stream;
678 __u32 result = SCTP_STRRESET_DENIED;
679 struct sctp_stream_out *streamout;
680 struct sctp_chunk *chunk = NULL;
681 __u32 request_seq, outcnt;
682 __u16 out, i;
684 request_seq = ntohl(addstrm->request_seq);
685 if (TSN_lt(asoc->strreset_inseq, request_seq) ||
686 TSN_lt(request_seq, asoc->strreset_inseq - 2)) {
687 result = SCTP_STRRESET_ERR_BAD_SEQNO;
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;
696 asoc->strreset_inseq++;
698 if (!(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ))
699 goto out;
701 if (asoc->strreset_outstanding) {
702 result = SCTP_STRRESET_ERR_IN_PROGRESS;
703 goto out;
706 out = ntohs(addstrm->number_of_streams);
707 outcnt = stream->outcnt + out;
708 if (!out || outcnt > SCTP_MAX_STREAM)
709 goto out;
711 streamout = krealloc(stream->out, outcnt * sizeof(*streamout),
712 GFP_ATOMIC);
713 if (!streamout)
714 goto out;
716 memset(streamout + stream->outcnt, 0, out * sizeof(*streamout));
717 stream->out = streamout;
719 chunk = sctp_make_strreset_addstrm(asoc, out, 0);
720 if (!chunk)
721 goto out;
723 asoc->strreset_chunk = chunk;
724 asoc->strreset_outstanding = 1;
725 sctp_chunk_hold(asoc->strreset_chunk);
727 stream->outcnt = outcnt;
729 result = SCTP_STRRESET_PERFORMED;
731 *evp = sctp_ulpevent_make_stream_change_event(asoc,
732 0, 0, ntohs(addstrm->number_of_streams), GFP_ATOMIC);
734 out:
735 sctp_update_strreset_result(asoc, result);
736 err:
737 if (!chunk)
738 chunk = sctp_make_strreset_resp(asoc, result, request_seq);
740 return chunk;
743 struct sctp_chunk *sctp_process_strreset_resp(
744 struct sctp_association *asoc,
745 union sctp_params param,
746 struct sctp_ulpevent **evp)
748 struct sctp_stream *stream = &asoc->stream;
749 struct sctp_strreset_resp *resp = param.v;
750 struct sctp_transport *t;
751 __u16 i, nums, flags = 0;
752 struct sctp_paramhdr *req;
753 __u32 result;
755 req = sctp_chunk_lookup_strreset_param(asoc, resp->response_seq, 0);
756 if (!req)
757 return NULL;
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;
770 if (req->type == SCTP_PARAM_RESET_OUT_REQUEST) {
771 struct sctp_strreset_outreq *outreq;
772 __u16 *str_p;
774 outreq = (struct sctp_strreset_outreq *)req;
775 str_p = outreq->list_of_streams;
776 nums = (ntohs(outreq->param_hdr.length) - sizeof(*outreq)) / 2;
778 if (result == SCTP_STRRESET_PERFORMED) {
779 if (nums) {
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;
787 flags = SCTP_STREAM_RESET_OUTGOING_SSN;
790 for (i = 0; i < stream->outcnt; i++)
791 stream->out[i].state = SCTP_STREAM_OPEN;
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;
797 __u16 *str_p;
799 /* if the result is performed, it's impossible for inreq */
800 if (result == SCTP_STRRESET_PERFORMED)
801 return NULL;
803 inreq = (struct sctp_strreset_inreq *)req;
804 str_p = inreq->list_of_streams;
805 nums = (ntohs(inreq->param_hdr.length) - sizeof(*inreq)) / 2;
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;
813 /* check for resptsn, as sctp_verify_reconf didn't do it*/
814 if (ntohs(param.p->length) != sizeof(*resptsn))
815 return NULL;
817 resptsn = (struct sctp_strreset_resptsn *)resp;
818 stsn = ntohl(resptsn->senders_next_tsn);
819 rtsn = ntohl(resptsn->receivers_next_tsn);
821 if (result == SCTP_STRRESET_PERFORMED) {
822 __u32 mtsn = sctp_tsnmap_get_max_tsn_seen(
823 &asoc->peer.tsn_map);
825 sctp_ulpq_reasm_flushtsn(&asoc->ulpq, mtsn);
826 sctp_ulpq_abort_pd(&asoc->ulpq, GFP_ATOMIC);
828 sctp_tsnmap_init(&asoc->peer.tsn_map,
829 SCTP_TSN_MAP_INITIAL,
830 stsn, GFP_ATOMIC);
832 sctp_outq_free(&asoc->outqueue);
834 asoc->next_tsn = rtsn;
835 asoc->ctsn_ack_point = asoc->next_tsn - 1;
836 asoc->adv_peer_ack_point = asoc->ctsn_ack_point;
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;
844 for (i = 0; i < stream->outcnt; i++)
845 stream->out[i].state = SCTP_STREAM_OPEN;
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;
853 addstrm = (struct sctp_strreset_addstrm *)req;
854 nums = ntohs(addstrm->number_of_streams);
855 number = stream->outcnt - nums;
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;
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;
868 /* if the result is performed, it's impossible for addstrm in
869 * request.
871 if (result == SCTP_STRRESET_PERFORMED)
872 return NULL;
874 addstrm = (struct sctp_strreset_addstrm *)req;
875 nums = ntohs(addstrm->number_of_streams);
877 *evp = sctp_ulpevent_make_stream_change_event(asoc, flags,
878 nums, 0, GFP_ATOMIC);
881 asoc->strreset_outstanding--;
882 asoc->strreset_outseq++;
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);
890 sctp_chunk_put(asoc->strreset_chunk);
891 asoc->strreset_chunk = NULL;
894 return NULL;