]>
Commit | Line | Data |
---|---|---|
a8386317 XL |
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> | |
7f9d68ac | 36 | #include <net/sctp/sm.h> |
a8386317 | 37 | |
3dbcc105 | 38 | int sctp_stream_new(struct sctp_association *asoc, gfp_t gfp) |
a8386317 XL |
39 | { |
40 | struct sctp_stream *stream; | |
41 | int i; | |
42 | ||
43 | stream = kzalloc(sizeof(*stream), gfp); | |
44 | if (!stream) | |
3dbcc105 | 45 | return -ENOMEM; |
a8386317 | 46 | |
3dbcc105 | 47 | stream->outcnt = asoc->c.sinit_num_ostreams; |
a8386317 XL |
48 | stream->out = kcalloc(stream->outcnt, sizeof(*stream->out), gfp); |
49 | if (!stream->out) { | |
50 | kfree(stream); | |
3dbcc105 | 51 | return -ENOMEM; |
a8386317 XL |
52 | } |
53 | for (i = 0; i < stream->outcnt; i++) | |
54 | stream->out[i].state = SCTP_STREAM_OPEN; | |
55 | ||
3dbcc105 XL |
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; | |
a8386317 XL |
79 | stream->in = kcalloc(stream->incnt, sizeof(*stream->in), gfp); |
80 | if (!stream->in) { | |
81 | kfree(stream->out); | |
3dbcc105 | 82 | goto nomem; |
a8386317 XL |
83 | } |
84 | ||
3dbcc105 XL |
85 | return 0; |
86 | ||
87 | nomem: | |
88 | asoc->stream = NULL; | |
89 | kfree(stream); | |
90 | ||
91 | return -ENOMEM; | |
a8386317 XL |
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 | } | |
7f9d68ac XL |
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 | ||
16e1a919 XL |
165 | for (i = 0; i < str_nums; i++) |
166 | str_list[i] = htons(str_list[i]); | |
167 | ||
7f9d68ac | 168 | chunk = sctp_make_strreset_req(asoc, str_nums, str_list, out, in); |
16e1a919 XL |
169 | |
170 | for (i = 0; i < str_nums; i++) | |
171 | str_list[i] = ntohs(str_list[i]); | |
172 | ||
119aecba XL |
173 | if (!chunk) { |
174 | retval = -ENOMEM; | |
7f9d68ac | 175 | goto out; |
119aecba | 176 | } |
7f9d68ac XL |
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 | ||
7f9d68ac XL |
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; | |
119aecba XL |
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; | |
7f9d68ac XL |
207 | } |
208 | ||
119aecba XL |
209 | asoc->strreset_outstanding = out + in; |
210 | ||
7f9d68ac XL |
211 | out: |
212 | return retval; | |
213 | } | |
a92ce1a4 XL |
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 | } | |
242bd2d5 XL |
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 | ||
242bd2d5 XL |
297 | chunk = sctp_make_strreset_addstrm(asoc, out, in); |
298 | if (!chunk) | |
299 | goto out; | |
300 | ||
301 | asoc->strreset_chunk = chunk; | |
302 | sctp_chunk_hold(asoc->strreset_chunk); | |
303 | ||
304 | retval = sctp_send_reconf(asoc, chunk); | |
305 | if (retval) { | |
306 | sctp_chunk_put(asoc->strreset_chunk); | |
307 | asoc->strreset_chunk = NULL; | |
308 | goto out; | |
309 | } | |
310 | ||
311 | stream->incnt = incnt; | |
312 | stream->outcnt = outcnt; | |
313 | ||
314 | asoc->strreset_outstanding = !!out + !!in; | |
315 | ||
316 | out: | |
317 | return retval; | |
318 | } | |
81054476 XL |
319 | |
320 | static sctp_paramhdr_t *sctp_chunk_lookup_strreset_param( | |
50a41591 XL |
321 | struct sctp_association *asoc, __u32 resp_seq, |
322 | __be16 type) | |
81054476 XL |
323 | { |
324 | struct sctp_chunk *chunk = asoc->strreset_chunk; | |
325 | struct sctp_reconf_chunk *hdr; | |
326 | union sctp_params param; | |
327 | ||
50a41591 | 328 | if (!chunk) |
81054476 XL |
329 | return NULL; |
330 | ||
331 | hdr = (struct sctp_reconf_chunk *)chunk->chunk_hdr; | |
332 | sctp_walk_params(param, hdr, params) { | |
333 | /* sctp_strreset_tsnreq is actually the basic structure | |
334 | * of all stream reconf params, so it's safe to use it | |
335 | * to access request_seq. | |
336 | */ | |
337 | struct sctp_strreset_tsnreq *req = param.v; | |
338 | ||
50a41591 XL |
339 | if ((!resp_seq || req->request_seq == resp_seq) && |
340 | (!type || type == req->param_hdr.type)) | |
81054476 XL |
341 | return param.v; |
342 | } | |
343 | ||
344 | return NULL; | |
345 | } | |
346 | ||
347 | struct sctp_chunk *sctp_process_strreset_outreq( | |
348 | struct sctp_association *asoc, | |
349 | union sctp_params param, | |
350 | struct sctp_ulpevent **evp) | |
351 | { | |
352 | struct sctp_strreset_outreq *outreq = param.v; | |
353 | struct sctp_stream *stream = asoc->stream; | |
354 | __u16 i, nums, flags = 0, *str_p = NULL; | |
355 | __u32 result = SCTP_STRRESET_DENIED; | |
356 | __u32 request_seq; | |
357 | ||
358 | request_seq = ntohl(outreq->request_seq); | |
359 | ||
360 | if (ntohl(outreq->send_reset_at_tsn) > | |
361 | sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map)) { | |
362 | result = SCTP_STRRESET_IN_PROGRESS; | |
363 | goto out; | |
364 | } | |
365 | ||
366 | if (request_seq > asoc->strreset_inseq) { | |
367 | result = SCTP_STRRESET_ERR_BAD_SEQNO; | |
368 | goto out; | |
369 | } else if (request_seq == asoc->strreset_inseq) { | |
370 | asoc->strreset_inseq++; | |
371 | } | |
372 | ||
373 | /* Check strreset_enable after inseq inc, as sender cannot tell | |
374 | * the peer doesn't enable strreset after receiving response with | |
375 | * result denied, as well as to keep consistent with bsd. | |
376 | */ | |
377 | if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ)) | |
378 | goto out; | |
379 | ||
380 | if (asoc->strreset_chunk) { | |
50a41591 XL |
381 | if (!sctp_chunk_lookup_strreset_param( |
382 | asoc, outreq->response_seq, | |
383 | SCTP_PARAM_RESET_IN_REQUEST)) { | |
81054476 XL |
384 | /* same process with outstanding isn't 0 */ |
385 | result = SCTP_STRRESET_ERR_IN_PROGRESS; | |
386 | goto out; | |
387 | } | |
388 | ||
389 | asoc->strreset_outstanding--; | |
390 | asoc->strreset_outseq++; | |
391 | ||
392 | if (!asoc->strreset_outstanding) { | |
50a41591 XL |
393 | struct sctp_transport *t; |
394 | ||
81054476 XL |
395 | t = asoc->strreset_chunk->transport; |
396 | if (del_timer(&t->reconf_timer)) | |
397 | sctp_transport_put(t); | |
398 | ||
399 | sctp_chunk_put(asoc->strreset_chunk); | |
400 | asoc->strreset_chunk = NULL; | |
401 | } | |
402 | ||
403 | flags = SCTP_STREAM_RESET_INCOMING_SSN; | |
404 | } | |
405 | ||
406 | nums = (ntohs(param.p->length) - sizeof(*outreq)) / 2; | |
407 | if (nums) { | |
408 | str_p = outreq->list_of_streams; | |
409 | for (i = 0; i < nums; i++) { | |
410 | if (ntohs(str_p[i]) >= stream->incnt) { | |
411 | result = SCTP_STRRESET_ERR_WRONG_SSN; | |
412 | goto out; | |
413 | } | |
414 | } | |
415 | ||
416 | for (i = 0; i < nums; i++) | |
417 | stream->in[ntohs(str_p[i])].ssn = 0; | |
418 | } else { | |
419 | for (i = 0; i < stream->incnt; i++) | |
420 | stream->in[i].ssn = 0; | |
421 | } | |
422 | ||
423 | result = SCTP_STRRESET_PERFORMED; | |
424 | ||
425 | *evp = sctp_ulpevent_make_stream_reset_event(asoc, | |
426 | flags | SCTP_STREAM_RESET_OUTGOING_SSN, nums, str_p, | |
427 | GFP_ATOMIC); | |
428 | ||
429 | out: | |
430 | return sctp_make_strreset_resp(asoc, result, request_seq); | |
431 | } | |
16e1a919 XL |
432 | |
433 | struct sctp_chunk *sctp_process_strreset_inreq( | |
434 | struct sctp_association *asoc, | |
435 | union sctp_params param, | |
436 | struct sctp_ulpevent **evp) | |
437 | { | |
438 | struct sctp_strreset_inreq *inreq = param.v; | |
439 | struct sctp_stream *stream = asoc->stream; | |
440 | __u32 result = SCTP_STRRESET_DENIED; | |
441 | struct sctp_chunk *chunk = NULL; | |
442 | __u16 i, nums, *str_p; | |
443 | __u32 request_seq; | |
444 | ||
445 | request_seq = ntohl(inreq->request_seq); | |
446 | if (request_seq > asoc->strreset_inseq) { | |
447 | result = SCTP_STRRESET_ERR_BAD_SEQNO; | |
448 | goto out; | |
449 | } else if (request_seq == asoc->strreset_inseq) { | |
450 | asoc->strreset_inseq++; | |
451 | } | |
452 | ||
453 | if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ)) | |
454 | goto out; | |
455 | ||
456 | if (asoc->strreset_outstanding) { | |
457 | result = SCTP_STRRESET_ERR_IN_PROGRESS; | |
458 | goto out; | |
459 | } | |
460 | ||
461 | nums = (ntohs(param.p->length) - sizeof(*inreq)) / 2; | |
462 | str_p = inreq->list_of_streams; | |
463 | for (i = 0; i < nums; i++) { | |
464 | if (ntohs(str_p[i]) >= stream->outcnt) { | |
465 | result = SCTP_STRRESET_ERR_WRONG_SSN; | |
466 | goto out; | |
467 | } | |
468 | } | |
469 | ||
470 | chunk = sctp_make_strreset_req(asoc, nums, str_p, 1, 0); | |
471 | if (!chunk) | |
472 | goto out; | |
473 | ||
474 | if (nums) | |
475 | for (i = 0; i < nums; i++) | |
476 | stream->out[ntohs(str_p[i])].state = | |
477 | SCTP_STREAM_CLOSED; | |
478 | else | |
479 | for (i = 0; i < stream->outcnt; i++) | |
480 | stream->out[i].state = SCTP_STREAM_CLOSED; | |
481 | ||
482 | asoc->strreset_chunk = chunk; | |
483 | asoc->strreset_outstanding = 1; | |
484 | sctp_chunk_hold(asoc->strreset_chunk); | |
485 | ||
486 | *evp = sctp_ulpevent_make_stream_reset_event(asoc, | |
487 | SCTP_STREAM_RESET_INCOMING_SSN, nums, str_p, GFP_ATOMIC); | |
488 | ||
489 | out: | |
490 | if (!chunk) | |
491 | chunk = sctp_make_strreset_resp(asoc, result, request_seq); | |
492 | ||
493 | return chunk; | |
494 | } | |
692787ce XL |
495 | |
496 | struct sctp_chunk *sctp_process_strreset_tsnreq( | |
497 | struct sctp_association *asoc, | |
498 | union sctp_params param, | |
499 | struct sctp_ulpevent **evp) | |
500 | { | |
501 | __u32 init_tsn = 0, next_tsn = 0, max_tsn_seen; | |
502 | struct sctp_strreset_tsnreq *tsnreq = param.v; | |
503 | struct sctp_stream *stream = asoc->stream; | |
504 | __u32 result = SCTP_STRRESET_DENIED; | |
505 | __u32 request_seq; | |
506 | __u16 i; | |
507 | ||
508 | request_seq = ntohl(tsnreq->request_seq); | |
509 | if (request_seq > asoc->strreset_inseq) { | |
510 | result = SCTP_STRRESET_ERR_BAD_SEQNO; | |
511 | goto out; | |
512 | } else if (request_seq == asoc->strreset_inseq) { | |
513 | asoc->strreset_inseq++; | |
514 | } | |
515 | ||
516 | if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_ASSOC_REQ)) | |
517 | goto out; | |
518 | ||
519 | if (asoc->strreset_outstanding) { | |
520 | result = SCTP_STRRESET_ERR_IN_PROGRESS; | |
521 | goto out; | |
522 | } | |
523 | ||
524 | /* G3: The same processing as though a SACK chunk with no gap report | |
525 | * and a cumulative TSN ACK of the Sender's Next TSN minus 1 were | |
526 | * received MUST be performed. | |
527 | */ | |
528 | max_tsn_seen = sctp_tsnmap_get_max_tsn_seen(&asoc->peer.tsn_map); | |
529 | sctp_ulpq_reasm_flushtsn(&asoc->ulpq, max_tsn_seen); | |
530 | sctp_ulpq_abort_pd(&asoc->ulpq, GFP_ATOMIC); | |
531 | ||
532 | /* G1: Compute an appropriate value for the Receiver's Next TSN -- the | |
533 | * TSN that the peer should use to send the next DATA chunk. The | |
534 | * value SHOULD be the smallest TSN not acknowledged by the | |
535 | * receiver of the request plus 2^31. | |
536 | */ | |
537 | init_tsn = sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map) + (1 << 31); | |
538 | sctp_tsnmap_init(&asoc->peer.tsn_map, SCTP_TSN_MAP_INITIAL, | |
539 | init_tsn, GFP_ATOMIC); | |
540 | ||
541 | /* G4: The same processing as though a FWD-TSN chunk (as defined in | |
542 | * [RFC3758]) with all streams affected and a new cumulative TSN | |
543 | * ACK of the Receiver's Next TSN minus 1 were received MUST be | |
544 | * performed. | |
545 | */ | |
546 | sctp_outq_free(&asoc->outqueue); | |
547 | ||
548 | /* G2: Compute an appropriate value for the local endpoint's next TSN, | |
549 | * i.e., the next TSN assigned by the receiver of the SSN/TSN reset | |
550 | * chunk. The value SHOULD be the highest TSN sent by the receiver | |
551 | * of the request plus 1. | |
552 | */ | |
553 | next_tsn = asoc->next_tsn; | |
554 | asoc->ctsn_ack_point = next_tsn - 1; | |
555 | asoc->adv_peer_ack_point = asoc->ctsn_ack_point; | |
556 | ||
557 | /* G5: The next expected and outgoing SSNs MUST be reset to 0 for all | |
558 | * incoming and outgoing streams. | |
559 | */ | |
560 | for (i = 0; i < stream->outcnt; i++) | |
561 | stream->out[i].ssn = 0; | |
562 | for (i = 0; i < stream->incnt; i++) | |
563 | stream->in[i].ssn = 0; | |
564 | ||
565 | result = SCTP_STRRESET_PERFORMED; | |
566 | ||
567 | *evp = sctp_ulpevent_make_assoc_reset_event(asoc, 0, init_tsn, | |
568 | next_tsn, GFP_ATOMIC); | |
569 | ||
570 | out: | |
571 | return sctp_make_strreset_tsnresp(asoc, result, request_seq, | |
572 | next_tsn, init_tsn); | |
573 | } | |
50a41591 XL |
574 | |
575 | struct sctp_chunk *sctp_process_strreset_addstrm_out( | |
576 | struct sctp_association *asoc, | |
577 | union sctp_params param, | |
578 | struct sctp_ulpevent **evp) | |
579 | { | |
580 | struct sctp_strreset_addstrm *addstrm = param.v; | |
581 | struct sctp_stream *stream = asoc->stream; | |
582 | __u32 result = SCTP_STRRESET_DENIED; | |
583 | struct sctp_stream_in *streamin; | |
584 | __u32 request_seq, incnt; | |
585 | __u16 in; | |
586 | ||
587 | request_seq = ntohl(addstrm->request_seq); | |
588 | if (request_seq > asoc->strreset_inseq) { | |
589 | result = SCTP_STRRESET_ERR_BAD_SEQNO; | |
590 | goto out; | |
591 | } else if (request_seq == asoc->strreset_inseq) { | |
592 | asoc->strreset_inseq++; | |
593 | } | |
594 | ||
595 | if (!(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ)) | |
596 | goto out; | |
597 | ||
598 | if (asoc->strreset_chunk) { | |
599 | if (!sctp_chunk_lookup_strreset_param( | |
600 | asoc, 0, SCTP_PARAM_RESET_ADD_IN_STREAMS)) { | |
601 | /* same process with outstanding isn't 0 */ | |
602 | result = SCTP_STRRESET_ERR_IN_PROGRESS; | |
603 | goto out; | |
604 | } | |
605 | ||
606 | asoc->strreset_outstanding--; | |
607 | asoc->strreset_outseq++; | |
608 | ||
609 | if (!asoc->strreset_outstanding) { | |
610 | struct sctp_transport *t; | |
611 | ||
612 | t = asoc->strreset_chunk->transport; | |
613 | if (del_timer(&t->reconf_timer)) | |
614 | sctp_transport_put(t); | |
615 | ||
616 | sctp_chunk_put(asoc->strreset_chunk); | |
617 | asoc->strreset_chunk = NULL; | |
618 | } | |
619 | } | |
620 | ||
621 | in = ntohs(addstrm->number_of_streams); | |
622 | incnt = stream->incnt + in; | |
623 | if (!in || incnt > SCTP_MAX_STREAM) | |
624 | goto out; | |
625 | ||
626 | streamin = krealloc(stream->in, incnt * sizeof(*streamin), | |
627 | GFP_ATOMIC); | |
628 | if (!streamin) | |
629 | goto out; | |
630 | ||
631 | memset(streamin + stream->incnt, 0, in * sizeof(*streamin)); | |
632 | stream->in = streamin; | |
633 | stream->incnt = incnt; | |
634 | ||
635 | result = SCTP_STRRESET_PERFORMED; | |
636 | ||
637 | *evp = sctp_ulpevent_make_stream_change_event(asoc, | |
638 | 0, ntohs(addstrm->number_of_streams), 0, GFP_ATOMIC); | |
639 | ||
640 | out: | |
641 | return sctp_make_strreset_resp(asoc, result, request_seq); | |
642 | } | |
c5c4ebb3 XL |
643 | |
644 | struct sctp_chunk *sctp_process_strreset_addstrm_in( | |
645 | struct sctp_association *asoc, | |
646 | union sctp_params param, | |
647 | struct sctp_ulpevent **evp) | |
648 | { | |
649 | struct sctp_strreset_addstrm *addstrm = param.v; | |
650 | struct sctp_stream *stream = asoc->stream; | |
651 | __u32 result = SCTP_STRRESET_DENIED; | |
652 | struct sctp_stream_out *streamout; | |
653 | struct sctp_chunk *chunk = NULL; | |
654 | __u32 request_seq, outcnt; | |
655 | __u16 out; | |
656 | ||
657 | request_seq = ntohl(addstrm->request_seq); | |
658 | if (request_seq > asoc->strreset_inseq) { | |
659 | result = SCTP_STRRESET_ERR_BAD_SEQNO; | |
660 | goto out; | |
661 | } else if (request_seq == asoc->strreset_inseq) { | |
662 | asoc->strreset_inseq++; | |
663 | } | |
664 | ||
665 | if (!(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ)) | |
666 | goto out; | |
667 | ||
668 | if (asoc->strreset_outstanding) { | |
669 | result = SCTP_STRRESET_ERR_IN_PROGRESS; | |
670 | goto out; | |
671 | } | |
672 | ||
673 | out = ntohs(addstrm->number_of_streams); | |
674 | outcnt = stream->outcnt + out; | |
675 | if (!out || outcnt > SCTP_MAX_STREAM) | |
676 | goto out; | |
677 | ||
678 | streamout = krealloc(stream->out, outcnt * sizeof(*streamout), | |
679 | GFP_ATOMIC); | |
680 | if (!streamout) | |
681 | goto out; | |
682 | ||
683 | memset(streamout + stream->outcnt, 0, out * sizeof(*streamout)); | |
684 | stream->out = streamout; | |
685 | ||
686 | chunk = sctp_make_strreset_addstrm(asoc, out, 0); | |
687 | if (!chunk) | |
688 | goto out; | |
689 | ||
690 | asoc->strreset_chunk = chunk; | |
691 | asoc->strreset_outstanding = 1; | |
692 | sctp_chunk_hold(asoc->strreset_chunk); | |
693 | ||
694 | stream->outcnt = outcnt; | |
695 | ||
696 | *evp = sctp_ulpevent_make_stream_change_event(asoc, | |
697 | 0, 0, ntohs(addstrm->number_of_streams), GFP_ATOMIC); | |
698 | ||
699 | out: | |
700 | if (!chunk) | |
701 | chunk = sctp_make_strreset_resp(asoc, result, request_seq); | |
702 | ||
703 | return chunk; | |
704 | } | |
11ae76e6 XL |
705 | |
706 | struct sctp_chunk *sctp_process_strreset_resp( | |
707 | struct sctp_association *asoc, | |
708 | union sctp_params param, | |
709 | struct sctp_ulpevent **evp) | |
710 | { | |
711 | struct sctp_strreset_resp *resp = param.v; | |
712 | struct sctp_stream *stream = asoc->stream; | |
713 | struct sctp_transport *t; | |
714 | __u16 i, nums, flags = 0; | |
715 | sctp_paramhdr_t *req; | |
716 | __u32 result; | |
717 | ||
718 | req = sctp_chunk_lookup_strreset_param(asoc, resp->response_seq, 0); | |
719 | if (!req) | |
720 | return NULL; | |
721 | ||
722 | result = ntohl(resp->result); | |
723 | if (result != SCTP_STRRESET_PERFORMED) { | |
724 | /* if in progress, do nothing but retransmit */ | |
725 | if (result == SCTP_STRRESET_IN_PROGRESS) | |
726 | return NULL; | |
727 | else if (result == SCTP_STRRESET_DENIED) | |
728 | flags = SCTP_STREAM_RESET_DENIED; | |
729 | else | |
730 | flags = SCTP_STREAM_RESET_FAILED; | |
731 | } | |
732 | ||
733 | if (req->type == SCTP_PARAM_RESET_OUT_REQUEST) { | |
734 | struct sctp_strreset_outreq *outreq; | |
edb12f2d | 735 | __u16 *str_p; |
11ae76e6 XL |
736 | |
737 | outreq = (struct sctp_strreset_outreq *)req; | |
edb12f2d | 738 | str_p = outreq->list_of_streams; |
11ae76e6 XL |
739 | nums = (ntohs(outreq->param_hdr.length) - sizeof(*outreq)) / 2; |
740 | ||
741 | if (result == SCTP_STRRESET_PERFORMED) { | |
742 | if (nums) { | |
11ae76e6 XL |
743 | for (i = 0; i < nums; i++) |
744 | stream->out[ntohs(str_p[i])].ssn = 0; | |
745 | } else { | |
746 | for (i = 0; i < stream->outcnt; i++) | |
747 | stream->out[i].ssn = 0; | |
748 | } | |
749 | ||
750 | flags = SCTP_STREAM_RESET_OUTGOING_SSN; | |
751 | } | |
752 | ||
753 | for (i = 0; i < stream->outcnt; i++) | |
754 | stream->out[i].state = SCTP_STREAM_OPEN; | |
755 | ||
756 | *evp = sctp_ulpevent_make_stream_reset_event(asoc, flags, | |
757 | nums, str_p, GFP_ATOMIC); | |
758 | } else if (req->type == SCTP_PARAM_RESET_IN_REQUEST) { | |
759 | struct sctp_strreset_inreq *inreq; | |
edb12f2d | 760 | __u16 *str_p; |
11ae76e6 XL |
761 | |
762 | /* if the result is performed, it's impossible for inreq */ | |
763 | if (result == SCTP_STRRESET_PERFORMED) | |
764 | return NULL; | |
765 | ||
766 | inreq = (struct sctp_strreset_inreq *)req; | |
edb12f2d | 767 | str_p = inreq->list_of_streams; |
11ae76e6 XL |
768 | nums = (ntohs(inreq->param_hdr.length) - sizeof(*inreq)) / 2; |
769 | ||
11ae76e6 XL |
770 | *evp = sctp_ulpevent_make_stream_reset_event(asoc, flags, |
771 | nums, str_p, GFP_ATOMIC); | |
772 | } else if (req->type == SCTP_PARAM_RESET_TSN_REQUEST) { | |
773 | struct sctp_strreset_resptsn *resptsn; | |
774 | __u32 stsn, rtsn; | |
775 | ||
776 | /* check for resptsn, as sctp_verify_reconf didn't do it*/ | |
777 | if (ntohs(param.p->length) != sizeof(*resptsn)) | |
778 | return NULL; | |
779 | ||
780 | resptsn = (struct sctp_strreset_resptsn *)resp; | |
781 | stsn = ntohl(resptsn->senders_next_tsn); | |
782 | rtsn = ntohl(resptsn->receivers_next_tsn); | |
783 | ||
784 | if (result == SCTP_STRRESET_PERFORMED) { | |
785 | __u32 mtsn = sctp_tsnmap_get_max_tsn_seen( | |
786 | &asoc->peer.tsn_map); | |
787 | ||
788 | sctp_ulpq_reasm_flushtsn(&asoc->ulpq, mtsn); | |
789 | sctp_ulpq_abort_pd(&asoc->ulpq, GFP_ATOMIC); | |
790 | ||
791 | sctp_tsnmap_init(&asoc->peer.tsn_map, | |
792 | SCTP_TSN_MAP_INITIAL, | |
793 | stsn, GFP_ATOMIC); | |
794 | ||
795 | sctp_outq_free(&asoc->outqueue); | |
796 | ||
797 | asoc->next_tsn = rtsn; | |
798 | asoc->ctsn_ack_point = asoc->next_tsn - 1; | |
799 | asoc->adv_peer_ack_point = asoc->ctsn_ack_point; | |
800 | ||
801 | for (i = 0; i < stream->outcnt; i++) | |
802 | stream->out[i].ssn = 0; | |
803 | for (i = 0; i < stream->incnt; i++) | |
804 | stream->in[i].ssn = 0; | |
805 | } | |
806 | ||
807 | for (i = 0; i < stream->outcnt; i++) | |
808 | stream->out[i].state = SCTP_STREAM_OPEN; | |
809 | ||
810 | *evp = sctp_ulpevent_make_assoc_reset_event(asoc, flags, | |
811 | stsn, rtsn, GFP_ATOMIC); | |
812 | } else if (req->type == SCTP_PARAM_RESET_ADD_OUT_STREAMS) { | |
813 | struct sctp_strreset_addstrm *addstrm; | |
814 | __u16 number; | |
815 | ||
816 | addstrm = (struct sctp_strreset_addstrm *)req; | |
817 | nums = ntohs(addstrm->number_of_streams); | |
818 | number = stream->outcnt - nums; | |
819 | ||
820 | if (result == SCTP_STRRESET_PERFORMED) | |
821 | for (i = number; i < stream->outcnt; i++) | |
822 | stream->out[i].state = SCTP_STREAM_OPEN; | |
823 | else | |
824 | stream->outcnt = number; | |
825 | ||
826 | *evp = sctp_ulpevent_make_stream_change_event(asoc, flags, | |
827 | 0, nums, GFP_ATOMIC); | |
828 | } else if (req->type == SCTP_PARAM_RESET_ADD_IN_STREAMS) { | |
829 | struct sctp_strreset_addstrm *addstrm; | |
830 | ||
831 | /* if the result is performed, it's impossible for addstrm in | |
832 | * request. | |
833 | */ | |
834 | if (result == SCTP_STRRESET_PERFORMED) | |
835 | return NULL; | |
836 | ||
837 | addstrm = (struct sctp_strreset_addstrm *)req; | |
838 | nums = ntohs(addstrm->number_of_streams); | |
839 | ||
840 | *evp = sctp_ulpevent_make_stream_change_event(asoc, flags, | |
841 | nums, 0, GFP_ATOMIC); | |
842 | } | |
843 | ||
844 | asoc->strreset_outstanding--; | |
845 | asoc->strreset_outseq++; | |
846 | ||
847 | /* remove everything for this reconf request */ | |
848 | if (!asoc->strreset_outstanding) { | |
849 | t = asoc->strreset_chunk->transport; | |
850 | if (del_timer(&t->reconf_timer)) | |
851 | sctp_transport_put(t); | |
852 | ||
853 | sctp_chunk_put(asoc->strreset_chunk); | |
854 | asoc->strreset_chunk = NULL; | |
855 | } | |
856 | ||
857 | return NULL; | |
858 | } |