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