]> git.karo-electronics.de Git - karo-tx-linux.git/blob - net/sctp/stream.c
sctp: alloc stream info when initializing asoc
[karo-tx-linux.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 int sctp_stream_new(struct sctp_association *asoc, gfp_t gfp)
39 {
40         struct sctp_stream *stream;
41         int i;
42
43         stream = kzalloc(sizeof(*stream), gfp);
44         if (!stream)
45                 return -ENOMEM;
46
47         stream->outcnt = asoc->c.sinit_num_ostreams;
48         stream->out = kcalloc(stream->outcnt, sizeof(*stream->out), gfp);
49         if (!stream->out) {
50                 kfree(stream);
51                 return -ENOMEM;
52         }
53         for (i = 0; i < stream->outcnt; i++)
54                 stream->out[i].state = SCTP_STREAM_OPEN;
55
56         asoc->stream = stream;
57
58         return 0;
59 }
60
61 int sctp_stream_init(struct sctp_association *asoc, gfp_t gfp)
62 {
63         struct sctp_stream *stream = asoc->stream;
64         int i;
65
66         /* Initial stream->out size may be very big, so free it and alloc
67          * a new one with new outcnt to save memory.
68          */
69         kfree(stream->out);
70         stream->outcnt = asoc->c.sinit_num_ostreams;
71         stream->out = kcalloc(stream->outcnt, sizeof(*stream->out), gfp);
72         if (!stream->out)
73                 goto nomem;
74
75         for (i = 0; i < stream->outcnt; i++)
76                 stream->out[i].state = SCTP_STREAM_OPEN;
77
78         stream->incnt = asoc->c.sinit_max_instreams;
79         stream->in = kcalloc(stream->incnt, sizeof(*stream->in), gfp);
80         if (!stream->in) {
81                 kfree(stream->out);
82                 goto nomem;
83         }
84
85         return 0;
86
87 nomem:
88         asoc->stream = NULL;
89         kfree(stream);
90
91         return -ENOMEM;
92 }
93
94 void sctp_stream_free(struct sctp_stream *stream)
95 {
96         if (unlikely(!stream))
97                 return;
98
99         kfree(stream->out);
100         kfree(stream->in);
101         kfree(stream);
102 }
103
104 void sctp_stream_clear(struct sctp_stream *stream)
105 {
106         int i;
107
108         for (i = 0; i < stream->outcnt; i++)
109                 stream->out[i].ssn = 0;
110
111         for (i = 0; i < stream->incnt; i++)
112                 stream->in[i].ssn = 0;
113 }
114
115 static int sctp_send_reconf(struct sctp_association *asoc,
116                             struct sctp_chunk *chunk)
117 {
118         struct net *net = sock_net(asoc->base.sk);
119         int retval = 0;
120
121         retval = sctp_primitive_RECONF(net, asoc, chunk);
122         if (retval)
123                 sctp_chunk_free(chunk);
124
125         return retval;
126 }
127
128 int sctp_send_reset_streams(struct sctp_association *asoc,
129                             struct sctp_reset_streams *params)
130 {
131         struct sctp_stream *stream = asoc->stream;
132         __u16 i, str_nums, *str_list;
133         struct sctp_chunk *chunk;
134         int retval = -EINVAL;
135         bool out, in;
136
137         if (!asoc->peer.reconf_capable ||
138             !(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ)) {
139                 retval = -ENOPROTOOPT;
140                 goto out;
141         }
142
143         if (asoc->strreset_outstanding) {
144                 retval = -EINPROGRESS;
145                 goto out;
146         }
147
148         out = params->srs_flags & SCTP_STREAM_RESET_OUTGOING;
149         in  = params->srs_flags & SCTP_STREAM_RESET_INCOMING;
150         if (!out && !in)
151                 goto out;
152
153         str_nums = params->srs_number_streams;
154         str_list = params->srs_stream_list;
155         if (out && str_nums)
156                 for (i = 0; i < str_nums; i++)
157                         if (str_list[i] >= stream->outcnt)
158                                 goto out;
159
160         if (in && str_nums)
161                 for (i = 0; i < str_nums; i++)
162                         if (str_list[i] >= stream->incnt)
163                                 goto out;
164
165         for (i = 0; i < str_nums; i++)
166                 str_list[i] = htons(str_list[i]);
167
168         chunk = sctp_make_strreset_req(asoc, str_nums, str_list, out, in);
169
170         for (i = 0; i < str_nums; i++)
171                 str_list[i] = ntohs(str_list[i]);
172
173         if (!chunk) {
174                 retval = -ENOMEM;
175                 goto out;
176         }
177
178         if (out) {
179                 if (str_nums)
180                         for (i = 0; i < str_nums; i++)
181                                 stream->out[str_list[i]].state =
182                                                        SCTP_STREAM_CLOSED;
183                 else
184                         for (i = 0; i < stream->outcnt; i++)
185                                 stream->out[i].state = SCTP_STREAM_CLOSED;
186         }
187
188         asoc->strreset_chunk = chunk;
189         sctp_chunk_hold(asoc->strreset_chunk);
190
191         retval = sctp_send_reconf(asoc, chunk);
192         if (retval) {
193                 sctp_chunk_put(asoc->strreset_chunk);
194                 asoc->strreset_chunk = NULL;
195                 if (!out)
196                         goto out;
197
198                 if (str_nums)
199                         for (i = 0; i < str_nums; i++)
200                                 stream->out[str_list[i]].state =
201                                                        SCTP_STREAM_OPEN;
202                 else
203                         for (i = 0; i < stream->outcnt; i++)
204                                 stream->out[i].state = SCTP_STREAM_OPEN;
205
206                 goto out;
207         }
208
209         asoc->strreset_outstanding = out + in;
210
211 out:
212         return retval;
213 }
214
215 int sctp_send_reset_assoc(struct sctp_association *asoc)
216 {
217         struct sctp_chunk *chunk = NULL;
218         int retval;
219         __u16 i;
220
221         if (!asoc->peer.reconf_capable ||
222             !(asoc->strreset_enable & SCTP_ENABLE_RESET_ASSOC_REQ))
223                 return -ENOPROTOOPT;
224
225         if (asoc->strreset_outstanding)
226                 return -EINPROGRESS;
227
228         chunk = sctp_make_strreset_tsnreq(asoc);
229         if (!chunk)
230                 return -ENOMEM;
231
232         /* Block further xmit of data until this request is completed */
233         for (i = 0; i < asoc->stream->outcnt; i++)
234                 asoc->stream->out[i].state = SCTP_STREAM_CLOSED;
235
236         asoc->strreset_chunk = chunk;
237         sctp_chunk_hold(asoc->strreset_chunk);
238
239         retval = sctp_send_reconf(asoc, chunk);
240         if (retval) {
241                 sctp_chunk_put(asoc->strreset_chunk);
242                 asoc->strreset_chunk = NULL;
243
244                 for (i = 0; i < asoc->stream->outcnt; i++)
245                         asoc->stream->out[i].state = SCTP_STREAM_OPEN;
246
247                 return retval;
248         }
249
250         asoc->strreset_outstanding = 1;
251
252         return 0;
253 }
254
255 int sctp_send_add_streams(struct sctp_association *asoc,
256                           struct sctp_add_streams *params)
257 {
258         struct sctp_stream *stream = asoc->stream;
259         struct sctp_chunk *chunk = NULL;
260         int retval = -ENOMEM;
261         __u32 outcnt, incnt;
262         __u16 out, in;
263
264         if (!asoc->peer.reconf_capable ||
265             !(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ)) {
266                 retval = -ENOPROTOOPT;
267                 goto out;
268         }
269
270         if (asoc->strreset_outstanding) {
271                 retval = -EINPROGRESS;
272                 goto out;
273         }
274
275         out = params->sas_outstrms;
276         in  = params->sas_instrms;
277         outcnt = stream->outcnt + out;
278         incnt = stream->incnt + in;
279         if (outcnt > SCTP_MAX_STREAM || incnt > SCTP_MAX_STREAM ||
280             (!out && !in)) {
281                 retval = -EINVAL;
282                 goto out;
283         }
284
285         if (out) {
286                 struct sctp_stream_out *streamout;
287
288                 streamout = krealloc(stream->out, outcnt * sizeof(*streamout),
289                                      GFP_KERNEL);
290                 if (!streamout)
291                         goto out;
292
293                 memset(streamout + stream->outcnt, 0, out * sizeof(*streamout));
294                 stream->out = streamout;
295         }
296
297         if (in) {
298                 struct sctp_stream_in *streamin;
299
300                 streamin = krealloc(stream->in, incnt * sizeof(*streamin),
301                                     GFP_KERNEL);
302                 if (!streamin)
303                         goto out;
304
305                 memset(streamin + stream->incnt, 0, in * sizeof(*streamin));
306                 stream->in = streamin;
307         }
308
309         chunk = sctp_make_strreset_addstrm(asoc, out, in);
310         if (!chunk)
311                 goto out;
312
313         asoc->strreset_chunk = chunk;
314         sctp_chunk_hold(asoc->strreset_chunk);
315
316         retval = sctp_send_reconf(asoc, chunk);
317         if (retval) {
318                 sctp_chunk_put(asoc->strreset_chunk);
319                 asoc->strreset_chunk = NULL;
320                 goto out;
321         }
322
323         stream->incnt = incnt;
324         stream->outcnt = outcnt;
325
326         asoc->strreset_outstanding = !!out + !!in;
327
328 out:
329         return retval;
330 }
331
332 static sctp_paramhdr_t *sctp_chunk_lookup_strreset_param(
333                         struct sctp_association *asoc, __u32 resp_seq)
334 {
335         struct sctp_chunk *chunk = asoc->strreset_chunk;
336         struct sctp_reconf_chunk *hdr;
337         union sctp_params param;
338
339         if (ntohl(resp_seq) != asoc->strreset_outseq || !chunk)
340                 return NULL;
341
342         hdr = (struct sctp_reconf_chunk *)chunk->chunk_hdr;
343         sctp_walk_params(param, hdr, params) {
344                 /* sctp_strreset_tsnreq is actually the basic structure
345                  * of all stream reconf params, so it's safe to use it
346                  * to access request_seq.
347                  */
348                 struct sctp_strreset_tsnreq *req = param.v;
349
350                 if (req->request_seq == resp_seq)
351                         return param.v;
352         }
353
354         return NULL;
355 }
356
357 struct sctp_chunk *sctp_process_strreset_outreq(
358                                 struct sctp_association *asoc,
359                                 union sctp_params param,
360                                 struct sctp_ulpevent **evp)
361 {
362         struct sctp_strreset_outreq *outreq = param.v;
363         struct sctp_stream *stream = asoc->stream;
364         __u16 i, nums, flags = 0, *str_p = NULL;
365         __u32 result = SCTP_STRRESET_DENIED;
366         __u32 request_seq;
367
368         request_seq = ntohl(outreq->request_seq);
369
370         if (ntohl(outreq->send_reset_at_tsn) >
371             sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map)) {
372                 result = SCTP_STRRESET_IN_PROGRESS;
373                 goto out;
374         }
375
376         if (request_seq > asoc->strreset_inseq) {
377                 result = SCTP_STRRESET_ERR_BAD_SEQNO;
378                 goto out;
379         } else if (request_seq == asoc->strreset_inseq) {
380                 asoc->strreset_inseq++;
381         }
382
383         /* Check strreset_enable after inseq inc, as sender cannot tell
384          * the peer doesn't enable strreset after receiving response with
385          * result denied, as well as to keep consistent with bsd.
386          */
387         if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ))
388                 goto out;
389
390         if (asoc->strreset_chunk) {
391                 sctp_paramhdr_t *param_hdr;
392                 struct sctp_transport *t;
393
394                 param_hdr = sctp_chunk_lookup_strreset_param(
395                                         asoc, outreq->response_seq);
396                 if (!param_hdr || param_hdr->type !=
397                                         SCTP_PARAM_RESET_IN_REQUEST) {
398                         /* same process with outstanding isn't 0 */
399                         result = SCTP_STRRESET_ERR_IN_PROGRESS;
400                         goto out;
401                 }
402
403                 asoc->strreset_outstanding--;
404                 asoc->strreset_outseq++;
405
406                 if (!asoc->strreset_outstanding) {
407                         t = asoc->strreset_chunk->transport;
408                         if (del_timer(&t->reconf_timer))
409                                 sctp_transport_put(t);
410
411                         sctp_chunk_put(asoc->strreset_chunk);
412                         asoc->strreset_chunk = NULL;
413                 }
414
415                 flags = SCTP_STREAM_RESET_INCOMING_SSN;
416         }
417
418         nums = (ntohs(param.p->length) - sizeof(*outreq)) / 2;
419         if (nums) {
420                 str_p = outreq->list_of_streams;
421                 for (i = 0; i < nums; i++) {
422                         if (ntohs(str_p[i]) >= stream->incnt) {
423                                 result = SCTP_STRRESET_ERR_WRONG_SSN;
424                                 goto out;
425                         }
426                 }
427
428                 for (i = 0; i < nums; i++)
429                         stream->in[ntohs(str_p[i])].ssn = 0;
430         } else {
431                 for (i = 0; i < stream->incnt; i++)
432                         stream->in[i].ssn = 0;
433         }
434
435         result = SCTP_STRRESET_PERFORMED;
436
437         *evp = sctp_ulpevent_make_stream_reset_event(asoc,
438                 flags | SCTP_STREAM_RESET_OUTGOING_SSN, nums, str_p,
439                 GFP_ATOMIC);
440
441 out:
442         return sctp_make_strreset_resp(asoc, result, request_seq);
443 }
444
445 struct sctp_chunk *sctp_process_strreset_inreq(
446                                 struct sctp_association *asoc,
447                                 union sctp_params param,
448                                 struct sctp_ulpevent **evp)
449 {
450         struct sctp_strreset_inreq *inreq = param.v;
451         struct sctp_stream *stream = asoc->stream;
452         __u32 result = SCTP_STRRESET_DENIED;
453         struct sctp_chunk *chunk = NULL;
454         __u16 i, nums, *str_p;
455         __u32 request_seq;
456
457         request_seq = ntohl(inreq->request_seq);
458         if (request_seq > asoc->strreset_inseq) {
459                 result = SCTP_STRRESET_ERR_BAD_SEQNO;
460                 goto out;
461         } else if (request_seq == asoc->strreset_inseq) {
462                 asoc->strreset_inseq++;
463         }
464
465         if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ))
466                 goto out;
467
468         if (asoc->strreset_outstanding) {
469                 result = SCTP_STRRESET_ERR_IN_PROGRESS;
470                 goto out;
471         }
472
473         nums = (ntohs(param.p->length) - sizeof(*inreq)) / 2;
474         str_p = inreq->list_of_streams;
475         for (i = 0; i < nums; i++) {
476                 if (ntohs(str_p[i]) >= stream->outcnt) {
477                         result = SCTP_STRRESET_ERR_WRONG_SSN;
478                         goto out;
479                 }
480         }
481
482         chunk = sctp_make_strreset_req(asoc, nums, str_p, 1, 0);
483         if (!chunk)
484                 goto out;
485
486         if (nums)
487                 for (i = 0; i < nums; i++)
488                         stream->out[ntohs(str_p[i])].state =
489                                                SCTP_STREAM_CLOSED;
490         else
491                 for (i = 0; i < stream->outcnt; i++)
492                         stream->out[i].state = SCTP_STREAM_CLOSED;
493
494         asoc->strreset_chunk = chunk;
495         asoc->strreset_outstanding = 1;
496         sctp_chunk_hold(asoc->strreset_chunk);
497
498         *evp = sctp_ulpevent_make_stream_reset_event(asoc,
499                 SCTP_STREAM_RESET_INCOMING_SSN, nums, str_p, GFP_ATOMIC);
500
501 out:
502         if (!chunk)
503                 chunk =  sctp_make_strreset_resp(asoc, result, request_seq);
504
505         return chunk;
506 }