xref: /openbmc/linux/net/sctp/stream.c (revision 4d141581)
147505b8bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2a8386317SXin Long /* SCTP kernel implementation
3a8386317SXin Long  * (C) Copyright IBM Corp. 2001, 2004
4a8386317SXin Long  * Copyright (c) 1999-2000 Cisco, Inc.
5a8386317SXin Long  * Copyright (c) 1999-2001 Motorola, Inc.
6a8386317SXin Long  * Copyright (c) 2001 Intel Corp.
7a8386317SXin Long  *
8a8386317SXin Long  * This file is part of the SCTP kernel implementation
9a8386317SXin Long  *
10fae8b6f4SXin Long  * This file contains sctp stream maniuplation primitives and helpers.
11a8386317SXin Long  *
12a8386317SXin Long  * Please send any bug reports or fixes you make to the
13a8386317SXin Long  * email address(es):
14a8386317SXin Long  *    lksctp developers <linux-sctp@vger.kernel.org>
15a8386317SXin Long  *
16a8386317SXin Long  * Written or modified by:
17a8386317SXin Long  *    Xin Long <lucien.xin@gmail.com>
18a8386317SXin Long  */
19a8386317SXin Long 
205bbbbe32SMarcelo Ricardo Leitner #include <linux/list.h>
21a8386317SXin Long #include <net/sctp/sctp.h>
227f9d68acSXin Long #include <net/sctp/sm.h>
235bbbbe32SMarcelo Ricardo Leitner #include <net/sctp/stream_sched.h>
245bbbbe32SMarcelo Ricardo Leitner 
255bbbbe32SMarcelo Ricardo Leitner /* Migrates chunks from stream queues to new stream queues if needed,
265bbbbe32SMarcelo Ricardo Leitner  * but not across associations. Also, removes those chunks to streams
275bbbbe32SMarcelo Ricardo Leitner  * higher than the new max.
285bbbbe32SMarcelo Ricardo Leitner  */
295bbbbe32SMarcelo Ricardo Leitner static void sctp_stream_outq_migrate(struct sctp_stream *stream,
305bbbbe32SMarcelo Ricardo Leitner 				     struct sctp_stream *new, __u16 outcnt)
315bbbbe32SMarcelo Ricardo Leitner {
325bbbbe32SMarcelo Ricardo Leitner 	struct sctp_association *asoc;
335bbbbe32SMarcelo Ricardo Leitner 	struct sctp_chunk *ch, *temp;
345bbbbe32SMarcelo Ricardo Leitner 	struct sctp_outq *outq;
355bbbbe32SMarcelo Ricardo Leitner 	int i;
365bbbbe32SMarcelo Ricardo Leitner 
375bbbbe32SMarcelo Ricardo Leitner 	asoc = container_of(stream, struct sctp_association, stream);
385bbbbe32SMarcelo Ricardo Leitner 	outq = &asoc->outqueue;
395bbbbe32SMarcelo Ricardo Leitner 
405bbbbe32SMarcelo Ricardo Leitner 	list_for_each_entry_safe(ch, temp, &outq->out_chunk_list, list) {
415bbbbe32SMarcelo Ricardo Leitner 		__u16 sid = sctp_chunk_stream_no(ch);
425bbbbe32SMarcelo Ricardo Leitner 
435bbbbe32SMarcelo Ricardo Leitner 		if (sid < outcnt)
445bbbbe32SMarcelo Ricardo Leitner 			continue;
455bbbbe32SMarcelo Ricardo Leitner 
465bbbbe32SMarcelo Ricardo Leitner 		sctp_sched_dequeue_common(outq, ch);
475bbbbe32SMarcelo Ricardo Leitner 		/* No need to call dequeue_done here because
485bbbbe32SMarcelo Ricardo Leitner 		 * the chunks are not scheduled by now.
495bbbbe32SMarcelo Ricardo Leitner 		 */
505bbbbe32SMarcelo Ricardo Leitner 
515bbbbe32SMarcelo Ricardo Leitner 		/* Mark as failed send. */
5208f46070SXin Long 		sctp_chunk_fail(ch, (__force __u32)SCTP_ERROR_INV_STRM);
535bbbbe32SMarcelo Ricardo Leitner 		if (asoc->peer.prsctp_capable &&
545bbbbe32SMarcelo Ricardo Leitner 		    SCTP_PR_PRIO_ENABLED(ch->sinfo.sinfo_flags))
555bbbbe32SMarcelo Ricardo Leitner 			asoc->sent_cnt_removable--;
565bbbbe32SMarcelo Ricardo Leitner 
575bbbbe32SMarcelo Ricardo Leitner 		sctp_chunk_free(ch);
585bbbbe32SMarcelo Ricardo Leitner 	}
595bbbbe32SMarcelo Ricardo Leitner 
605bbbbe32SMarcelo Ricardo Leitner 	if (new) {
615bbbbe32SMarcelo Ricardo Leitner 		/* Here we actually move the old ext stuff into the new
625bbbbe32SMarcelo Ricardo Leitner 		 * buffer, because we want to keep it. Then
635bbbbe32SMarcelo Ricardo Leitner 		 * sctp_stream_update will swap ->out pointers.
645bbbbe32SMarcelo Ricardo Leitner 		 */
655bbbbe32SMarcelo Ricardo Leitner 		for (i = 0; i < outcnt; i++) {
660d493b4dSKonstantin Khorenko 			kfree(SCTP_SO(new, i)->ext);
670d493b4dSKonstantin Khorenko 			SCTP_SO(new, i)->ext = SCTP_SO(stream, i)->ext;
680d493b4dSKonstantin Khorenko 			SCTP_SO(stream, i)->ext = NULL;
695bbbbe32SMarcelo Ricardo Leitner 		}
705bbbbe32SMarcelo Ricardo Leitner 	}
715bbbbe32SMarcelo Ricardo Leitner 
72af98c5a7SXin Long 	for (i = outcnt; i < stream->outcnt; i++) {
730d493b4dSKonstantin Khorenko 		kfree(SCTP_SO(stream, i)->ext);
74af98c5a7SXin Long 		SCTP_SO(stream, i)->ext = NULL;
75af98c5a7SXin Long 	}
765bbbbe32SMarcelo Ricardo Leitner }
77a8386317SXin Long 
78e090abd0SMarcelo Ricardo Leitner static int sctp_stream_alloc_out(struct sctp_stream *stream, __u16 outcnt,
79e090abd0SMarcelo Ricardo Leitner 				 gfp_t gfp)
80e090abd0SMarcelo Ricardo Leitner {
812075e50cSKent Overstreet 	int ret;
82e090abd0SMarcelo Ricardo Leitner 
832075e50cSKent Overstreet 	if (outcnt <= stream->outcnt)
842075e50cSKent Overstreet 		return 0;
85e090abd0SMarcelo Ricardo Leitner 
862075e50cSKent Overstreet 	ret = genradix_prealloc(&stream->out, outcnt, gfp);
872075e50cSKent Overstreet 	if (ret)
882075e50cSKent Overstreet 		return ret;
89cfe4bd7aSXin Long 
902075e50cSKent Overstreet 	stream->outcnt = outcnt;
91e090abd0SMarcelo Ricardo Leitner 	return 0;
92e090abd0SMarcelo Ricardo Leitner }
93e090abd0SMarcelo Ricardo Leitner 
941fdb8d8fSMarcelo Ricardo Leitner static int sctp_stream_alloc_in(struct sctp_stream *stream, __u16 incnt,
951fdb8d8fSMarcelo Ricardo Leitner 				gfp_t gfp)
961fdb8d8fSMarcelo Ricardo Leitner {
972075e50cSKent Overstreet 	int ret;
981fdb8d8fSMarcelo Ricardo Leitner 
992075e50cSKent Overstreet 	if (incnt <= stream->incnt)
1002075e50cSKent Overstreet 		return 0;
1011fdb8d8fSMarcelo Ricardo Leitner 
1022075e50cSKent Overstreet 	ret = genradix_prealloc(&stream->in, incnt, gfp);
1032075e50cSKent Overstreet 	if (ret)
1042075e50cSKent Overstreet 		return ret;
1051fdb8d8fSMarcelo Ricardo Leitner 
1062075e50cSKent Overstreet 	stream->incnt = incnt;
1071fdb8d8fSMarcelo Ricardo Leitner 	return 0;
1081fdb8d8fSMarcelo Ricardo Leitner }
1091fdb8d8fSMarcelo Ricardo Leitner 
110ff356414SXin Long int sctp_stream_init(struct sctp_stream *stream, __u16 outcnt, __u16 incnt,
111ff356414SXin Long 		     gfp_t gfp)
112a8386317SXin Long {
1135bbbbe32SMarcelo Ricardo Leitner 	struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream);
1145bbbbe32SMarcelo Ricardo Leitner 	int i, ret = 0;
1153dbcc105SXin Long 
1161ae2eaaaSMarcelo Ricardo Leitner 	gfp |= __GFP_NOWARN;
1171ae2eaaaSMarcelo Ricardo Leitner 
1183dbcc105SXin Long 	/* Initial stream->out size may be very big, so free it and alloc
1191ae2eaaaSMarcelo Ricardo Leitner 	 * a new one with new outcnt to save memory if needed.
1203dbcc105SXin Long 	 */
1211ae2eaaaSMarcelo Ricardo Leitner 	if (outcnt == stream->outcnt)
1221ae2eaaaSMarcelo Ricardo Leitner 		goto in;
1231ae2eaaaSMarcelo Ricardo Leitner 
1245bbbbe32SMarcelo Ricardo Leitner 	/* Filter out chunks queued on streams that won't exist anymore */
1255bbbbe32SMarcelo Ricardo Leitner 	sched->unsched_all(stream);
1265bbbbe32SMarcelo Ricardo Leitner 	sctp_stream_outq_migrate(stream, NULL, outcnt);
1275bbbbe32SMarcelo Ricardo Leitner 	sched->sched_all(stream);
1285bbbbe32SMarcelo Ricardo Leitner 
12979d08951SMarcelo Ricardo Leitner 	ret = sctp_stream_alloc_out(stream, outcnt, gfp);
13079d08951SMarcelo Ricardo Leitner 	if (ret)
13179d08951SMarcelo Ricardo Leitner 		goto out;
1323dbcc105SXin Long 
1333dbcc105SXin Long 	for (i = 0; i < stream->outcnt; i++)
13405364ca0SKonstantin Khorenko 		SCTP_SO(stream, i)->state = SCTP_STREAM_OPEN;
1353dbcc105SXin Long 
1361ae2eaaaSMarcelo Ricardo Leitner in:
1370c3f6f65SXin Long 	sctp_stream_interleave_init(stream);
138ff356414SXin Long 	if (!incnt)
1395bbbbe32SMarcelo Ricardo Leitner 		goto out;
140ff356414SXin Long 
14179d08951SMarcelo Ricardo Leitner 	ret = sctp_stream_alloc_in(stream, incnt, gfp);
14279d08951SMarcelo Ricardo Leitner 	if (ret) {
1435bbbbe32SMarcelo Ricardo Leitner 		sched->free(stream);
1442075e50cSKent Overstreet 		genradix_free(&stream->out);
14579d08951SMarcelo Ricardo Leitner 		stream->outcnt = 0;
14679d08951SMarcelo Ricardo Leitner 		goto out;
14779d08951SMarcelo Ricardo Leitner 	}
14879d08951SMarcelo Ricardo Leitner 
1495bbbbe32SMarcelo Ricardo Leitner out:
1505bbbbe32SMarcelo Ricardo Leitner 	return ret;
151a8386317SXin Long }
152a8386317SXin Long 
153f952be79SMarcelo Ricardo Leitner int sctp_stream_init_ext(struct sctp_stream *stream, __u16 sid)
154f952be79SMarcelo Ricardo Leitner {
155f952be79SMarcelo Ricardo Leitner 	struct sctp_stream_out_ext *soute;
1564d141581SMarcelo Ricardo Leitner 	int ret;
157f952be79SMarcelo Ricardo Leitner 
158f952be79SMarcelo Ricardo Leitner 	soute = kzalloc(sizeof(*soute), GFP_KERNEL);
159f952be79SMarcelo Ricardo Leitner 	if (!soute)
160f952be79SMarcelo Ricardo Leitner 		return -ENOMEM;
16105364ca0SKonstantin Khorenko 	SCTP_SO(stream, sid)->ext = soute;
162f952be79SMarcelo Ricardo Leitner 
1634d141581SMarcelo Ricardo Leitner 	ret = sctp_sched_init_sid(stream, sid, GFP_KERNEL);
1644d141581SMarcelo Ricardo Leitner 	if (ret) {
1654d141581SMarcelo Ricardo Leitner 		kfree(SCTP_SO(stream, sid)->ext);
1664d141581SMarcelo Ricardo Leitner 		SCTP_SO(stream, sid)->ext = NULL;
1674d141581SMarcelo Ricardo Leitner 	}
1684d141581SMarcelo Ricardo Leitner 
1694d141581SMarcelo Ricardo Leitner 	return ret;
170f952be79SMarcelo Ricardo Leitner }
171f952be79SMarcelo Ricardo Leitner 
172a8386317SXin Long void sctp_stream_free(struct sctp_stream *stream)
173a8386317SXin Long {
1745bbbbe32SMarcelo Ricardo Leitner 	struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream);
175f952be79SMarcelo Ricardo Leitner 	int i;
176f952be79SMarcelo Ricardo Leitner 
1775bbbbe32SMarcelo Ricardo Leitner 	sched->free(stream);
178f952be79SMarcelo Ricardo Leitner 	for (i = 0; i < stream->outcnt; i++)
17905364ca0SKonstantin Khorenko 		kfree(SCTP_SO(stream, i)->ext);
1802075e50cSKent Overstreet 	genradix_free(&stream->out);
1812075e50cSKent Overstreet 	genradix_free(&stream->in);
182a8386317SXin Long }
183a8386317SXin Long 
184a8386317SXin Long void sctp_stream_clear(struct sctp_stream *stream)
185a8386317SXin Long {
186a8386317SXin Long 	int i;
187a8386317SXin Long 
188107e2425SXin Long 	for (i = 0; i < stream->outcnt; i++) {
18905364ca0SKonstantin Khorenko 		SCTP_SO(stream, i)->mid = 0;
19005364ca0SKonstantin Khorenko 		SCTP_SO(stream, i)->mid_uo = 0;
191107e2425SXin Long 	}
192a8386317SXin Long 
193a8386317SXin Long 	for (i = 0; i < stream->incnt; i++)
19405364ca0SKonstantin Khorenko 		SCTP_SI(stream, i)->mid = 0;
195a8386317SXin Long }
1967f9d68acSXin Long 
197cee360abSXin Long void sctp_stream_update(struct sctp_stream *stream, struct sctp_stream *new)
198cee360abSXin Long {
1995bbbbe32SMarcelo Ricardo Leitner 	struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream);
2005bbbbe32SMarcelo Ricardo Leitner 
2015bbbbe32SMarcelo Ricardo Leitner 	sched->unsched_all(stream);
2025bbbbe32SMarcelo Ricardo Leitner 	sctp_stream_outq_migrate(stream, new, new->outcnt);
203cee360abSXin Long 	sctp_stream_free(stream);
204cee360abSXin Long 
205cee360abSXin Long 	stream->out = new->out;
206cee360abSXin Long 	stream->in  = new->in;
207cee360abSXin Long 	stream->outcnt = new->outcnt;
208cee360abSXin Long 	stream->incnt  = new->incnt;
209cee360abSXin Long 
2105bbbbe32SMarcelo Ricardo Leitner 	sched->sched_all(stream);
2115bbbbe32SMarcelo Ricardo Leitner 
2122075e50cSKent Overstreet 	new->out.tree.root = NULL;
2132075e50cSKent Overstreet 	new->in.tree.root  = NULL;
2146a9a27d5SXin Long 	new->outcnt = 0;
2156a9a27d5SXin Long 	new->incnt  = 0;
216cee360abSXin Long }
217cee360abSXin Long 
2187f9d68acSXin Long static int sctp_send_reconf(struct sctp_association *asoc,
2197f9d68acSXin Long 			    struct sctp_chunk *chunk)
2207f9d68acSXin Long {
2217f9d68acSXin Long 	struct net *net = sock_net(asoc->base.sk);
2227f9d68acSXin Long 	int retval = 0;
2237f9d68acSXin Long 
2247f9d68acSXin Long 	retval = sctp_primitive_RECONF(net, asoc, chunk);
2257f9d68acSXin Long 	if (retval)
2267f9d68acSXin Long 		sctp_chunk_free(chunk);
2277f9d68acSXin Long 
2287f9d68acSXin Long 	return retval;
2297f9d68acSXin Long }
2307f9d68acSXin Long 
231d570a59cSXin Long static bool sctp_stream_outq_is_empty(struct sctp_stream *stream,
232d570a59cSXin Long 				      __u16 str_nums, __be16 *str_list)
233d570a59cSXin Long {
234d570a59cSXin Long 	struct sctp_association *asoc;
235d570a59cSXin Long 	__u16 i;
236d570a59cSXin Long 
237d570a59cSXin Long 	asoc = container_of(stream, struct sctp_association, stream);
238d570a59cSXin Long 	if (!asoc->outqueue.out_qlen)
239d570a59cSXin Long 		return true;
240d570a59cSXin Long 
241d570a59cSXin Long 	if (!str_nums)
242d570a59cSXin Long 		return false;
243d570a59cSXin Long 
244d570a59cSXin Long 	for (i = 0; i < str_nums; i++) {
245d570a59cSXin Long 		__u16 sid = ntohs(str_list[i]);
246d570a59cSXin Long 
24705364ca0SKonstantin Khorenko 		if (SCTP_SO(stream, sid)->ext &&
24805364ca0SKonstantin Khorenko 		    !list_empty(&SCTP_SO(stream, sid)->ext->outq))
249d570a59cSXin Long 			return false;
250d570a59cSXin Long 	}
251d570a59cSXin Long 
252d570a59cSXin Long 	return true;
253d570a59cSXin Long }
254d570a59cSXin Long 
2557f9d68acSXin Long int sctp_send_reset_streams(struct sctp_association *asoc,
2567f9d68acSXin Long 			    struct sctp_reset_streams *params)
2577f9d68acSXin Long {
258cee360abSXin Long 	struct sctp_stream *stream = &asoc->stream;
2597f9d68acSXin Long 	__u16 i, str_nums, *str_list;
2607f9d68acSXin Long 	struct sctp_chunk *chunk;
2617f9d68acSXin Long 	int retval = -EINVAL;
2621da4fc97SXin Long 	__be16 *nstr_list;
2637f9d68acSXin Long 	bool out, in;
2647f9d68acSXin Long 
2657f9d68acSXin Long 	if (!asoc->peer.reconf_capable ||
2667f9d68acSXin Long 	    !(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ)) {
2677f9d68acSXin Long 		retval = -ENOPROTOOPT;
2687f9d68acSXin Long 		goto out;
2697f9d68acSXin Long 	}
2707f9d68acSXin Long 
2717f9d68acSXin Long 	if (asoc->strreset_outstanding) {
2727f9d68acSXin Long 		retval = -EINPROGRESS;
2737f9d68acSXin Long 		goto out;
2747f9d68acSXin Long 	}
2757f9d68acSXin Long 
2767f9d68acSXin Long 	out = params->srs_flags & SCTP_STREAM_RESET_OUTGOING;
2777f9d68acSXin Long 	in  = params->srs_flags & SCTP_STREAM_RESET_INCOMING;
2787f9d68acSXin Long 	if (!out && !in)
2797f9d68acSXin Long 		goto out;
2807f9d68acSXin Long 
2817f9d68acSXin Long 	str_nums = params->srs_number_streams;
2827f9d68acSXin Long 	str_list = params->srs_stream_list;
283423852f8SXin Long 	if (str_nums) {
284423852f8SXin Long 		int param_len = 0;
285423852f8SXin Long 
286423852f8SXin Long 		if (out) {
2877f9d68acSXin Long 			for (i = 0; i < str_nums; i++)
2887f9d68acSXin Long 				if (str_list[i] >= stream->outcnt)
2897f9d68acSXin Long 					goto out;
2907f9d68acSXin Long 
291423852f8SXin Long 			param_len = str_nums * sizeof(__u16) +
292423852f8SXin Long 				    sizeof(struct sctp_strreset_outreq);
293423852f8SXin Long 		}
294423852f8SXin Long 
295423852f8SXin Long 		if (in) {
2967f9d68acSXin Long 			for (i = 0; i < str_nums; i++)
2977f9d68acSXin Long 				if (str_list[i] >= stream->incnt)
2987f9d68acSXin Long 					goto out;
2997f9d68acSXin Long 
300423852f8SXin Long 			param_len += str_nums * sizeof(__u16) +
301423852f8SXin Long 				     sizeof(struct sctp_strreset_inreq);
302423852f8SXin Long 		}
303423852f8SXin Long 
304423852f8SXin Long 		if (param_len > SCTP_MAX_CHUNK_LEN -
305423852f8SXin Long 				sizeof(struct sctp_reconf_chunk))
306423852f8SXin Long 			goto out;
307423852f8SXin Long 	}
308423852f8SXin Long 
3091da4fc97SXin Long 	nstr_list = kcalloc(str_nums, sizeof(__be16), GFP_KERNEL);
3101da4fc97SXin Long 	if (!nstr_list) {
3111da4fc97SXin Long 		retval = -ENOMEM;
3121da4fc97SXin Long 		goto out;
3131da4fc97SXin Long 	}
31416e1a919SXin Long 
31516e1a919SXin Long 	for (i = 0; i < str_nums; i++)
3161da4fc97SXin Long 		nstr_list[i] = htons(str_list[i]);
3171da4fc97SXin Long 
318d570a59cSXin Long 	if (out && !sctp_stream_outq_is_empty(stream, str_nums, nstr_list)) {
319d570a59cSXin Long 		retval = -EAGAIN;
320d570a59cSXin Long 		goto out;
321d570a59cSXin Long 	}
322d570a59cSXin Long 
3231da4fc97SXin Long 	chunk = sctp_make_strreset_req(asoc, str_nums, nstr_list, out, in);
3241da4fc97SXin Long 
3251da4fc97SXin Long 	kfree(nstr_list);
32616e1a919SXin Long 
327119aecbaSXin Long 	if (!chunk) {
328119aecbaSXin Long 		retval = -ENOMEM;
3297f9d68acSXin Long 		goto out;
330119aecbaSXin Long 	}
3317f9d68acSXin Long 
3327f9d68acSXin Long 	if (out) {
3337f9d68acSXin Long 		if (str_nums)
3347f9d68acSXin Long 			for (i = 0; i < str_nums; i++)
33505364ca0SKonstantin Khorenko 				SCTP_SO(stream, str_list[i])->state =
3367f9d68acSXin Long 						       SCTP_STREAM_CLOSED;
3377f9d68acSXin Long 		else
3387f9d68acSXin Long 			for (i = 0; i < stream->outcnt; i++)
33905364ca0SKonstantin Khorenko 				SCTP_SO(stream, i)->state = SCTP_STREAM_CLOSED;
3407f9d68acSXin Long 	}
3417f9d68acSXin Long 
3427f9d68acSXin Long 	asoc->strreset_chunk = chunk;
3437f9d68acSXin Long 	sctp_chunk_hold(asoc->strreset_chunk);
3447f9d68acSXin Long 
3457f9d68acSXin Long 	retval = sctp_send_reconf(asoc, chunk);
3467f9d68acSXin Long 	if (retval) {
3477f9d68acSXin Long 		sctp_chunk_put(asoc->strreset_chunk);
3487f9d68acSXin Long 		asoc->strreset_chunk = NULL;
349119aecbaSXin Long 		if (!out)
350119aecbaSXin Long 			goto out;
351119aecbaSXin Long 
352119aecbaSXin Long 		if (str_nums)
353119aecbaSXin Long 			for (i = 0; i < str_nums; i++)
35405364ca0SKonstantin Khorenko 				SCTP_SO(stream, str_list[i])->state =
355119aecbaSXin Long 						       SCTP_STREAM_OPEN;
356119aecbaSXin Long 		else
357119aecbaSXin Long 			for (i = 0; i < stream->outcnt; i++)
35805364ca0SKonstantin Khorenko 				SCTP_SO(stream, i)->state = SCTP_STREAM_OPEN;
359119aecbaSXin Long 
360119aecbaSXin Long 		goto out;
3617f9d68acSXin Long 	}
3627f9d68acSXin Long 
363119aecbaSXin Long 	asoc->strreset_outstanding = out + in;
364119aecbaSXin Long 
3657f9d68acSXin Long out:
3667f9d68acSXin Long 	return retval;
3677f9d68acSXin Long }
368a92ce1a4SXin Long 
369a92ce1a4SXin Long int sctp_send_reset_assoc(struct sctp_association *asoc)
370a92ce1a4SXin Long {
371cee360abSXin Long 	struct sctp_stream *stream = &asoc->stream;
372a92ce1a4SXin Long 	struct sctp_chunk *chunk = NULL;
373a92ce1a4SXin Long 	int retval;
374a92ce1a4SXin Long 	__u16 i;
375a92ce1a4SXin Long 
376a92ce1a4SXin Long 	if (!asoc->peer.reconf_capable ||
377a92ce1a4SXin Long 	    !(asoc->strreset_enable & SCTP_ENABLE_RESET_ASSOC_REQ))
378a92ce1a4SXin Long 		return -ENOPROTOOPT;
379a92ce1a4SXin Long 
380a92ce1a4SXin Long 	if (asoc->strreset_outstanding)
381a92ce1a4SXin Long 		return -EINPROGRESS;
382a92ce1a4SXin Long 
3835c6144a0SXin Long 	if (!sctp_outq_is_empty(&asoc->outqueue))
3845c6144a0SXin Long 		return -EAGAIN;
3855c6144a0SXin Long 
386a92ce1a4SXin Long 	chunk = sctp_make_strreset_tsnreq(asoc);
387a92ce1a4SXin Long 	if (!chunk)
388a92ce1a4SXin Long 		return -ENOMEM;
389a92ce1a4SXin Long 
390a92ce1a4SXin Long 	/* Block further xmit of data until this request is completed */
391cee360abSXin Long 	for (i = 0; i < stream->outcnt; i++)
39205364ca0SKonstantin Khorenko 		SCTP_SO(stream, i)->state = SCTP_STREAM_CLOSED;
393a92ce1a4SXin Long 
394a92ce1a4SXin Long 	asoc->strreset_chunk = chunk;
395a92ce1a4SXin Long 	sctp_chunk_hold(asoc->strreset_chunk);
396a92ce1a4SXin Long 
397a92ce1a4SXin Long 	retval = sctp_send_reconf(asoc, chunk);
398a92ce1a4SXin Long 	if (retval) {
399a92ce1a4SXin Long 		sctp_chunk_put(asoc->strreset_chunk);
400a92ce1a4SXin Long 		asoc->strreset_chunk = NULL;
401a92ce1a4SXin Long 
402cee360abSXin Long 		for (i = 0; i < stream->outcnt; i++)
40305364ca0SKonstantin Khorenko 			SCTP_SO(stream, i)->state = SCTP_STREAM_OPEN;
404a92ce1a4SXin Long 
405a92ce1a4SXin Long 		return retval;
406a92ce1a4SXin Long 	}
407a92ce1a4SXin Long 
408a92ce1a4SXin Long 	asoc->strreset_outstanding = 1;
409a92ce1a4SXin Long 
410a92ce1a4SXin Long 	return 0;
411a92ce1a4SXin Long }
412242bd2d5SXin Long 
413242bd2d5SXin Long int sctp_send_add_streams(struct sctp_association *asoc,
414242bd2d5SXin Long 			  struct sctp_add_streams *params)
415242bd2d5SXin Long {
416cee360abSXin Long 	struct sctp_stream *stream = &asoc->stream;
417242bd2d5SXin Long 	struct sctp_chunk *chunk = NULL;
418dc82673fSWei Yongjun 	int retval;
419242bd2d5SXin Long 	__u32 outcnt, incnt;
420242bd2d5SXin Long 	__u16 out, in;
421242bd2d5SXin Long 
422242bd2d5SXin Long 	if (!asoc->peer.reconf_capable ||
423242bd2d5SXin Long 	    !(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ)) {
424242bd2d5SXin Long 		retval = -ENOPROTOOPT;
425242bd2d5SXin Long 		goto out;
426242bd2d5SXin Long 	}
427242bd2d5SXin Long 
428242bd2d5SXin Long 	if (asoc->strreset_outstanding) {
429242bd2d5SXin Long 		retval = -EINPROGRESS;
430242bd2d5SXin Long 		goto out;
431242bd2d5SXin Long 	}
432242bd2d5SXin Long 
433242bd2d5SXin Long 	out = params->sas_outstrms;
434242bd2d5SXin Long 	in  = params->sas_instrms;
435242bd2d5SXin Long 	outcnt = stream->outcnt + out;
436242bd2d5SXin Long 	incnt = stream->incnt + in;
437242bd2d5SXin Long 	if (outcnt > SCTP_MAX_STREAM || incnt > SCTP_MAX_STREAM ||
438242bd2d5SXin Long 	    (!out && !in)) {
439242bd2d5SXin Long 		retval = -EINVAL;
440242bd2d5SXin Long 		goto out;
441242bd2d5SXin Long 	}
442242bd2d5SXin Long 
443242bd2d5SXin Long 	if (out) {
444e090abd0SMarcelo Ricardo Leitner 		retval = sctp_stream_alloc_out(stream, outcnt, GFP_KERNEL);
445e090abd0SMarcelo Ricardo Leitner 		if (retval)
446242bd2d5SXin Long 			goto out;
447242bd2d5SXin Long 	}
448242bd2d5SXin Long 
449242bd2d5SXin Long 	chunk = sctp_make_strreset_addstrm(asoc, out, in);
450dc82673fSWei Yongjun 	if (!chunk) {
451dc82673fSWei Yongjun 		retval = -ENOMEM;
452242bd2d5SXin Long 		goto out;
453dc82673fSWei Yongjun 	}
454242bd2d5SXin Long 
455242bd2d5SXin Long 	asoc->strreset_chunk = chunk;
456242bd2d5SXin Long 	sctp_chunk_hold(asoc->strreset_chunk);
457242bd2d5SXin Long 
458242bd2d5SXin Long 	retval = sctp_send_reconf(asoc, chunk);
459242bd2d5SXin Long 	if (retval) {
460242bd2d5SXin Long 		sctp_chunk_put(asoc->strreset_chunk);
461242bd2d5SXin Long 		asoc->strreset_chunk = NULL;
462242bd2d5SXin Long 		goto out;
463242bd2d5SXin Long 	}
464242bd2d5SXin Long 
465242bd2d5SXin Long 	asoc->strreset_outstanding = !!out + !!in;
466242bd2d5SXin Long 
467242bd2d5SXin Long out:
468242bd2d5SXin Long 	return retval;
469242bd2d5SXin Long }
47081054476SXin Long 
4713c918704SXin Long static struct sctp_paramhdr *sctp_chunk_lookup_strreset_param(
4721da4fc97SXin Long 			struct sctp_association *asoc, __be32 resp_seq,
47350a41591SXin Long 			__be16 type)
47481054476SXin Long {
47581054476SXin Long 	struct sctp_chunk *chunk = asoc->strreset_chunk;
47681054476SXin Long 	struct sctp_reconf_chunk *hdr;
47781054476SXin Long 	union sctp_params param;
47881054476SXin Long 
47950a41591SXin Long 	if (!chunk)
48081054476SXin Long 		return NULL;
48181054476SXin Long 
48281054476SXin Long 	hdr = (struct sctp_reconf_chunk *)chunk->chunk_hdr;
48381054476SXin Long 	sctp_walk_params(param, hdr, params) {
48481054476SXin Long 		/* sctp_strreset_tsnreq is actually the basic structure
48581054476SXin Long 		 * of all stream reconf params, so it's safe to use it
48681054476SXin Long 		 * to access request_seq.
48781054476SXin Long 		 */
48881054476SXin Long 		struct sctp_strreset_tsnreq *req = param.v;
48981054476SXin Long 
49050a41591SXin Long 		if ((!resp_seq || req->request_seq == resp_seq) &&
49150a41591SXin Long 		    (!type || type == req->param_hdr.type))
49281054476SXin Long 			return param.v;
49381054476SXin Long 	}
49481054476SXin Long 
49581054476SXin Long 	return NULL;
49681054476SXin Long }
49781054476SXin Long 
498e4dc99c7SXin Long static void sctp_update_strreset_result(struct sctp_association *asoc,
499e4dc99c7SXin Long 					__u32 result)
500e4dc99c7SXin Long {
501e4dc99c7SXin Long 	asoc->strreset_result[1] = asoc->strreset_result[0];
502e4dc99c7SXin Long 	asoc->strreset_result[0] = result;
503e4dc99c7SXin Long }
504e4dc99c7SXin Long 
50581054476SXin Long struct sctp_chunk *sctp_process_strreset_outreq(
50681054476SXin Long 				struct sctp_association *asoc,
50781054476SXin Long 				union sctp_params param,
50881054476SXin Long 				struct sctp_ulpevent **evp)
50981054476SXin Long {
51081054476SXin Long 	struct sctp_strreset_outreq *outreq = param.v;
511cee360abSXin Long 	struct sctp_stream *stream = &asoc->stream;
51281054476SXin Long 	__u32 result = SCTP_STRRESET_DENIED;
5131da4fc97SXin Long 	__be16 *str_p = NULL;
51481054476SXin Long 	__u32 request_seq;
5152e6dc4d9SXin Long 	__u16 i, nums;
51681054476SXin Long 
51781054476SXin Long 	request_seq = ntohl(outreq->request_seq);
51881054476SXin Long 
51981054476SXin Long 	if (ntohl(outreq->send_reset_at_tsn) >
52081054476SXin Long 	    sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map)) {
52181054476SXin Long 		result = SCTP_STRRESET_IN_PROGRESS;
522e4dc99c7SXin Long 		goto err;
52381054476SXin Long 	}
52481054476SXin Long 
525e4dc99c7SXin Long 	if (TSN_lt(asoc->strreset_inseq, request_seq) ||
526e4dc99c7SXin Long 	    TSN_lt(request_seq, asoc->strreset_inseq - 2)) {
52781054476SXin Long 		result = SCTP_STRRESET_ERR_BAD_SEQNO;
528e4dc99c7SXin Long 		goto err;
529e4dc99c7SXin Long 	} else if (TSN_lt(request_seq, asoc->strreset_inseq)) {
530e4dc99c7SXin Long 		i = asoc->strreset_inseq - request_seq - 1;
531e4dc99c7SXin Long 		result = asoc->strreset_result[i];
532e4dc99c7SXin Long 		goto err;
53381054476SXin Long 	}
534e4dc99c7SXin Long 	asoc->strreset_inseq++;
53581054476SXin Long 
53681054476SXin Long 	/* Check strreset_enable after inseq inc, as sender cannot tell
53781054476SXin Long 	 * the peer doesn't enable strreset after receiving response with
53881054476SXin Long 	 * result denied, as well as to keep consistent with bsd.
53981054476SXin Long 	 */
54081054476SXin Long 	if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ))
54181054476SXin Long 		goto out;
54281054476SXin Long 
5432e6dc4d9SXin Long 	nums = (ntohs(param.p->length) - sizeof(*outreq)) / sizeof(__u16);
5442e6dc4d9SXin Long 	str_p = outreq->list_of_streams;
5452e6dc4d9SXin Long 	for (i = 0; i < nums; i++) {
5462e6dc4d9SXin Long 		if (ntohs(str_p[i]) >= stream->incnt) {
5472e6dc4d9SXin Long 			result = SCTP_STRRESET_ERR_WRONG_SSN;
5482e6dc4d9SXin Long 			goto out;
5492e6dc4d9SXin Long 		}
5502e6dc4d9SXin Long 	}
5512e6dc4d9SXin Long 
55281054476SXin Long 	if (asoc->strreset_chunk) {
55350a41591SXin Long 		if (!sctp_chunk_lookup_strreset_param(
55450a41591SXin Long 				asoc, outreq->response_seq,
55550a41591SXin Long 				SCTP_PARAM_RESET_IN_REQUEST)) {
55681054476SXin Long 			/* same process with outstanding isn't 0 */
55781054476SXin Long 			result = SCTP_STRRESET_ERR_IN_PROGRESS;
55881054476SXin Long 			goto out;
55981054476SXin Long 		}
56081054476SXin Long 
56181054476SXin Long 		asoc->strreset_outstanding--;
56281054476SXin Long 		asoc->strreset_outseq++;
56381054476SXin Long 
56481054476SXin Long 		if (!asoc->strreset_outstanding) {
56550a41591SXin Long 			struct sctp_transport *t;
56650a41591SXin Long 
56781054476SXin Long 			t = asoc->strreset_chunk->transport;
56881054476SXin Long 			if (del_timer(&t->reconf_timer))
56981054476SXin Long 				sctp_transport_put(t);
57081054476SXin Long 
57181054476SXin Long 			sctp_chunk_put(asoc->strreset_chunk);
57281054476SXin Long 			asoc->strreset_chunk = NULL;
57381054476SXin Long 		}
57481054476SXin Long 	}
57581054476SXin Long 
5762e6dc4d9SXin Long 	if (nums)
57781054476SXin Long 		for (i = 0; i < nums; i++)
57805364ca0SKonstantin Khorenko 			SCTP_SI(stream, ntohs(str_p[i]))->mid = 0;
5792e6dc4d9SXin Long 	else
58081054476SXin Long 		for (i = 0; i < stream->incnt; i++)
58105364ca0SKonstantin Khorenko 			SCTP_SI(stream, i)->mid = 0;
58281054476SXin Long 
58381054476SXin Long 	result = SCTP_STRRESET_PERFORMED;
58481054476SXin Long 
58581054476SXin Long 	*evp = sctp_ulpevent_make_stream_reset_event(asoc,
5862e6dc4d9SXin Long 		SCTP_STREAM_RESET_INCOMING_SSN, nums, str_p, GFP_ATOMIC);
58781054476SXin Long 
58881054476SXin Long out:
589e4dc99c7SXin Long 	sctp_update_strreset_result(asoc, result);
590e4dc99c7SXin Long err:
59181054476SXin Long 	return sctp_make_strreset_resp(asoc, result, request_seq);
59281054476SXin Long }
59316e1a919SXin Long 
59416e1a919SXin Long struct sctp_chunk *sctp_process_strreset_inreq(
59516e1a919SXin Long 				struct sctp_association *asoc,
59616e1a919SXin Long 				union sctp_params param,
59716e1a919SXin Long 				struct sctp_ulpevent **evp)
59816e1a919SXin Long {
59916e1a919SXin Long 	struct sctp_strreset_inreq *inreq = param.v;
600cee360abSXin Long 	struct sctp_stream *stream = &asoc->stream;
60116e1a919SXin Long 	__u32 result = SCTP_STRRESET_DENIED;
60216e1a919SXin Long 	struct sctp_chunk *chunk = NULL;
60316e1a919SXin Long 	__u32 request_seq;
6041da4fc97SXin Long 	__u16 i, nums;
6051da4fc97SXin Long 	__be16 *str_p;
60616e1a919SXin Long 
60716e1a919SXin Long 	request_seq = ntohl(inreq->request_seq);
608d0f025e6SXin Long 	if (TSN_lt(asoc->strreset_inseq, request_seq) ||
609d0f025e6SXin Long 	    TSN_lt(request_seq, asoc->strreset_inseq - 2)) {
61016e1a919SXin Long 		result = SCTP_STRRESET_ERR_BAD_SEQNO;
611d0f025e6SXin Long 		goto err;
612d0f025e6SXin Long 	} else if (TSN_lt(request_seq, asoc->strreset_inseq)) {
613d0f025e6SXin Long 		i = asoc->strreset_inseq - request_seq - 1;
614d0f025e6SXin Long 		result = asoc->strreset_result[i];
615d0f025e6SXin Long 		if (result == SCTP_STRRESET_PERFORMED)
616d0f025e6SXin Long 			return NULL;
617d0f025e6SXin Long 		goto err;
61816e1a919SXin Long 	}
619d0f025e6SXin Long 	asoc->strreset_inseq++;
62016e1a919SXin Long 
62116e1a919SXin Long 	if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ))
62216e1a919SXin Long 		goto out;
62316e1a919SXin Long 
62416e1a919SXin Long 	if (asoc->strreset_outstanding) {
62516e1a919SXin Long 		result = SCTP_STRRESET_ERR_IN_PROGRESS;
62616e1a919SXin Long 		goto out;
62716e1a919SXin Long 	}
62816e1a919SXin Long 
6293aa623daSXin Long 	nums = (ntohs(param.p->length) - sizeof(*inreq)) / sizeof(__u16);
63016e1a919SXin Long 	str_p = inreq->list_of_streams;
63116e1a919SXin Long 	for (i = 0; i < nums; i++) {
63216e1a919SXin Long 		if (ntohs(str_p[i]) >= stream->outcnt) {
63316e1a919SXin Long 			result = SCTP_STRRESET_ERR_WRONG_SSN;
63416e1a919SXin Long 			goto out;
63516e1a919SXin Long 		}
63616e1a919SXin Long 	}
63716e1a919SXin Long 
638d570a59cSXin Long 	if (!sctp_stream_outq_is_empty(stream, nums, str_p)) {
639d570a59cSXin Long 		result = SCTP_STRRESET_IN_PROGRESS;
640d570a59cSXin Long 		asoc->strreset_inseq--;
641d570a59cSXin Long 		goto err;
642d570a59cSXin Long 	}
643d570a59cSXin Long 
64416e1a919SXin Long 	chunk = sctp_make_strreset_req(asoc, nums, str_p, 1, 0);
64516e1a919SXin Long 	if (!chunk)
64616e1a919SXin Long 		goto out;
64716e1a919SXin Long 
64816e1a919SXin Long 	if (nums)
64916e1a919SXin Long 		for (i = 0; i < nums; i++)
65005364ca0SKonstantin Khorenko 			SCTP_SO(stream, ntohs(str_p[i]))->state =
65116e1a919SXin Long 					       SCTP_STREAM_CLOSED;
65216e1a919SXin Long 	else
65316e1a919SXin Long 		for (i = 0; i < stream->outcnt; i++)
65405364ca0SKonstantin Khorenko 			SCTP_SO(stream, i)->state = SCTP_STREAM_CLOSED;
65516e1a919SXin Long 
65616e1a919SXin Long 	asoc->strreset_chunk = chunk;
65716e1a919SXin Long 	asoc->strreset_outstanding = 1;
65816e1a919SXin Long 	sctp_chunk_hold(asoc->strreset_chunk);
65916e1a919SXin Long 
660d0f025e6SXin Long 	result = SCTP_STRRESET_PERFORMED;
661d0f025e6SXin Long 
66216e1a919SXin Long out:
663d0f025e6SXin Long 	sctp_update_strreset_result(asoc, result);
664d0f025e6SXin Long err:
66516e1a919SXin Long 	if (!chunk)
66616e1a919SXin Long 		chunk =  sctp_make_strreset_resp(asoc, result, request_seq);
66716e1a919SXin Long 
66816e1a919SXin Long 	return chunk;
66916e1a919SXin Long }
670692787ceSXin Long 
671692787ceSXin Long struct sctp_chunk *sctp_process_strreset_tsnreq(
672692787ceSXin Long 				struct sctp_association *asoc,
673692787ceSXin Long 				union sctp_params param,
674692787ceSXin Long 				struct sctp_ulpevent **evp)
675692787ceSXin Long {
676692787ceSXin Long 	__u32 init_tsn = 0, next_tsn = 0, max_tsn_seen;
677692787ceSXin Long 	struct sctp_strreset_tsnreq *tsnreq = param.v;
678cee360abSXin Long 	struct sctp_stream *stream = &asoc->stream;
679692787ceSXin Long 	__u32 result = SCTP_STRRESET_DENIED;
680692787ceSXin Long 	__u32 request_seq;
681692787ceSXin Long 	__u16 i;
682692787ceSXin Long 
683692787ceSXin Long 	request_seq = ntohl(tsnreq->request_seq);
6846c801387SXin Long 	if (TSN_lt(asoc->strreset_inseq, request_seq) ||
6856c801387SXin Long 	    TSN_lt(request_seq, asoc->strreset_inseq - 2)) {
686692787ceSXin Long 		result = SCTP_STRRESET_ERR_BAD_SEQNO;
6876c801387SXin Long 		goto err;
6886c801387SXin Long 	} else if (TSN_lt(request_seq, asoc->strreset_inseq)) {
6896c801387SXin Long 		i = asoc->strreset_inseq - request_seq - 1;
6906c801387SXin Long 		result = asoc->strreset_result[i];
6916c801387SXin Long 		if (result == SCTP_STRRESET_PERFORMED) {
69252a39589SXin Long 			next_tsn = asoc->ctsn_ack_point + 1;
6936c801387SXin Long 			init_tsn =
6946c801387SXin Long 				sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map) + 1;
695692787ceSXin Long 		}
6966c801387SXin Long 		goto err;
6976c801387SXin Long 	}
6985c6144a0SXin Long 
6995c6144a0SXin Long 	if (!sctp_outq_is_empty(&asoc->outqueue)) {
7005c6144a0SXin Long 		result = SCTP_STRRESET_IN_PROGRESS;
7015c6144a0SXin Long 		goto err;
7025c6144a0SXin Long 	}
7035c6144a0SXin Long 
7046c801387SXin Long 	asoc->strreset_inseq++;
705692787ceSXin Long 
706692787ceSXin Long 	if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_ASSOC_REQ))
707692787ceSXin Long 		goto out;
708692787ceSXin Long 
709692787ceSXin Long 	if (asoc->strreset_outstanding) {
710692787ceSXin Long 		result = SCTP_STRRESET_ERR_IN_PROGRESS;
711692787ceSXin Long 		goto out;
712692787ceSXin Long 	}
713692787ceSXin Long 
714159f2a74SXin Long 	/* G4: The same processing as though a FWD-TSN chunk (as defined in
715159f2a74SXin Long 	 *     [RFC3758]) with all streams affected and a new cumulative TSN
716159f2a74SXin Long 	 *     ACK of the Receiver's Next TSN minus 1 were received MUST be
717159f2a74SXin Long 	 *     performed.
718692787ceSXin Long 	 */
719692787ceSXin Long 	max_tsn_seen = sctp_tsnmap_get_max_tsn_seen(&asoc->peer.tsn_map);
72047b20a88SXin Long 	asoc->stream.si->report_ftsn(&asoc->ulpq, max_tsn_seen);
721692787ceSXin Long 
722692787ceSXin Long 	/* G1: Compute an appropriate value for the Receiver's Next TSN -- the
723692787ceSXin Long 	 *     TSN that the peer should use to send the next DATA chunk.  The
724692787ceSXin Long 	 *     value SHOULD be the smallest TSN not acknowledged by the
725692787ceSXin Long 	 *     receiver of the request plus 2^31.
726692787ceSXin Long 	 */
727692787ceSXin Long 	init_tsn = sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map) + (1 << 31);
728692787ceSXin Long 	sctp_tsnmap_init(&asoc->peer.tsn_map, SCTP_TSN_MAP_INITIAL,
729692787ceSXin Long 			 init_tsn, GFP_ATOMIC);
730692787ceSXin Long 
731159f2a74SXin Long 	/* G3: The same processing as though a SACK chunk with no gap report
732159f2a74SXin Long 	 *     and a cumulative TSN ACK of the Sender's Next TSN minus 1 were
733159f2a74SXin Long 	 *     received MUST be performed.
734692787ceSXin Long 	 */
735692787ceSXin Long 	sctp_outq_free(&asoc->outqueue);
736692787ceSXin Long 
737692787ceSXin Long 	/* G2: Compute an appropriate value for the local endpoint's next TSN,
738692787ceSXin Long 	 *     i.e., the next TSN assigned by the receiver of the SSN/TSN reset
739692787ceSXin Long 	 *     chunk.  The value SHOULD be the highest TSN sent by the receiver
740692787ceSXin Long 	 *     of the request plus 1.
741692787ceSXin Long 	 */
742692787ceSXin Long 	next_tsn = asoc->next_tsn;
743692787ceSXin Long 	asoc->ctsn_ack_point = next_tsn - 1;
744692787ceSXin Long 	asoc->adv_peer_ack_point = asoc->ctsn_ack_point;
745692787ceSXin Long 
746692787ceSXin Long 	/* G5:  The next expected and outgoing SSNs MUST be reset to 0 for all
747692787ceSXin Long 	 *      incoming and outgoing streams.
748692787ceSXin Long 	 */
749107e2425SXin Long 	for (i = 0; i < stream->outcnt; i++) {
75005364ca0SKonstantin Khorenko 		SCTP_SO(stream, i)->mid = 0;
75105364ca0SKonstantin Khorenko 		SCTP_SO(stream, i)->mid_uo = 0;
752107e2425SXin Long 	}
753692787ceSXin Long 	for (i = 0; i < stream->incnt; i++)
75405364ca0SKonstantin Khorenko 		SCTP_SI(stream, i)->mid = 0;
755692787ceSXin Long 
756692787ceSXin Long 	result = SCTP_STRRESET_PERFORMED;
757692787ceSXin Long 
758692787ceSXin Long 	*evp = sctp_ulpevent_make_assoc_reset_event(asoc, 0, init_tsn,
759692787ceSXin Long 						    next_tsn, GFP_ATOMIC);
760692787ceSXin Long 
761692787ceSXin Long out:
7626c801387SXin Long 	sctp_update_strreset_result(asoc, result);
7636c801387SXin Long err:
764692787ceSXin Long 	return sctp_make_strreset_tsnresp(asoc, result, request_seq,
765692787ceSXin Long 					  next_tsn, init_tsn);
766692787ceSXin Long }
76750a41591SXin Long 
76850a41591SXin Long struct sctp_chunk *sctp_process_strreset_addstrm_out(
76950a41591SXin Long 				struct sctp_association *asoc,
77050a41591SXin Long 				union sctp_params param,
77150a41591SXin Long 				struct sctp_ulpevent **evp)
77250a41591SXin Long {
77350a41591SXin Long 	struct sctp_strreset_addstrm *addstrm = param.v;
774cee360abSXin Long 	struct sctp_stream *stream = &asoc->stream;
77550a41591SXin Long 	__u32 result = SCTP_STRRESET_DENIED;
77650a41591SXin Long 	__u32 request_seq, incnt;
777e4dc99c7SXin Long 	__u16 in, i;
77850a41591SXin Long 
77950a41591SXin Long 	request_seq = ntohl(addstrm->request_seq);
780e4dc99c7SXin Long 	if (TSN_lt(asoc->strreset_inseq, request_seq) ||
781e4dc99c7SXin Long 	    TSN_lt(request_seq, asoc->strreset_inseq - 2)) {
78250a41591SXin Long 		result = SCTP_STRRESET_ERR_BAD_SEQNO;
783e4dc99c7SXin Long 		goto err;
784e4dc99c7SXin Long 	} else if (TSN_lt(request_seq, asoc->strreset_inseq)) {
785e4dc99c7SXin Long 		i = asoc->strreset_inseq - request_seq - 1;
786e4dc99c7SXin Long 		result = asoc->strreset_result[i];
787e4dc99c7SXin Long 		goto err;
78850a41591SXin Long 	}
789e4dc99c7SXin Long 	asoc->strreset_inseq++;
79050a41591SXin Long 
79150a41591SXin Long 	if (!(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ))
79250a41591SXin Long 		goto out;
79350a41591SXin Long 
7948220c870SXin Long 	in = ntohs(addstrm->number_of_streams);
7958220c870SXin Long 	incnt = stream->incnt + in;
7968220c870SXin Long 	if (!in || incnt > SCTP_MAX_STREAM)
7978220c870SXin Long 		goto out;
7988220c870SXin Long 
7998220c870SXin Long 	if (sctp_stream_alloc_in(stream, incnt, GFP_ATOMIC))
8008220c870SXin Long 		goto out;
8018220c870SXin Long 
80250a41591SXin Long 	if (asoc->strreset_chunk) {
80350a41591SXin Long 		if (!sctp_chunk_lookup_strreset_param(
80450a41591SXin Long 			asoc, 0, SCTP_PARAM_RESET_ADD_IN_STREAMS)) {
80550a41591SXin Long 			/* same process with outstanding isn't 0 */
80650a41591SXin Long 			result = SCTP_STRRESET_ERR_IN_PROGRESS;
80750a41591SXin Long 			goto out;
80850a41591SXin Long 		}
80950a41591SXin Long 
81050a41591SXin Long 		asoc->strreset_outstanding--;
81150a41591SXin Long 		asoc->strreset_outseq++;
81250a41591SXin Long 
81350a41591SXin Long 		if (!asoc->strreset_outstanding) {
81450a41591SXin Long 			struct sctp_transport *t;
81550a41591SXin Long 
81650a41591SXin Long 			t = asoc->strreset_chunk->transport;
81750a41591SXin Long 			if (del_timer(&t->reconf_timer))
81850a41591SXin Long 				sctp_transport_put(t);
81950a41591SXin Long 
82050a41591SXin Long 			sctp_chunk_put(asoc->strreset_chunk);
82150a41591SXin Long 			asoc->strreset_chunk = NULL;
82250a41591SXin Long 		}
82350a41591SXin Long 	}
82450a41591SXin Long 
82550a41591SXin Long 	stream->incnt = incnt;
82650a41591SXin Long 
82750a41591SXin Long 	result = SCTP_STRRESET_PERFORMED;
82850a41591SXin Long 
82950a41591SXin Long 	*evp = sctp_ulpevent_make_stream_change_event(asoc,
83050a41591SXin Long 		0, ntohs(addstrm->number_of_streams), 0, GFP_ATOMIC);
83150a41591SXin Long 
83250a41591SXin Long out:
833e4dc99c7SXin Long 	sctp_update_strreset_result(asoc, result);
834e4dc99c7SXin Long err:
83550a41591SXin Long 	return sctp_make_strreset_resp(asoc, result, request_seq);
83650a41591SXin Long }
837c5c4ebb3SXin Long 
838c5c4ebb3SXin Long struct sctp_chunk *sctp_process_strreset_addstrm_in(
839c5c4ebb3SXin Long 				struct sctp_association *asoc,
840c5c4ebb3SXin Long 				union sctp_params param,
841c5c4ebb3SXin Long 				struct sctp_ulpevent **evp)
842c5c4ebb3SXin Long {
843c5c4ebb3SXin Long 	struct sctp_strreset_addstrm *addstrm = param.v;
844cee360abSXin Long 	struct sctp_stream *stream = &asoc->stream;
845c5c4ebb3SXin Long 	__u32 result = SCTP_STRRESET_DENIED;
846c5c4ebb3SXin Long 	struct sctp_chunk *chunk = NULL;
847c5c4ebb3SXin Long 	__u32 request_seq, outcnt;
848d0f025e6SXin Long 	__u16 out, i;
849e090abd0SMarcelo Ricardo Leitner 	int ret;
850c5c4ebb3SXin Long 
851c5c4ebb3SXin Long 	request_seq = ntohl(addstrm->request_seq);
852d0f025e6SXin Long 	if (TSN_lt(asoc->strreset_inseq, request_seq) ||
853d0f025e6SXin Long 	    TSN_lt(request_seq, asoc->strreset_inseq - 2)) {
854c5c4ebb3SXin Long 		result = SCTP_STRRESET_ERR_BAD_SEQNO;
855d0f025e6SXin Long 		goto err;
856d0f025e6SXin Long 	} else if (TSN_lt(request_seq, asoc->strreset_inseq)) {
857d0f025e6SXin Long 		i = asoc->strreset_inseq - request_seq - 1;
858d0f025e6SXin Long 		result = asoc->strreset_result[i];
859d0f025e6SXin Long 		if (result == SCTP_STRRESET_PERFORMED)
860d0f025e6SXin Long 			return NULL;
861d0f025e6SXin Long 		goto err;
862c5c4ebb3SXin Long 	}
863d0f025e6SXin Long 	asoc->strreset_inseq++;
864c5c4ebb3SXin Long 
865c5c4ebb3SXin Long 	if (!(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ))
866c5c4ebb3SXin Long 		goto out;
867c5c4ebb3SXin Long 
868c5c4ebb3SXin Long 	if (asoc->strreset_outstanding) {
869c5c4ebb3SXin Long 		result = SCTP_STRRESET_ERR_IN_PROGRESS;
870c5c4ebb3SXin Long 		goto out;
871c5c4ebb3SXin Long 	}
872c5c4ebb3SXin Long 
873c5c4ebb3SXin Long 	out = ntohs(addstrm->number_of_streams);
874c5c4ebb3SXin Long 	outcnt = stream->outcnt + out;
875c5c4ebb3SXin Long 	if (!out || outcnt > SCTP_MAX_STREAM)
876c5c4ebb3SXin Long 		goto out;
877c5c4ebb3SXin Long 
878e090abd0SMarcelo Ricardo Leitner 	ret = sctp_stream_alloc_out(stream, outcnt, GFP_ATOMIC);
879e090abd0SMarcelo Ricardo Leitner 	if (ret)
880c5c4ebb3SXin Long 		goto out;
881c5c4ebb3SXin Long 
882c5c4ebb3SXin Long 	chunk = sctp_make_strreset_addstrm(asoc, out, 0);
883c5c4ebb3SXin Long 	if (!chunk)
884c5c4ebb3SXin Long 		goto out;
885c5c4ebb3SXin Long 
886c5c4ebb3SXin Long 	asoc->strreset_chunk = chunk;
887c5c4ebb3SXin Long 	asoc->strreset_outstanding = 1;
888c5c4ebb3SXin Long 	sctp_chunk_hold(asoc->strreset_chunk);
889c5c4ebb3SXin Long 
890c5c4ebb3SXin Long 	stream->outcnt = outcnt;
891c5c4ebb3SXin Long 
892d0f025e6SXin Long 	result = SCTP_STRRESET_PERFORMED;
893d0f025e6SXin Long 
894c5c4ebb3SXin Long out:
895d0f025e6SXin Long 	sctp_update_strreset_result(asoc, result);
896d0f025e6SXin Long err:
897c5c4ebb3SXin Long 	if (!chunk)
898c5c4ebb3SXin Long 		chunk = sctp_make_strreset_resp(asoc, result, request_seq);
899c5c4ebb3SXin Long 
900c5c4ebb3SXin Long 	return chunk;
901c5c4ebb3SXin Long }
90211ae76e6SXin Long 
90311ae76e6SXin Long struct sctp_chunk *sctp_process_strreset_resp(
90411ae76e6SXin Long 				struct sctp_association *asoc,
90511ae76e6SXin Long 				union sctp_params param,
90611ae76e6SXin Long 				struct sctp_ulpevent **evp)
90711ae76e6SXin Long {
908cee360abSXin Long 	struct sctp_stream *stream = &asoc->stream;
90911ae76e6SXin Long 	struct sctp_strreset_resp *resp = param.v;
91011ae76e6SXin Long 	struct sctp_transport *t;
91111ae76e6SXin Long 	__u16 i, nums, flags = 0;
9123c918704SXin Long 	struct sctp_paramhdr *req;
91311ae76e6SXin Long 	__u32 result;
91411ae76e6SXin Long 
91511ae76e6SXin Long 	req = sctp_chunk_lookup_strreset_param(asoc, resp->response_seq, 0);
91611ae76e6SXin Long 	if (!req)
91711ae76e6SXin Long 		return NULL;
91811ae76e6SXin Long 
91911ae76e6SXin Long 	result = ntohl(resp->result);
92011ae76e6SXin Long 	if (result != SCTP_STRRESET_PERFORMED) {
92111ae76e6SXin Long 		/* if in progress, do nothing but retransmit */
92211ae76e6SXin Long 		if (result == SCTP_STRRESET_IN_PROGRESS)
92311ae76e6SXin Long 			return NULL;
92411ae76e6SXin Long 		else if (result == SCTP_STRRESET_DENIED)
92511ae76e6SXin Long 			flags = SCTP_STREAM_RESET_DENIED;
92611ae76e6SXin Long 		else
92711ae76e6SXin Long 			flags = SCTP_STREAM_RESET_FAILED;
92811ae76e6SXin Long 	}
92911ae76e6SXin Long 
93011ae76e6SXin Long 	if (req->type == SCTP_PARAM_RESET_OUT_REQUEST) {
93111ae76e6SXin Long 		struct sctp_strreset_outreq *outreq;
9321da4fc97SXin Long 		__be16 *str_p;
93311ae76e6SXin Long 
93411ae76e6SXin Long 		outreq = (struct sctp_strreset_outreq *)req;
935edb12f2dSXin Long 		str_p = outreq->list_of_streams;
9363aa623daSXin Long 		nums = (ntohs(outreq->param_hdr.length) - sizeof(*outreq)) /
9373aa623daSXin Long 		       sizeof(__u16);
93811ae76e6SXin Long 
93911ae76e6SXin Long 		if (result == SCTP_STRRESET_PERFORMED) {
94005364ca0SKonstantin Khorenko 			struct sctp_stream_out *sout;
94111ae76e6SXin Long 			if (nums) {
942107e2425SXin Long 				for (i = 0; i < nums; i++) {
94305364ca0SKonstantin Khorenko 					sout = SCTP_SO(stream, ntohs(str_p[i]));
94405364ca0SKonstantin Khorenko 					sout->mid = 0;
94505364ca0SKonstantin Khorenko 					sout->mid_uo = 0;
946107e2425SXin Long 				}
94711ae76e6SXin Long 			} else {
948107e2425SXin Long 				for (i = 0; i < stream->outcnt; i++) {
94905364ca0SKonstantin Khorenko 					sout = SCTP_SO(stream, i);
95005364ca0SKonstantin Khorenko 					sout->mid = 0;
95105364ca0SKonstantin Khorenko 					sout->mid_uo = 0;
952107e2425SXin Long 				}
95311ae76e6SXin Long 			}
95411ae76e6SXin Long 		}
95511ae76e6SXin Long 
9562e6dc4d9SXin Long 		flags |= SCTP_STREAM_RESET_OUTGOING_SSN;
9572e6dc4d9SXin Long 
95811ae76e6SXin Long 		for (i = 0; i < stream->outcnt; i++)
95905364ca0SKonstantin Khorenko 			SCTP_SO(stream, i)->state = SCTP_STREAM_OPEN;
96011ae76e6SXin Long 
96111ae76e6SXin Long 		*evp = sctp_ulpevent_make_stream_reset_event(asoc, flags,
96211ae76e6SXin Long 			nums, str_p, GFP_ATOMIC);
96311ae76e6SXin Long 	} else if (req->type == SCTP_PARAM_RESET_IN_REQUEST) {
96411ae76e6SXin Long 		struct sctp_strreset_inreq *inreq;
9651da4fc97SXin Long 		__be16 *str_p;
96611ae76e6SXin Long 
96711ae76e6SXin Long 		/* if the result is performed, it's impossible for inreq */
96811ae76e6SXin Long 		if (result == SCTP_STRRESET_PERFORMED)
96911ae76e6SXin Long 			return NULL;
97011ae76e6SXin Long 
97111ae76e6SXin Long 		inreq = (struct sctp_strreset_inreq *)req;
972edb12f2dSXin Long 		str_p = inreq->list_of_streams;
9733aa623daSXin Long 		nums = (ntohs(inreq->param_hdr.length) - sizeof(*inreq)) /
9743aa623daSXin Long 		       sizeof(__u16);
97511ae76e6SXin Long 
9762e6dc4d9SXin Long 		flags |= SCTP_STREAM_RESET_INCOMING_SSN;
9772e6dc4d9SXin Long 
97811ae76e6SXin Long 		*evp = sctp_ulpevent_make_stream_reset_event(asoc, flags,
97911ae76e6SXin Long 			nums, str_p, GFP_ATOMIC);
98011ae76e6SXin Long 	} else if (req->type == SCTP_PARAM_RESET_TSN_REQUEST) {
98111ae76e6SXin Long 		struct sctp_strreset_resptsn *resptsn;
98211ae76e6SXin Long 		__u32 stsn, rtsn;
98311ae76e6SXin Long 
98411ae76e6SXin Long 		/* check for resptsn, as sctp_verify_reconf didn't do it*/
98511ae76e6SXin Long 		if (ntohs(param.p->length) != sizeof(*resptsn))
98611ae76e6SXin Long 			return NULL;
98711ae76e6SXin Long 
98811ae76e6SXin Long 		resptsn = (struct sctp_strreset_resptsn *)resp;
98911ae76e6SXin Long 		stsn = ntohl(resptsn->senders_next_tsn);
99011ae76e6SXin Long 		rtsn = ntohl(resptsn->receivers_next_tsn);
99111ae76e6SXin Long 
99211ae76e6SXin Long 		if (result == SCTP_STRRESET_PERFORMED) {
99311ae76e6SXin Long 			__u32 mtsn = sctp_tsnmap_get_max_tsn_seen(
99411ae76e6SXin Long 						&asoc->peer.tsn_map);
995159f2a74SXin Long 			LIST_HEAD(temp);
99611ae76e6SXin Long 
99747b20a88SXin Long 			asoc->stream.si->report_ftsn(&asoc->ulpq, mtsn);
99811ae76e6SXin Long 
99911ae76e6SXin Long 			sctp_tsnmap_init(&asoc->peer.tsn_map,
100011ae76e6SXin Long 					 SCTP_TSN_MAP_INITIAL,
100111ae76e6SXin Long 					 stsn, GFP_ATOMIC);
100211ae76e6SXin Long 
1003159f2a74SXin Long 			/* Clean up sacked and abandoned queues only. As the
1004159f2a74SXin Long 			 * out_chunk_list may not be empty, splice it to temp,
1005159f2a74SXin Long 			 * then get it back after sctp_outq_free is done.
1006159f2a74SXin Long 			 */
1007159f2a74SXin Long 			list_splice_init(&asoc->outqueue.out_chunk_list, &temp);
100811ae76e6SXin Long 			sctp_outq_free(&asoc->outqueue);
1009159f2a74SXin Long 			list_splice_init(&temp, &asoc->outqueue.out_chunk_list);
101011ae76e6SXin Long 
101111ae76e6SXin Long 			asoc->next_tsn = rtsn;
101211ae76e6SXin Long 			asoc->ctsn_ack_point = asoc->next_tsn - 1;
101311ae76e6SXin Long 			asoc->adv_peer_ack_point = asoc->ctsn_ack_point;
101411ae76e6SXin Long 
1015107e2425SXin Long 			for (i = 0; i < stream->outcnt; i++) {
101605364ca0SKonstantin Khorenko 				SCTP_SO(stream, i)->mid = 0;
101705364ca0SKonstantin Khorenko 				SCTP_SO(stream, i)->mid_uo = 0;
1018107e2425SXin Long 			}
101911ae76e6SXin Long 			for (i = 0; i < stream->incnt; i++)
102005364ca0SKonstantin Khorenko 				SCTP_SI(stream, i)->mid = 0;
102111ae76e6SXin Long 		}
102211ae76e6SXin Long 
102311ae76e6SXin Long 		for (i = 0; i < stream->outcnt; i++)
102405364ca0SKonstantin Khorenko 			SCTP_SO(stream, i)->state = SCTP_STREAM_OPEN;
102511ae76e6SXin Long 
102611ae76e6SXin Long 		*evp = sctp_ulpevent_make_assoc_reset_event(asoc, flags,
102711ae76e6SXin Long 			stsn, rtsn, GFP_ATOMIC);
102811ae76e6SXin Long 	} else if (req->type == SCTP_PARAM_RESET_ADD_OUT_STREAMS) {
102911ae76e6SXin Long 		struct sctp_strreset_addstrm *addstrm;
103011ae76e6SXin Long 		__u16 number;
103111ae76e6SXin Long 
103211ae76e6SXin Long 		addstrm = (struct sctp_strreset_addstrm *)req;
103311ae76e6SXin Long 		nums = ntohs(addstrm->number_of_streams);
103411ae76e6SXin Long 		number = stream->outcnt - nums;
103511ae76e6SXin Long 
103611ae76e6SXin Long 		if (result == SCTP_STRRESET_PERFORMED)
103711ae76e6SXin Long 			for (i = number; i < stream->outcnt; i++)
103805364ca0SKonstantin Khorenko 				SCTP_SO(stream, i)->state = SCTP_STREAM_OPEN;
103911ae76e6SXin Long 		else
104011ae76e6SXin Long 			stream->outcnt = number;
104111ae76e6SXin Long 
104211ae76e6SXin Long 		*evp = sctp_ulpevent_make_stream_change_event(asoc, flags,
104311ae76e6SXin Long 			0, nums, GFP_ATOMIC);
104411ae76e6SXin Long 	} else if (req->type == SCTP_PARAM_RESET_ADD_IN_STREAMS) {
104511ae76e6SXin Long 		struct sctp_strreset_addstrm *addstrm;
104611ae76e6SXin Long 
104711ae76e6SXin Long 		/* if the result is performed, it's impossible for addstrm in
104811ae76e6SXin Long 		 * request.
104911ae76e6SXin Long 		 */
105011ae76e6SXin Long 		if (result == SCTP_STRRESET_PERFORMED)
105111ae76e6SXin Long 			return NULL;
105211ae76e6SXin Long 
105311ae76e6SXin Long 		addstrm = (struct sctp_strreset_addstrm *)req;
105411ae76e6SXin Long 		nums = ntohs(addstrm->number_of_streams);
105511ae76e6SXin Long 
105611ae76e6SXin Long 		*evp = sctp_ulpevent_make_stream_change_event(asoc, flags,
105711ae76e6SXin Long 			nums, 0, GFP_ATOMIC);
105811ae76e6SXin Long 	}
105911ae76e6SXin Long 
106011ae76e6SXin Long 	asoc->strreset_outstanding--;
106111ae76e6SXin Long 	asoc->strreset_outseq++;
106211ae76e6SXin Long 
106311ae76e6SXin Long 	/* remove everything for this reconf request */
106411ae76e6SXin Long 	if (!asoc->strreset_outstanding) {
106511ae76e6SXin Long 		t = asoc->strreset_chunk->transport;
106611ae76e6SXin Long 		if (del_timer(&t->reconf_timer))
106711ae76e6SXin Long 			sctp_transport_put(t);
106811ae76e6SXin Long 
106911ae76e6SXin Long 		sctp_chunk_put(asoc->strreset_chunk);
107011ae76e6SXin Long 		asoc->strreset_chunk = NULL;
107111ae76e6SXin Long 	}
107211ae76e6SXin Long 
107311ae76e6SXin Long 	return NULL;
107411ae76e6SXin Long }
1075