18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/* SCTP kernel implementation
38c2ecf20Sopenharmony_ci * (C) Copyright IBM Corp. 2001, 2004
48c2ecf20Sopenharmony_ci * Copyright (c) 1999-2000 Cisco, Inc.
58c2ecf20Sopenharmony_ci * Copyright (c) 1999-2001 Motorola, Inc.
68c2ecf20Sopenharmony_ci * Copyright (c) 2001 Intel Corp.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * This file is part of the SCTP kernel implementation
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * This file contains sctp stream maniuplation primitives and helpers.
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * Please send any bug reports or fixes you make to the
138c2ecf20Sopenharmony_ci * email address(es):
148c2ecf20Sopenharmony_ci *    lksctp developers <linux-sctp@vger.kernel.org>
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci * Written or modified by:
178c2ecf20Sopenharmony_ci *    Xin Long <lucien.xin@gmail.com>
188c2ecf20Sopenharmony_ci */
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include <linux/list.h>
218c2ecf20Sopenharmony_ci#include <net/sctp/sctp.h>
228c2ecf20Sopenharmony_ci#include <net/sctp/sm.h>
238c2ecf20Sopenharmony_ci#include <net/sctp/stream_sched.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic void sctp_stream_shrink_out(struct sctp_stream *stream, __u16 outcnt)
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
288c2ecf20Sopenharmony_ci	struct sctp_chunk *ch, *temp;
298c2ecf20Sopenharmony_ci	struct sctp_outq *outq;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	asoc = container_of(stream, struct sctp_association, stream);
328c2ecf20Sopenharmony_ci	outq = &asoc->outqueue;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	list_for_each_entry_safe(ch, temp, &outq->out_chunk_list, list) {
358c2ecf20Sopenharmony_ci		__u16 sid = sctp_chunk_stream_no(ch);
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci		if (sid < outcnt)
388c2ecf20Sopenharmony_ci			continue;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci		sctp_sched_dequeue_common(outq, ch);
418c2ecf20Sopenharmony_ci		/* No need to call dequeue_done here because
428c2ecf20Sopenharmony_ci		 * the chunks are not scheduled by now.
438c2ecf20Sopenharmony_ci		 */
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci		/* Mark as failed send. */
468c2ecf20Sopenharmony_ci		sctp_chunk_fail(ch, (__force __u32)SCTP_ERROR_INV_STRM);
478c2ecf20Sopenharmony_ci		if (asoc->peer.prsctp_capable &&
488c2ecf20Sopenharmony_ci		    SCTP_PR_PRIO_ENABLED(ch->sinfo.sinfo_flags))
498c2ecf20Sopenharmony_ci			asoc->sent_cnt_removable--;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci		sctp_chunk_free(ch);
528c2ecf20Sopenharmony_ci	}
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic void sctp_stream_free_ext(struct sctp_stream *stream, __u16 sid)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	struct sctp_sched_ops *sched;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	if (!SCTP_SO(stream, sid)->ext)
608c2ecf20Sopenharmony_ci		return;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	sched = sctp_sched_ops_from_stream(stream);
638c2ecf20Sopenharmony_ci	sched->free_sid(stream, sid);
648c2ecf20Sopenharmony_ci	kfree(SCTP_SO(stream, sid)->ext);
658c2ecf20Sopenharmony_ci	SCTP_SO(stream, sid)->ext = NULL;
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci/* Migrates chunks from stream queues to new stream queues if needed,
698c2ecf20Sopenharmony_ci * but not across associations. Also, removes those chunks to streams
708c2ecf20Sopenharmony_ci * higher than the new max.
718c2ecf20Sopenharmony_ci */
728c2ecf20Sopenharmony_cistatic void sctp_stream_outq_migrate(struct sctp_stream *stream,
738c2ecf20Sopenharmony_ci				     struct sctp_stream *new, __u16 outcnt)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	int i;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	if (stream->outcnt > outcnt)
788c2ecf20Sopenharmony_ci		sctp_stream_shrink_out(stream, outcnt);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	if (new) {
818c2ecf20Sopenharmony_ci		/* Here we actually move the old ext stuff into the new
828c2ecf20Sopenharmony_ci		 * buffer, because we want to keep it. Then
838c2ecf20Sopenharmony_ci		 * sctp_stream_update will swap ->out pointers.
848c2ecf20Sopenharmony_ci		 */
858c2ecf20Sopenharmony_ci		for (i = 0; i < outcnt; i++) {
868c2ecf20Sopenharmony_ci			sctp_stream_free_ext(new, i);
878c2ecf20Sopenharmony_ci			SCTP_SO(new, i)->ext = SCTP_SO(stream, i)->ext;
888c2ecf20Sopenharmony_ci			SCTP_SO(stream, i)->ext = NULL;
898c2ecf20Sopenharmony_ci		}
908c2ecf20Sopenharmony_ci	}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	for (i = outcnt; i < stream->outcnt; i++)
938c2ecf20Sopenharmony_ci		sctp_stream_free_ext(stream, i);
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic int sctp_stream_alloc_out(struct sctp_stream *stream, __u16 outcnt,
978c2ecf20Sopenharmony_ci				 gfp_t gfp)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	int ret;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	if (outcnt <= stream->outcnt)
1028c2ecf20Sopenharmony_ci		goto out;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	ret = genradix_prealloc(&stream->out, outcnt, gfp);
1058c2ecf20Sopenharmony_ci	if (ret)
1068c2ecf20Sopenharmony_ci		return ret;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ciout:
1098c2ecf20Sopenharmony_ci	stream->outcnt = outcnt;
1108c2ecf20Sopenharmony_ci	return 0;
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic int sctp_stream_alloc_in(struct sctp_stream *stream, __u16 incnt,
1148c2ecf20Sopenharmony_ci				gfp_t gfp)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	int ret;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	if (incnt <= stream->incnt)
1198c2ecf20Sopenharmony_ci		goto out;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	ret = genradix_prealloc(&stream->in, incnt, gfp);
1228c2ecf20Sopenharmony_ci	if (ret)
1238c2ecf20Sopenharmony_ci		return ret;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ciout:
1268c2ecf20Sopenharmony_ci	stream->incnt = incnt;
1278c2ecf20Sopenharmony_ci	return 0;
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ciint sctp_stream_init(struct sctp_stream *stream, __u16 outcnt, __u16 incnt,
1318c2ecf20Sopenharmony_ci		     gfp_t gfp)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream);
1348c2ecf20Sopenharmony_ci	int i, ret = 0;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	gfp |= __GFP_NOWARN;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	/* Initial stream->out size may be very big, so free it and alloc
1398c2ecf20Sopenharmony_ci	 * a new one with new outcnt to save memory if needed.
1408c2ecf20Sopenharmony_ci	 */
1418c2ecf20Sopenharmony_ci	if (outcnt == stream->outcnt)
1428c2ecf20Sopenharmony_ci		goto handle_in;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	/* Filter out chunks queued on streams that won't exist anymore */
1458c2ecf20Sopenharmony_ci	sched->unsched_all(stream);
1468c2ecf20Sopenharmony_ci	sctp_stream_outq_migrate(stream, NULL, outcnt);
1478c2ecf20Sopenharmony_ci	sched->sched_all(stream);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	ret = sctp_stream_alloc_out(stream, outcnt, gfp);
1508c2ecf20Sopenharmony_ci	if (ret)
1518c2ecf20Sopenharmony_ci		return ret;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	for (i = 0; i < stream->outcnt; i++)
1548c2ecf20Sopenharmony_ci		SCTP_SO(stream, i)->state = SCTP_STREAM_OPEN;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_cihandle_in:
1578c2ecf20Sopenharmony_ci	sctp_stream_interleave_init(stream);
1588c2ecf20Sopenharmony_ci	if (!incnt)
1598c2ecf20Sopenharmony_ci		return 0;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	return sctp_stream_alloc_in(stream, incnt, gfp);
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ciint sctp_stream_init_ext(struct sctp_stream *stream, __u16 sid)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	struct sctp_stream_out_ext *soute;
1678c2ecf20Sopenharmony_ci	int ret;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	soute = kzalloc(sizeof(*soute), GFP_KERNEL);
1708c2ecf20Sopenharmony_ci	if (!soute)
1718c2ecf20Sopenharmony_ci		return -ENOMEM;
1728c2ecf20Sopenharmony_ci	SCTP_SO(stream, sid)->ext = soute;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	ret = sctp_sched_init_sid(stream, sid, GFP_KERNEL);
1758c2ecf20Sopenharmony_ci	if (ret) {
1768c2ecf20Sopenharmony_ci		kfree(SCTP_SO(stream, sid)->ext);
1778c2ecf20Sopenharmony_ci		SCTP_SO(stream, sid)->ext = NULL;
1788c2ecf20Sopenharmony_ci	}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	return ret;
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_civoid sctp_stream_free(struct sctp_stream *stream)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream);
1868c2ecf20Sopenharmony_ci	int i;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	sched->unsched_all(stream);
1898c2ecf20Sopenharmony_ci	for (i = 0; i < stream->outcnt; i++)
1908c2ecf20Sopenharmony_ci		sctp_stream_free_ext(stream, i);
1918c2ecf20Sopenharmony_ci	genradix_free(&stream->out);
1928c2ecf20Sopenharmony_ci	genradix_free(&stream->in);
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_civoid sctp_stream_clear(struct sctp_stream *stream)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	int i;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	for (i = 0; i < stream->outcnt; i++) {
2008c2ecf20Sopenharmony_ci		SCTP_SO(stream, i)->mid = 0;
2018c2ecf20Sopenharmony_ci		SCTP_SO(stream, i)->mid_uo = 0;
2028c2ecf20Sopenharmony_ci	}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	for (i = 0; i < stream->incnt; i++)
2058c2ecf20Sopenharmony_ci		SCTP_SI(stream, i)->mid = 0;
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_civoid sctp_stream_update(struct sctp_stream *stream, struct sctp_stream *new)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	sched->unsched_all(stream);
2138c2ecf20Sopenharmony_ci	sctp_stream_outq_migrate(stream, new, new->outcnt);
2148c2ecf20Sopenharmony_ci	sctp_stream_free(stream);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	stream->out = new->out;
2178c2ecf20Sopenharmony_ci	stream->in  = new->in;
2188c2ecf20Sopenharmony_ci	stream->outcnt = new->outcnt;
2198c2ecf20Sopenharmony_ci	stream->incnt  = new->incnt;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	sched->sched_all(stream);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	new->out.tree.root = NULL;
2248c2ecf20Sopenharmony_ci	new->in.tree.root  = NULL;
2258c2ecf20Sopenharmony_ci	new->outcnt = 0;
2268c2ecf20Sopenharmony_ci	new->incnt  = 0;
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic int sctp_send_reconf(struct sctp_association *asoc,
2308c2ecf20Sopenharmony_ci			    struct sctp_chunk *chunk)
2318c2ecf20Sopenharmony_ci{
2328c2ecf20Sopenharmony_ci	int retval = 0;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	retval = sctp_primitive_RECONF(asoc->base.net, asoc, chunk);
2358c2ecf20Sopenharmony_ci	if (retval)
2368c2ecf20Sopenharmony_ci		sctp_chunk_free(chunk);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	return retval;
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cistatic bool sctp_stream_outq_is_empty(struct sctp_stream *stream,
2428c2ecf20Sopenharmony_ci				      __u16 str_nums, __be16 *str_list)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
2458c2ecf20Sopenharmony_ci	__u16 i;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	asoc = container_of(stream, struct sctp_association, stream);
2488c2ecf20Sopenharmony_ci	if (!asoc->outqueue.out_qlen)
2498c2ecf20Sopenharmony_ci		return true;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	if (!str_nums)
2528c2ecf20Sopenharmony_ci		return false;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	for (i = 0; i < str_nums; i++) {
2558c2ecf20Sopenharmony_ci		__u16 sid = ntohs(str_list[i]);
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci		if (SCTP_SO(stream, sid)->ext &&
2588c2ecf20Sopenharmony_ci		    !list_empty(&SCTP_SO(stream, sid)->ext->outq))
2598c2ecf20Sopenharmony_ci			return false;
2608c2ecf20Sopenharmony_ci	}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	return true;
2638c2ecf20Sopenharmony_ci}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ciint sctp_send_reset_streams(struct sctp_association *asoc,
2668c2ecf20Sopenharmony_ci			    struct sctp_reset_streams *params)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	struct sctp_stream *stream = &asoc->stream;
2698c2ecf20Sopenharmony_ci	__u16 i, str_nums, *str_list;
2708c2ecf20Sopenharmony_ci	struct sctp_chunk *chunk;
2718c2ecf20Sopenharmony_ci	int retval = -EINVAL;
2728c2ecf20Sopenharmony_ci	__be16 *nstr_list;
2738c2ecf20Sopenharmony_ci	bool out, in;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	if (!asoc->peer.reconf_capable ||
2768c2ecf20Sopenharmony_ci	    !(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ)) {
2778c2ecf20Sopenharmony_ci		retval = -ENOPROTOOPT;
2788c2ecf20Sopenharmony_ci		goto out;
2798c2ecf20Sopenharmony_ci	}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	if (asoc->strreset_outstanding) {
2828c2ecf20Sopenharmony_ci		retval = -EINPROGRESS;
2838c2ecf20Sopenharmony_ci		goto out;
2848c2ecf20Sopenharmony_ci	}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	out = params->srs_flags & SCTP_STREAM_RESET_OUTGOING;
2878c2ecf20Sopenharmony_ci	in  = params->srs_flags & SCTP_STREAM_RESET_INCOMING;
2888c2ecf20Sopenharmony_ci	if (!out && !in)
2898c2ecf20Sopenharmony_ci		goto out;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	str_nums = params->srs_number_streams;
2928c2ecf20Sopenharmony_ci	str_list = params->srs_stream_list;
2938c2ecf20Sopenharmony_ci	if (str_nums) {
2948c2ecf20Sopenharmony_ci		int param_len = 0;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci		if (out) {
2978c2ecf20Sopenharmony_ci			for (i = 0; i < str_nums; i++)
2988c2ecf20Sopenharmony_ci				if (str_list[i] >= stream->outcnt)
2998c2ecf20Sopenharmony_ci					goto out;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci			param_len = str_nums * sizeof(__u16) +
3028c2ecf20Sopenharmony_ci				    sizeof(struct sctp_strreset_outreq);
3038c2ecf20Sopenharmony_ci		}
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci		if (in) {
3068c2ecf20Sopenharmony_ci			for (i = 0; i < str_nums; i++)
3078c2ecf20Sopenharmony_ci				if (str_list[i] >= stream->incnt)
3088c2ecf20Sopenharmony_ci					goto out;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci			param_len += str_nums * sizeof(__u16) +
3118c2ecf20Sopenharmony_ci				     sizeof(struct sctp_strreset_inreq);
3128c2ecf20Sopenharmony_ci		}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci		if (param_len > SCTP_MAX_CHUNK_LEN -
3158c2ecf20Sopenharmony_ci				sizeof(struct sctp_reconf_chunk))
3168c2ecf20Sopenharmony_ci			goto out;
3178c2ecf20Sopenharmony_ci	}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	nstr_list = kcalloc(str_nums, sizeof(__be16), GFP_KERNEL);
3208c2ecf20Sopenharmony_ci	if (!nstr_list) {
3218c2ecf20Sopenharmony_ci		retval = -ENOMEM;
3228c2ecf20Sopenharmony_ci		goto out;
3238c2ecf20Sopenharmony_ci	}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	for (i = 0; i < str_nums; i++)
3268c2ecf20Sopenharmony_ci		nstr_list[i] = htons(str_list[i]);
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	if (out && !sctp_stream_outq_is_empty(stream, str_nums, nstr_list)) {
3298c2ecf20Sopenharmony_ci		kfree(nstr_list);
3308c2ecf20Sopenharmony_ci		retval = -EAGAIN;
3318c2ecf20Sopenharmony_ci		goto out;
3328c2ecf20Sopenharmony_ci	}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	chunk = sctp_make_strreset_req(asoc, str_nums, nstr_list, out, in);
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	kfree(nstr_list);
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	if (!chunk) {
3398c2ecf20Sopenharmony_ci		retval = -ENOMEM;
3408c2ecf20Sopenharmony_ci		goto out;
3418c2ecf20Sopenharmony_ci	}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	if (out) {
3448c2ecf20Sopenharmony_ci		if (str_nums)
3458c2ecf20Sopenharmony_ci			for (i = 0; i < str_nums; i++)
3468c2ecf20Sopenharmony_ci				SCTP_SO(stream, str_list[i])->state =
3478c2ecf20Sopenharmony_ci						       SCTP_STREAM_CLOSED;
3488c2ecf20Sopenharmony_ci		else
3498c2ecf20Sopenharmony_ci			for (i = 0; i < stream->outcnt; i++)
3508c2ecf20Sopenharmony_ci				SCTP_SO(stream, i)->state = SCTP_STREAM_CLOSED;
3518c2ecf20Sopenharmony_ci	}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	asoc->strreset_chunk = chunk;
3548c2ecf20Sopenharmony_ci	sctp_chunk_hold(asoc->strreset_chunk);
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	retval = sctp_send_reconf(asoc, chunk);
3578c2ecf20Sopenharmony_ci	if (retval) {
3588c2ecf20Sopenharmony_ci		sctp_chunk_put(asoc->strreset_chunk);
3598c2ecf20Sopenharmony_ci		asoc->strreset_chunk = NULL;
3608c2ecf20Sopenharmony_ci		if (!out)
3618c2ecf20Sopenharmony_ci			goto out;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci		if (str_nums)
3648c2ecf20Sopenharmony_ci			for (i = 0; i < str_nums; i++)
3658c2ecf20Sopenharmony_ci				SCTP_SO(stream, str_list[i])->state =
3668c2ecf20Sopenharmony_ci						       SCTP_STREAM_OPEN;
3678c2ecf20Sopenharmony_ci		else
3688c2ecf20Sopenharmony_ci			for (i = 0; i < stream->outcnt; i++)
3698c2ecf20Sopenharmony_ci				SCTP_SO(stream, i)->state = SCTP_STREAM_OPEN;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci		goto out;
3728c2ecf20Sopenharmony_ci	}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	asoc->strreset_outstanding = out + in;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ciout:
3778c2ecf20Sopenharmony_ci	return retval;
3788c2ecf20Sopenharmony_ci}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ciint sctp_send_reset_assoc(struct sctp_association *asoc)
3818c2ecf20Sopenharmony_ci{
3828c2ecf20Sopenharmony_ci	struct sctp_stream *stream = &asoc->stream;
3838c2ecf20Sopenharmony_ci	struct sctp_chunk *chunk = NULL;
3848c2ecf20Sopenharmony_ci	int retval;
3858c2ecf20Sopenharmony_ci	__u16 i;
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	if (!asoc->peer.reconf_capable ||
3888c2ecf20Sopenharmony_ci	    !(asoc->strreset_enable & SCTP_ENABLE_RESET_ASSOC_REQ))
3898c2ecf20Sopenharmony_ci		return -ENOPROTOOPT;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	if (asoc->strreset_outstanding)
3928c2ecf20Sopenharmony_ci		return -EINPROGRESS;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	if (!sctp_outq_is_empty(&asoc->outqueue))
3958c2ecf20Sopenharmony_ci		return -EAGAIN;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	chunk = sctp_make_strreset_tsnreq(asoc);
3988c2ecf20Sopenharmony_ci	if (!chunk)
3998c2ecf20Sopenharmony_ci		return -ENOMEM;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	/* Block further xmit of data until this request is completed */
4028c2ecf20Sopenharmony_ci	for (i = 0; i < stream->outcnt; i++)
4038c2ecf20Sopenharmony_ci		SCTP_SO(stream, i)->state = SCTP_STREAM_CLOSED;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	asoc->strreset_chunk = chunk;
4068c2ecf20Sopenharmony_ci	sctp_chunk_hold(asoc->strreset_chunk);
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	retval = sctp_send_reconf(asoc, chunk);
4098c2ecf20Sopenharmony_ci	if (retval) {
4108c2ecf20Sopenharmony_ci		sctp_chunk_put(asoc->strreset_chunk);
4118c2ecf20Sopenharmony_ci		asoc->strreset_chunk = NULL;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci		for (i = 0; i < stream->outcnt; i++)
4148c2ecf20Sopenharmony_ci			SCTP_SO(stream, i)->state = SCTP_STREAM_OPEN;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci		return retval;
4178c2ecf20Sopenharmony_ci	}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	asoc->strreset_outstanding = 1;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	return 0;
4228c2ecf20Sopenharmony_ci}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ciint sctp_send_add_streams(struct sctp_association *asoc,
4258c2ecf20Sopenharmony_ci			  struct sctp_add_streams *params)
4268c2ecf20Sopenharmony_ci{
4278c2ecf20Sopenharmony_ci	struct sctp_stream *stream = &asoc->stream;
4288c2ecf20Sopenharmony_ci	struct sctp_chunk *chunk = NULL;
4298c2ecf20Sopenharmony_ci	int retval;
4308c2ecf20Sopenharmony_ci	__u32 outcnt, incnt;
4318c2ecf20Sopenharmony_ci	__u16 out, in;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	if (!asoc->peer.reconf_capable ||
4348c2ecf20Sopenharmony_ci	    !(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ)) {
4358c2ecf20Sopenharmony_ci		retval = -ENOPROTOOPT;
4368c2ecf20Sopenharmony_ci		goto out;
4378c2ecf20Sopenharmony_ci	}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	if (asoc->strreset_outstanding) {
4408c2ecf20Sopenharmony_ci		retval = -EINPROGRESS;
4418c2ecf20Sopenharmony_ci		goto out;
4428c2ecf20Sopenharmony_ci	}
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	out = params->sas_outstrms;
4458c2ecf20Sopenharmony_ci	in  = params->sas_instrms;
4468c2ecf20Sopenharmony_ci	outcnt = stream->outcnt + out;
4478c2ecf20Sopenharmony_ci	incnt = stream->incnt + in;
4488c2ecf20Sopenharmony_ci	if (outcnt > SCTP_MAX_STREAM || incnt > SCTP_MAX_STREAM ||
4498c2ecf20Sopenharmony_ci	    (!out && !in)) {
4508c2ecf20Sopenharmony_ci		retval = -EINVAL;
4518c2ecf20Sopenharmony_ci		goto out;
4528c2ecf20Sopenharmony_ci	}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	if (out) {
4558c2ecf20Sopenharmony_ci		retval = sctp_stream_alloc_out(stream, outcnt, GFP_KERNEL);
4568c2ecf20Sopenharmony_ci		if (retval)
4578c2ecf20Sopenharmony_ci			goto out;
4588c2ecf20Sopenharmony_ci	}
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	chunk = sctp_make_strreset_addstrm(asoc, out, in);
4618c2ecf20Sopenharmony_ci	if (!chunk) {
4628c2ecf20Sopenharmony_ci		retval = -ENOMEM;
4638c2ecf20Sopenharmony_ci		goto out;
4648c2ecf20Sopenharmony_ci	}
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	asoc->strreset_chunk = chunk;
4678c2ecf20Sopenharmony_ci	sctp_chunk_hold(asoc->strreset_chunk);
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	retval = sctp_send_reconf(asoc, chunk);
4708c2ecf20Sopenharmony_ci	if (retval) {
4718c2ecf20Sopenharmony_ci		sctp_chunk_put(asoc->strreset_chunk);
4728c2ecf20Sopenharmony_ci		asoc->strreset_chunk = NULL;
4738c2ecf20Sopenharmony_ci		goto out;
4748c2ecf20Sopenharmony_ci	}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	asoc->strreset_outstanding = !!out + !!in;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ciout:
4798c2ecf20Sopenharmony_ci	return retval;
4808c2ecf20Sopenharmony_ci}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_cistatic struct sctp_paramhdr *sctp_chunk_lookup_strreset_param(
4838c2ecf20Sopenharmony_ci			struct sctp_association *asoc, __be32 resp_seq,
4848c2ecf20Sopenharmony_ci			__be16 type)
4858c2ecf20Sopenharmony_ci{
4868c2ecf20Sopenharmony_ci	struct sctp_chunk *chunk = asoc->strreset_chunk;
4878c2ecf20Sopenharmony_ci	struct sctp_reconf_chunk *hdr;
4888c2ecf20Sopenharmony_ci	union sctp_params param;
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	if (!chunk)
4918c2ecf20Sopenharmony_ci		return NULL;
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	hdr = (struct sctp_reconf_chunk *)chunk->chunk_hdr;
4948c2ecf20Sopenharmony_ci	sctp_walk_params(param, hdr, params) {
4958c2ecf20Sopenharmony_ci		/* sctp_strreset_tsnreq is actually the basic structure
4968c2ecf20Sopenharmony_ci		 * of all stream reconf params, so it's safe to use it
4978c2ecf20Sopenharmony_ci		 * to access request_seq.
4988c2ecf20Sopenharmony_ci		 */
4998c2ecf20Sopenharmony_ci		struct sctp_strreset_tsnreq *req = param.v;
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci		if ((!resp_seq || req->request_seq == resp_seq) &&
5028c2ecf20Sopenharmony_ci		    (!type || type == req->param_hdr.type))
5038c2ecf20Sopenharmony_ci			return param.v;
5048c2ecf20Sopenharmony_ci	}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	return NULL;
5078c2ecf20Sopenharmony_ci}
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_cistatic void sctp_update_strreset_result(struct sctp_association *asoc,
5108c2ecf20Sopenharmony_ci					__u32 result)
5118c2ecf20Sopenharmony_ci{
5128c2ecf20Sopenharmony_ci	asoc->strreset_result[1] = asoc->strreset_result[0];
5138c2ecf20Sopenharmony_ci	asoc->strreset_result[0] = result;
5148c2ecf20Sopenharmony_ci}
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_cistruct sctp_chunk *sctp_process_strreset_outreq(
5178c2ecf20Sopenharmony_ci				struct sctp_association *asoc,
5188c2ecf20Sopenharmony_ci				union sctp_params param,
5198c2ecf20Sopenharmony_ci				struct sctp_ulpevent **evp)
5208c2ecf20Sopenharmony_ci{
5218c2ecf20Sopenharmony_ci	struct sctp_strreset_outreq *outreq = param.v;
5228c2ecf20Sopenharmony_ci	struct sctp_stream *stream = &asoc->stream;
5238c2ecf20Sopenharmony_ci	__u32 result = SCTP_STRRESET_DENIED;
5248c2ecf20Sopenharmony_ci	__be16 *str_p = NULL;
5258c2ecf20Sopenharmony_ci	__u32 request_seq;
5268c2ecf20Sopenharmony_ci	__u16 i, nums;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	request_seq = ntohl(outreq->request_seq);
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	if (ntohl(outreq->send_reset_at_tsn) >
5318c2ecf20Sopenharmony_ci	    sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map)) {
5328c2ecf20Sopenharmony_ci		result = SCTP_STRRESET_IN_PROGRESS;
5338c2ecf20Sopenharmony_ci		goto err;
5348c2ecf20Sopenharmony_ci	}
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	if (TSN_lt(asoc->strreset_inseq, request_seq) ||
5378c2ecf20Sopenharmony_ci	    TSN_lt(request_seq, asoc->strreset_inseq - 2)) {
5388c2ecf20Sopenharmony_ci		result = SCTP_STRRESET_ERR_BAD_SEQNO;
5398c2ecf20Sopenharmony_ci		goto err;
5408c2ecf20Sopenharmony_ci	} else if (TSN_lt(request_seq, asoc->strreset_inseq)) {
5418c2ecf20Sopenharmony_ci		i = asoc->strreset_inseq - request_seq - 1;
5428c2ecf20Sopenharmony_ci		result = asoc->strreset_result[i];
5438c2ecf20Sopenharmony_ci		goto err;
5448c2ecf20Sopenharmony_ci	}
5458c2ecf20Sopenharmony_ci	asoc->strreset_inseq++;
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	/* Check strreset_enable after inseq inc, as sender cannot tell
5488c2ecf20Sopenharmony_ci	 * the peer doesn't enable strreset after receiving response with
5498c2ecf20Sopenharmony_ci	 * result denied, as well as to keep consistent with bsd.
5508c2ecf20Sopenharmony_ci	 */
5518c2ecf20Sopenharmony_ci	if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ))
5528c2ecf20Sopenharmony_ci		goto out;
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	nums = (ntohs(param.p->length) - sizeof(*outreq)) / sizeof(__u16);
5558c2ecf20Sopenharmony_ci	str_p = outreq->list_of_streams;
5568c2ecf20Sopenharmony_ci	for (i = 0; i < nums; i++) {
5578c2ecf20Sopenharmony_ci		if (ntohs(str_p[i]) >= stream->incnt) {
5588c2ecf20Sopenharmony_ci			result = SCTP_STRRESET_ERR_WRONG_SSN;
5598c2ecf20Sopenharmony_ci			goto out;
5608c2ecf20Sopenharmony_ci		}
5618c2ecf20Sopenharmony_ci	}
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	if (asoc->strreset_chunk) {
5648c2ecf20Sopenharmony_ci		if (!sctp_chunk_lookup_strreset_param(
5658c2ecf20Sopenharmony_ci				asoc, outreq->response_seq,
5668c2ecf20Sopenharmony_ci				SCTP_PARAM_RESET_IN_REQUEST)) {
5678c2ecf20Sopenharmony_ci			/* same process with outstanding isn't 0 */
5688c2ecf20Sopenharmony_ci			result = SCTP_STRRESET_ERR_IN_PROGRESS;
5698c2ecf20Sopenharmony_ci			goto out;
5708c2ecf20Sopenharmony_ci		}
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci		asoc->strreset_outstanding--;
5738c2ecf20Sopenharmony_ci		asoc->strreset_outseq++;
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci		if (!asoc->strreset_outstanding) {
5768c2ecf20Sopenharmony_ci			struct sctp_transport *t;
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci			t = asoc->strreset_chunk->transport;
5798c2ecf20Sopenharmony_ci			if (del_timer(&t->reconf_timer))
5808c2ecf20Sopenharmony_ci				sctp_transport_put(t);
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci			sctp_chunk_put(asoc->strreset_chunk);
5838c2ecf20Sopenharmony_ci			asoc->strreset_chunk = NULL;
5848c2ecf20Sopenharmony_ci		}
5858c2ecf20Sopenharmony_ci	}
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	if (nums)
5888c2ecf20Sopenharmony_ci		for (i = 0; i < nums; i++)
5898c2ecf20Sopenharmony_ci			SCTP_SI(stream, ntohs(str_p[i]))->mid = 0;
5908c2ecf20Sopenharmony_ci	else
5918c2ecf20Sopenharmony_ci		for (i = 0; i < stream->incnt; i++)
5928c2ecf20Sopenharmony_ci			SCTP_SI(stream, i)->mid = 0;
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	result = SCTP_STRRESET_PERFORMED;
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	*evp = sctp_ulpevent_make_stream_reset_event(asoc,
5978c2ecf20Sopenharmony_ci		SCTP_STREAM_RESET_INCOMING_SSN, nums, str_p, GFP_ATOMIC);
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ciout:
6008c2ecf20Sopenharmony_ci	sctp_update_strreset_result(asoc, result);
6018c2ecf20Sopenharmony_cierr:
6028c2ecf20Sopenharmony_ci	return sctp_make_strreset_resp(asoc, result, request_seq);
6038c2ecf20Sopenharmony_ci}
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_cistruct sctp_chunk *sctp_process_strreset_inreq(
6068c2ecf20Sopenharmony_ci				struct sctp_association *asoc,
6078c2ecf20Sopenharmony_ci				union sctp_params param,
6088c2ecf20Sopenharmony_ci				struct sctp_ulpevent **evp)
6098c2ecf20Sopenharmony_ci{
6108c2ecf20Sopenharmony_ci	struct sctp_strreset_inreq *inreq = param.v;
6118c2ecf20Sopenharmony_ci	struct sctp_stream *stream = &asoc->stream;
6128c2ecf20Sopenharmony_ci	__u32 result = SCTP_STRRESET_DENIED;
6138c2ecf20Sopenharmony_ci	struct sctp_chunk *chunk = NULL;
6148c2ecf20Sopenharmony_ci	__u32 request_seq;
6158c2ecf20Sopenharmony_ci	__u16 i, nums;
6168c2ecf20Sopenharmony_ci	__be16 *str_p;
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	request_seq = ntohl(inreq->request_seq);
6198c2ecf20Sopenharmony_ci	if (TSN_lt(asoc->strreset_inseq, request_seq) ||
6208c2ecf20Sopenharmony_ci	    TSN_lt(request_seq, asoc->strreset_inseq - 2)) {
6218c2ecf20Sopenharmony_ci		result = SCTP_STRRESET_ERR_BAD_SEQNO;
6228c2ecf20Sopenharmony_ci		goto err;
6238c2ecf20Sopenharmony_ci	} else if (TSN_lt(request_seq, asoc->strreset_inseq)) {
6248c2ecf20Sopenharmony_ci		i = asoc->strreset_inseq - request_seq - 1;
6258c2ecf20Sopenharmony_ci		result = asoc->strreset_result[i];
6268c2ecf20Sopenharmony_ci		if (result == SCTP_STRRESET_PERFORMED)
6278c2ecf20Sopenharmony_ci			return NULL;
6288c2ecf20Sopenharmony_ci		goto err;
6298c2ecf20Sopenharmony_ci	}
6308c2ecf20Sopenharmony_ci	asoc->strreset_inseq++;
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ))
6338c2ecf20Sopenharmony_ci		goto out;
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	if (asoc->strreset_outstanding) {
6368c2ecf20Sopenharmony_ci		result = SCTP_STRRESET_ERR_IN_PROGRESS;
6378c2ecf20Sopenharmony_ci		goto out;
6388c2ecf20Sopenharmony_ci	}
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	nums = (ntohs(param.p->length) - sizeof(*inreq)) / sizeof(__u16);
6418c2ecf20Sopenharmony_ci	str_p = inreq->list_of_streams;
6428c2ecf20Sopenharmony_ci	for (i = 0; i < nums; i++) {
6438c2ecf20Sopenharmony_ci		if (ntohs(str_p[i]) >= stream->outcnt) {
6448c2ecf20Sopenharmony_ci			result = SCTP_STRRESET_ERR_WRONG_SSN;
6458c2ecf20Sopenharmony_ci			goto out;
6468c2ecf20Sopenharmony_ci		}
6478c2ecf20Sopenharmony_ci	}
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	if (!sctp_stream_outq_is_empty(stream, nums, str_p)) {
6508c2ecf20Sopenharmony_ci		result = SCTP_STRRESET_IN_PROGRESS;
6518c2ecf20Sopenharmony_ci		asoc->strreset_inseq--;
6528c2ecf20Sopenharmony_ci		goto err;
6538c2ecf20Sopenharmony_ci	}
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	chunk = sctp_make_strreset_req(asoc, nums, str_p, 1, 0);
6568c2ecf20Sopenharmony_ci	if (!chunk)
6578c2ecf20Sopenharmony_ci		goto out;
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	if (nums)
6608c2ecf20Sopenharmony_ci		for (i = 0; i < nums; i++)
6618c2ecf20Sopenharmony_ci			SCTP_SO(stream, ntohs(str_p[i]))->state =
6628c2ecf20Sopenharmony_ci					       SCTP_STREAM_CLOSED;
6638c2ecf20Sopenharmony_ci	else
6648c2ecf20Sopenharmony_ci		for (i = 0; i < stream->outcnt; i++)
6658c2ecf20Sopenharmony_ci			SCTP_SO(stream, i)->state = SCTP_STREAM_CLOSED;
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	asoc->strreset_chunk = chunk;
6688c2ecf20Sopenharmony_ci	asoc->strreset_outstanding = 1;
6698c2ecf20Sopenharmony_ci	sctp_chunk_hold(asoc->strreset_chunk);
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	result = SCTP_STRRESET_PERFORMED;
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ciout:
6748c2ecf20Sopenharmony_ci	sctp_update_strreset_result(asoc, result);
6758c2ecf20Sopenharmony_cierr:
6768c2ecf20Sopenharmony_ci	if (!chunk)
6778c2ecf20Sopenharmony_ci		chunk =  sctp_make_strreset_resp(asoc, result, request_seq);
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	return chunk;
6808c2ecf20Sopenharmony_ci}
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_cistruct sctp_chunk *sctp_process_strreset_tsnreq(
6838c2ecf20Sopenharmony_ci				struct sctp_association *asoc,
6848c2ecf20Sopenharmony_ci				union sctp_params param,
6858c2ecf20Sopenharmony_ci				struct sctp_ulpevent **evp)
6868c2ecf20Sopenharmony_ci{
6878c2ecf20Sopenharmony_ci	__u32 init_tsn = 0, next_tsn = 0, max_tsn_seen;
6888c2ecf20Sopenharmony_ci	struct sctp_strreset_tsnreq *tsnreq = param.v;
6898c2ecf20Sopenharmony_ci	struct sctp_stream *stream = &asoc->stream;
6908c2ecf20Sopenharmony_ci	__u32 result = SCTP_STRRESET_DENIED;
6918c2ecf20Sopenharmony_ci	__u32 request_seq;
6928c2ecf20Sopenharmony_ci	__u16 i;
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci	request_seq = ntohl(tsnreq->request_seq);
6958c2ecf20Sopenharmony_ci	if (TSN_lt(asoc->strreset_inseq, request_seq) ||
6968c2ecf20Sopenharmony_ci	    TSN_lt(request_seq, asoc->strreset_inseq - 2)) {
6978c2ecf20Sopenharmony_ci		result = SCTP_STRRESET_ERR_BAD_SEQNO;
6988c2ecf20Sopenharmony_ci		goto err;
6998c2ecf20Sopenharmony_ci	} else if (TSN_lt(request_seq, asoc->strreset_inseq)) {
7008c2ecf20Sopenharmony_ci		i = asoc->strreset_inseq - request_seq - 1;
7018c2ecf20Sopenharmony_ci		result = asoc->strreset_result[i];
7028c2ecf20Sopenharmony_ci		if (result == SCTP_STRRESET_PERFORMED) {
7038c2ecf20Sopenharmony_ci			next_tsn = asoc->ctsn_ack_point + 1;
7048c2ecf20Sopenharmony_ci			init_tsn =
7058c2ecf20Sopenharmony_ci				sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map) + 1;
7068c2ecf20Sopenharmony_ci		}
7078c2ecf20Sopenharmony_ci		goto err;
7088c2ecf20Sopenharmony_ci	}
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	if (!sctp_outq_is_empty(&asoc->outqueue)) {
7118c2ecf20Sopenharmony_ci		result = SCTP_STRRESET_IN_PROGRESS;
7128c2ecf20Sopenharmony_ci		goto err;
7138c2ecf20Sopenharmony_ci	}
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	asoc->strreset_inseq++;
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_ASSOC_REQ))
7188c2ecf20Sopenharmony_ci		goto out;
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	if (asoc->strreset_outstanding) {
7218c2ecf20Sopenharmony_ci		result = SCTP_STRRESET_ERR_IN_PROGRESS;
7228c2ecf20Sopenharmony_ci		goto out;
7238c2ecf20Sopenharmony_ci	}
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	/* G4: The same processing as though a FWD-TSN chunk (as defined in
7268c2ecf20Sopenharmony_ci	 *     [RFC3758]) with all streams affected and a new cumulative TSN
7278c2ecf20Sopenharmony_ci	 *     ACK of the Receiver's Next TSN minus 1 were received MUST be
7288c2ecf20Sopenharmony_ci	 *     performed.
7298c2ecf20Sopenharmony_ci	 */
7308c2ecf20Sopenharmony_ci	max_tsn_seen = sctp_tsnmap_get_max_tsn_seen(&asoc->peer.tsn_map);
7318c2ecf20Sopenharmony_ci	asoc->stream.si->report_ftsn(&asoc->ulpq, max_tsn_seen);
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	/* G1: Compute an appropriate value for the Receiver's Next TSN -- the
7348c2ecf20Sopenharmony_ci	 *     TSN that the peer should use to send the next DATA chunk.  The
7358c2ecf20Sopenharmony_ci	 *     value SHOULD be the smallest TSN not acknowledged by the
7368c2ecf20Sopenharmony_ci	 *     receiver of the request plus 2^31.
7378c2ecf20Sopenharmony_ci	 */
7388c2ecf20Sopenharmony_ci	init_tsn = sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map) + (1 << 31);
7398c2ecf20Sopenharmony_ci	sctp_tsnmap_init(&asoc->peer.tsn_map, SCTP_TSN_MAP_INITIAL,
7408c2ecf20Sopenharmony_ci			 init_tsn, GFP_ATOMIC);
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	/* G3: The same processing as though a SACK chunk with no gap report
7438c2ecf20Sopenharmony_ci	 *     and a cumulative TSN ACK of the Sender's Next TSN minus 1 were
7448c2ecf20Sopenharmony_ci	 *     received MUST be performed.
7458c2ecf20Sopenharmony_ci	 */
7468c2ecf20Sopenharmony_ci	sctp_outq_free(&asoc->outqueue);
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci	/* G2: Compute an appropriate value for the local endpoint's next TSN,
7498c2ecf20Sopenharmony_ci	 *     i.e., the next TSN assigned by the receiver of the SSN/TSN reset
7508c2ecf20Sopenharmony_ci	 *     chunk.  The value SHOULD be the highest TSN sent by the receiver
7518c2ecf20Sopenharmony_ci	 *     of the request plus 1.
7528c2ecf20Sopenharmony_ci	 */
7538c2ecf20Sopenharmony_ci	next_tsn = asoc->next_tsn;
7548c2ecf20Sopenharmony_ci	asoc->ctsn_ack_point = next_tsn - 1;
7558c2ecf20Sopenharmony_ci	asoc->adv_peer_ack_point = asoc->ctsn_ack_point;
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	/* G5:  The next expected and outgoing SSNs MUST be reset to 0 for all
7588c2ecf20Sopenharmony_ci	 *      incoming and outgoing streams.
7598c2ecf20Sopenharmony_ci	 */
7608c2ecf20Sopenharmony_ci	for (i = 0; i < stream->outcnt; i++) {
7618c2ecf20Sopenharmony_ci		SCTP_SO(stream, i)->mid = 0;
7628c2ecf20Sopenharmony_ci		SCTP_SO(stream, i)->mid_uo = 0;
7638c2ecf20Sopenharmony_ci	}
7648c2ecf20Sopenharmony_ci	for (i = 0; i < stream->incnt; i++)
7658c2ecf20Sopenharmony_ci		SCTP_SI(stream, i)->mid = 0;
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	result = SCTP_STRRESET_PERFORMED;
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	*evp = sctp_ulpevent_make_assoc_reset_event(asoc, 0, init_tsn,
7708c2ecf20Sopenharmony_ci						    next_tsn, GFP_ATOMIC);
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ciout:
7738c2ecf20Sopenharmony_ci	sctp_update_strreset_result(asoc, result);
7748c2ecf20Sopenharmony_cierr:
7758c2ecf20Sopenharmony_ci	return sctp_make_strreset_tsnresp(asoc, result, request_seq,
7768c2ecf20Sopenharmony_ci					  next_tsn, init_tsn);
7778c2ecf20Sopenharmony_ci}
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_cistruct sctp_chunk *sctp_process_strreset_addstrm_out(
7808c2ecf20Sopenharmony_ci				struct sctp_association *asoc,
7818c2ecf20Sopenharmony_ci				union sctp_params param,
7828c2ecf20Sopenharmony_ci				struct sctp_ulpevent **evp)
7838c2ecf20Sopenharmony_ci{
7848c2ecf20Sopenharmony_ci	struct sctp_strreset_addstrm *addstrm = param.v;
7858c2ecf20Sopenharmony_ci	struct sctp_stream *stream = &asoc->stream;
7868c2ecf20Sopenharmony_ci	__u32 result = SCTP_STRRESET_DENIED;
7878c2ecf20Sopenharmony_ci	__u32 request_seq, incnt;
7888c2ecf20Sopenharmony_ci	__u16 in, i;
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci	request_seq = ntohl(addstrm->request_seq);
7918c2ecf20Sopenharmony_ci	if (TSN_lt(asoc->strreset_inseq, request_seq) ||
7928c2ecf20Sopenharmony_ci	    TSN_lt(request_seq, asoc->strreset_inseq - 2)) {
7938c2ecf20Sopenharmony_ci		result = SCTP_STRRESET_ERR_BAD_SEQNO;
7948c2ecf20Sopenharmony_ci		goto err;
7958c2ecf20Sopenharmony_ci	} else if (TSN_lt(request_seq, asoc->strreset_inseq)) {
7968c2ecf20Sopenharmony_ci		i = asoc->strreset_inseq - request_seq - 1;
7978c2ecf20Sopenharmony_ci		result = asoc->strreset_result[i];
7988c2ecf20Sopenharmony_ci		goto err;
7998c2ecf20Sopenharmony_ci	}
8008c2ecf20Sopenharmony_ci	asoc->strreset_inseq++;
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	if (!(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ))
8038c2ecf20Sopenharmony_ci		goto out;
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	in = ntohs(addstrm->number_of_streams);
8068c2ecf20Sopenharmony_ci	incnt = stream->incnt + in;
8078c2ecf20Sopenharmony_ci	if (!in || incnt > SCTP_MAX_STREAM)
8088c2ecf20Sopenharmony_ci		goto out;
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	if (sctp_stream_alloc_in(stream, incnt, GFP_ATOMIC))
8118c2ecf20Sopenharmony_ci		goto out;
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	if (asoc->strreset_chunk) {
8148c2ecf20Sopenharmony_ci		if (!sctp_chunk_lookup_strreset_param(
8158c2ecf20Sopenharmony_ci			asoc, 0, SCTP_PARAM_RESET_ADD_IN_STREAMS)) {
8168c2ecf20Sopenharmony_ci			/* same process with outstanding isn't 0 */
8178c2ecf20Sopenharmony_ci			result = SCTP_STRRESET_ERR_IN_PROGRESS;
8188c2ecf20Sopenharmony_ci			goto out;
8198c2ecf20Sopenharmony_ci		}
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci		asoc->strreset_outstanding--;
8228c2ecf20Sopenharmony_ci		asoc->strreset_outseq++;
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci		if (!asoc->strreset_outstanding) {
8258c2ecf20Sopenharmony_ci			struct sctp_transport *t;
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci			t = asoc->strreset_chunk->transport;
8288c2ecf20Sopenharmony_ci			if (del_timer(&t->reconf_timer))
8298c2ecf20Sopenharmony_ci				sctp_transport_put(t);
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci			sctp_chunk_put(asoc->strreset_chunk);
8328c2ecf20Sopenharmony_ci			asoc->strreset_chunk = NULL;
8338c2ecf20Sopenharmony_ci		}
8348c2ecf20Sopenharmony_ci	}
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	stream->incnt = incnt;
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	result = SCTP_STRRESET_PERFORMED;
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	*evp = sctp_ulpevent_make_stream_change_event(asoc,
8418c2ecf20Sopenharmony_ci		0, ntohs(addstrm->number_of_streams), 0, GFP_ATOMIC);
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ciout:
8448c2ecf20Sopenharmony_ci	sctp_update_strreset_result(asoc, result);
8458c2ecf20Sopenharmony_cierr:
8468c2ecf20Sopenharmony_ci	return sctp_make_strreset_resp(asoc, result, request_seq);
8478c2ecf20Sopenharmony_ci}
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_cistruct sctp_chunk *sctp_process_strreset_addstrm_in(
8508c2ecf20Sopenharmony_ci				struct sctp_association *asoc,
8518c2ecf20Sopenharmony_ci				union sctp_params param,
8528c2ecf20Sopenharmony_ci				struct sctp_ulpevent **evp)
8538c2ecf20Sopenharmony_ci{
8548c2ecf20Sopenharmony_ci	struct sctp_strreset_addstrm *addstrm = param.v;
8558c2ecf20Sopenharmony_ci	struct sctp_stream *stream = &asoc->stream;
8568c2ecf20Sopenharmony_ci	__u32 result = SCTP_STRRESET_DENIED;
8578c2ecf20Sopenharmony_ci	struct sctp_chunk *chunk = NULL;
8588c2ecf20Sopenharmony_ci	__u32 request_seq, outcnt;
8598c2ecf20Sopenharmony_ci	__u16 out, i;
8608c2ecf20Sopenharmony_ci	int ret;
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	request_seq = ntohl(addstrm->request_seq);
8638c2ecf20Sopenharmony_ci	if (TSN_lt(asoc->strreset_inseq, request_seq) ||
8648c2ecf20Sopenharmony_ci	    TSN_lt(request_seq, asoc->strreset_inseq - 2)) {
8658c2ecf20Sopenharmony_ci		result = SCTP_STRRESET_ERR_BAD_SEQNO;
8668c2ecf20Sopenharmony_ci		goto err;
8678c2ecf20Sopenharmony_ci	} else if (TSN_lt(request_seq, asoc->strreset_inseq)) {
8688c2ecf20Sopenharmony_ci		i = asoc->strreset_inseq - request_seq - 1;
8698c2ecf20Sopenharmony_ci		result = asoc->strreset_result[i];
8708c2ecf20Sopenharmony_ci		if (result == SCTP_STRRESET_PERFORMED)
8718c2ecf20Sopenharmony_ci			return NULL;
8728c2ecf20Sopenharmony_ci		goto err;
8738c2ecf20Sopenharmony_ci	}
8748c2ecf20Sopenharmony_ci	asoc->strreset_inseq++;
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci	if (!(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ))
8778c2ecf20Sopenharmony_ci		goto out;
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci	if (asoc->strreset_outstanding) {
8808c2ecf20Sopenharmony_ci		result = SCTP_STRRESET_ERR_IN_PROGRESS;
8818c2ecf20Sopenharmony_ci		goto out;
8828c2ecf20Sopenharmony_ci	}
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci	out = ntohs(addstrm->number_of_streams);
8858c2ecf20Sopenharmony_ci	outcnt = stream->outcnt + out;
8868c2ecf20Sopenharmony_ci	if (!out || outcnt > SCTP_MAX_STREAM)
8878c2ecf20Sopenharmony_ci		goto out;
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci	ret = sctp_stream_alloc_out(stream, outcnt, GFP_ATOMIC);
8908c2ecf20Sopenharmony_ci	if (ret)
8918c2ecf20Sopenharmony_ci		goto out;
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci	chunk = sctp_make_strreset_addstrm(asoc, out, 0);
8948c2ecf20Sopenharmony_ci	if (!chunk)
8958c2ecf20Sopenharmony_ci		goto out;
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci	asoc->strreset_chunk = chunk;
8988c2ecf20Sopenharmony_ci	asoc->strreset_outstanding = 1;
8998c2ecf20Sopenharmony_ci	sctp_chunk_hold(asoc->strreset_chunk);
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	stream->outcnt = outcnt;
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci	result = SCTP_STRRESET_PERFORMED;
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ciout:
9068c2ecf20Sopenharmony_ci	sctp_update_strreset_result(asoc, result);
9078c2ecf20Sopenharmony_cierr:
9088c2ecf20Sopenharmony_ci	if (!chunk)
9098c2ecf20Sopenharmony_ci		chunk = sctp_make_strreset_resp(asoc, result, request_seq);
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	return chunk;
9128c2ecf20Sopenharmony_ci}
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_cistruct sctp_chunk *sctp_process_strreset_resp(
9158c2ecf20Sopenharmony_ci				struct sctp_association *asoc,
9168c2ecf20Sopenharmony_ci				union sctp_params param,
9178c2ecf20Sopenharmony_ci				struct sctp_ulpevent **evp)
9188c2ecf20Sopenharmony_ci{
9198c2ecf20Sopenharmony_ci	struct sctp_stream *stream = &asoc->stream;
9208c2ecf20Sopenharmony_ci	struct sctp_strreset_resp *resp = param.v;
9218c2ecf20Sopenharmony_ci	struct sctp_transport *t;
9228c2ecf20Sopenharmony_ci	__u16 i, nums, flags = 0;
9238c2ecf20Sopenharmony_ci	struct sctp_paramhdr *req;
9248c2ecf20Sopenharmony_ci	__u32 result;
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci	req = sctp_chunk_lookup_strreset_param(asoc, resp->response_seq, 0);
9278c2ecf20Sopenharmony_ci	if (!req)
9288c2ecf20Sopenharmony_ci		return NULL;
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_ci	result = ntohl(resp->result);
9318c2ecf20Sopenharmony_ci	if (result != SCTP_STRRESET_PERFORMED) {
9328c2ecf20Sopenharmony_ci		/* if in progress, do nothing but retransmit */
9338c2ecf20Sopenharmony_ci		if (result == SCTP_STRRESET_IN_PROGRESS)
9348c2ecf20Sopenharmony_ci			return NULL;
9358c2ecf20Sopenharmony_ci		else if (result == SCTP_STRRESET_DENIED)
9368c2ecf20Sopenharmony_ci			flags = SCTP_STREAM_RESET_DENIED;
9378c2ecf20Sopenharmony_ci		else
9388c2ecf20Sopenharmony_ci			flags = SCTP_STREAM_RESET_FAILED;
9398c2ecf20Sopenharmony_ci	}
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci	if (req->type == SCTP_PARAM_RESET_OUT_REQUEST) {
9428c2ecf20Sopenharmony_ci		struct sctp_strreset_outreq *outreq;
9438c2ecf20Sopenharmony_ci		__be16 *str_p;
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci		outreq = (struct sctp_strreset_outreq *)req;
9468c2ecf20Sopenharmony_ci		str_p = outreq->list_of_streams;
9478c2ecf20Sopenharmony_ci		nums = (ntohs(outreq->param_hdr.length) - sizeof(*outreq)) /
9488c2ecf20Sopenharmony_ci		       sizeof(__u16);
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci		if (result == SCTP_STRRESET_PERFORMED) {
9518c2ecf20Sopenharmony_ci			struct sctp_stream_out *sout;
9528c2ecf20Sopenharmony_ci			if (nums) {
9538c2ecf20Sopenharmony_ci				for (i = 0; i < nums; i++) {
9548c2ecf20Sopenharmony_ci					sout = SCTP_SO(stream, ntohs(str_p[i]));
9558c2ecf20Sopenharmony_ci					sout->mid = 0;
9568c2ecf20Sopenharmony_ci					sout->mid_uo = 0;
9578c2ecf20Sopenharmony_ci				}
9588c2ecf20Sopenharmony_ci			} else {
9598c2ecf20Sopenharmony_ci				for (i = 0; i < stream->outcnt; i++) {
9608c2ecf20Sopenharmony_ci					sout = SCTP_SO(stream, i);
9618c2ecf20Sopenharmony_ci					sout->mid = 0;
9628c2ecf20Sopenharmony_ci					sout->mid_uo = 0;
9638c2ecf20Sopenharmony_ci				}
9648c2ecf20Sopenharmony_ci			}
9658c2ecf20Sopenharmony_ci		}
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci		flags |= SCTP_STREAM_RESET_OUTGOING_SSN;
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci		for (i = 0; i < stream->outcnt; i++)
9708c2ecf20Sopenharmony_ci			SCTP_SO(stream, i)->state = SCTP_STREAM_OPEN;
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci		*evp = sctp_ulpevent_make_stream_reset_event(asoc, flags,
9738c2ecf20Sopenharmony_ci			nums, str_p, GFP_ATOMIC);
9748c2ecf20Sopenharmony_ci	} else if (req->type == SCTP_PARAM_RESET_IN_REQUEST) {
9758c2ecf20Sopenharmony_ci		struct sctp_strreset_inreq *inreq;
9768c2ecf20Sopenharmony_ci		__be16 *str_p;
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci		/* if the result is performed, it's impossible for inreq */
9798c2ecf20Sopenharmony_ci		if (result == SCTP_STRRESET_PERFORMED)
9808c2ecf20Sopenharmony_ci			return NULL;
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci		inreq = (struct sctp_strreset_inreq *)req;
9838c2ecf20Sopenharmony_ci		str_p = inreq->list_of_streams;
9848c2ecf20Sopenharmony_ci		nums = (ntohs(inreq->param_hdr.length) - sizeof(*inreq)) /
9858c2ecf20Sopenharmony_ci		       sizeof(__u16);
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci		flags |= SCTP_STREAM_RESET_INCOMING_SSN;
9888c2ecf20Sopenharmony_ci
9898c2ecf20Sopenharmony_ci		*evp = sctp_ulpevent_make_stream_reset_event(asoc, flags,
9908c2ecf20Sopenharmony_ci			nums, str_p, GFP_ATOMIC);
9918c2ecf20Sopenharmony_ci	} else if (req->type == SCTP_PARAM_RESET_TSN_REQUEST) {
9928c2ecf20Sopenharmony_ci		struct sctp_strreset_resptsn *resptsn;
9938c2ecf20Sopenharmony_ci		__u32 stsn, rtsn;
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci		/* check for resptsn, as sctp_verify_reconf didn't do it*/
9968c2ecf20Sopenharmony_ci		if (ntohs(param.p->length) != sizeof(*resptsn))
9978c2ecf20Sopenharmony_ci			return NULL;
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci		resptsn = (struct sctp_strreset_resptsn *)resp;
10008c2ecf20Sopenharmony_ci		stsn = ntohl(resptsn->senders_next_tsn);
10018c2ecf20Sopenharmony_ci		rtsn = ntohl(resptsn->receivers_next_tsn);
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_ci		if (result == SCTP_STRRESET_PERFORMED) {
10048c2ecf20Sopenharmony_ci			__u32 mtsn = sctp_tsnmap_get_max_tsn_seen(
10058c2ecf20Sopenharmony_ci						&asoc->peer.tsn_map);
10068c2ecf20Sopenharmony_ci			LIST_HEAD(temp);
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci			asoc->stream.si->report_ftsn(&asoc->ulpq, mtsn);
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci			sctp_tsnmap_init(&asoc->peer.tsn_map,
10118c2ecf20Sopenharmony_ci					 SCTP_TSN_MAP_INITIAL,
10128c2ecf20Sopenharmony_ci					 stsn, GFP_ATOMIC);
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci			/* Clean up sacked and abandoned queues only. As the
10158c2ecf20Sopenharmony_ci			 * out_chunk_list may not be empty, splice it to temp,
10168c2ecf20Sopenharmony_ci			 * then get it back after sctp_outq_free is done.
10178c2ecf20Sopenharmony_ci			 */
10188c2ecf20Sopenharmony_ci			list_splice_init(&asoc->outqueue.out_chunk_list, &temp);
10198c2ecf20Sopenharmony_ci			sctp_outq_free(&asoc->outqueue);
10208c2ecf20Sopenharmony_ci			list_splice_init(&temp, &asoc->outqueue.out_chunk_list);
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ci			asoc->next_tsn = rtsn;
10238c2ecf20Sopenharmony_ci			asoc->ctsn_ack_point = asoc->next_tsn - 1;
10248c2ecf20Sopenharmony_ci			asoc->adv_peer_ack_point = asoc->ctsn_ack_point;
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_ci			for (i = 0; i < stream->outcnt; i++) {
10278c2ecf20Sopenharmony_ci				SCTP_SO(stream, i)->mid = 0;
10288c2ecf20Sopenharmony_ci				SCTP_SO(stream, i)->mid_uo = 0;
10298c2ecf20Sopenharmony_ci			}
10308c2ecf20Sopenharmony_ci			for (i = 0; i < stream->incnt; i++)
10318c2ecf20Sopenharmony_ci				SCTP_SI(stream, i)->mid = 0;
10328c2ecf20Sopenharmony_ci		}
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_ci		for (i = 0; i < stream->outcnt; i++)
10358c2ecf20Sopenharmony_ci			SCTP_SO(stream, i)->state = SCTP_STREAM_OPEN;
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci		*evp = sctp_ulpevent_make_assoc_reset_event(asoc, flags,
10388c2ecf20Sopenharmony_ci			stsn, rtsn, GFP_ATOMIC);
10398c2ecf20Sopenharmony_ci	} else if (req->type == SCTP_PARAM_RESET_ADD_OUT_STREAMS) {
10408c2ecf20Sopenharmony_ci		struct sctp_strreset_addstrm *addstrm;
10418c2ecf20Sopenharmony_ci		__u16 number;
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_ci		addstrm = (struct sctp_strreset_addstrm *)req;
10448c2ecf20Sopenharmony_ci		nums = ntohs(addstrm->number_of_streams);
10458c2ecf20Sopenharmony_ci		number = stream->outcnt - nums;
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_ci		if (result == SCTP_STRRESET_PERFORMED) {
10488c2ecf20Sopenharmony_ci			for (i = number; i < stream->outcnt; i++)
10498c2ecf20Sopenharmony_ci				SCTP_SO(stream, i)->state = SCTP_STREAM_OPEN;
10508c2ecf20Sopenharmony_ci		} else {
10518c2ecf20Sopenharmony_ci			sctp_stream_shrink_out(stream, number);
10528c2ecf20Sopenharmony_ci			stream->outcnt = number;
10538c2ecf20Sopenharmony_ci		}
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ci		*evp = sctp_ulpevent_make_stream_change_event(asoc, flags,
10568c2ecf20Sopenharmony_ci			0, nums, GFP_ATOMIC);
10578c2ecf20Sopenharmony_ci	} else if (req->type == SCTP_PARAM_RESET_ADD_IN_STREAMS) {
10588c2ecf20Sopenharmony_ci		struct sctp_strreset_addstrm *addstrm;
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_ci		/* if the result is performed, it's impossible for addstrm in
10618c2ecf20Sopenharmony_ci		 * request.
10628c2ecf20Sopenharmony_ci		 */
10638c2ecf20Sopenharmony_ci		if (result == SCTP_STRRESET_PERFORMED)
10648c2ecf20Sopenharmony_ci			return NULL;
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_ci		addstrm = (struct sctp_strreset_addstrm *)req;
10678c2ecf20Sopenharmony_ci		nums = ntohs(addstrm->number_of_streams);
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_ci		*evp = sctp_ulpevent_make_stream_change_event(asoc, flags,
10708c2ecf20Sopenharmony_ci			nums, 0, GFP_ATOMIC);
10718c2ecf20Sopenharmony_ci	}
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci	asoc->strreset_outstanding--;
10748c2ecf20Sopenharmony_ci	asoc->strreset_outseq++;
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci	/* remove everything for this reconf request */
10778c2ecf20Sopenharmony_ci	if (!asoc->strreset_outstanding) {
10788c2ecf20Sopenharmony_ci		t = asoc->strreset_chunk->transport;
10798c2ecf20Sopenharmony_ci		if (del_timer(&t->reconf_timer))
10808c2ecf20Sopenharmony_ci			sctp_transport_put(t);
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_ci		sctp_chunk_put(asoc->strreset_chunk);
10838c2ecf20Sopenharmony_ci		asoc->strreset_chunk = NULL;
10848c2ecf20Sopenharmony_ci	}
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_ci	return NULL;
10878c2ecf20Sopenharmony_ci}
1088