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