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