Merge tag 'xfs-4.11-fixes-3' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux
[GitHub/MotorolaMobilityLLC/kernel-slsi.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 }