xref: /openbmc/linux/net/sctp/stream.c (revision 1fdb8d8f)
1a8386317SXin Long /* SCTP kernel implementation
2a8386317SXin Long  * (C) Copyright IBM Corp. 2001, 2004
3a8386317SXin Long  * Copyright (c) 1999-2000 Cisco, Inc.
4a8386317SXin Long  * Copyright (c) 1999-2001 Motorola, Inc.
5a8386317SXin Long  * Copyright (c) 2001 Intel Corp.
6a8386317SXin Long  *
7a8386317SXin Long  * This file is part of the SCTP kernel implementation
8a8386317SXin Long  *
9a8386317SXin Long  * These functions manipulate sctp tsn mapping array.
10a8386317SXin Long  *
11a8386317SXin Long  * This SCTP implementation is free software;
12a8386317SXin Long  * you can redistribute it and/or modify it under the terms of
13a8386317SXin Long  * the GNU General Public License as published by
14a8386317SXin Long  * the Free Software Foundation; either version 2, or (at your option)
15a8386317SXin Long  * any later version.
16a8386317SXin Long  *
17a8386317SXin Long  * This SCTP implementation is distributed in the hope that it
18a8386317SXin Long  * will be useful, but WITHOUT ANY WARRANTY; without even the implied
19a8386317SXin Long  *                 ************************
20a8386317SXin Long  * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
21a8386317SXin Long  * See the GNU General Public License for more details.
22a8386317SXin Long  *
23a8386317SXin Long  * You should have received a copy of the GNU General Public License
24a8386317SXin Long  * along with GNU CC; see the file COPYING.  If not, see
25a8386317SXin Long  * <http://www.gnu.org/licenses/>.
26a8386317SXin Long  *
27a8386317SXin Long  * Please send any bug reports or fixes you make to the
28a8386317SXin Long  * email address(es):
29a8386317SXin Long  *    lksctp developers <linux-sctp@vger.kernel.org>
30a8386317SXin Long  *
31a8386317SXin Long  * Written or modified by:
32a8386317SXin Long  *    Xin Long <lucien.xin@gmail.com>
33a8386317SXin Long  */
34a8386317SXin Long 
35a8386317SXin Long #include <net/sctp/sctp.h>
367f9d68acSXin Long #include <net/sctp/sm.h>
37a8386317SXin Long 
38e090abd0SMarcelo Ricardo Leitner static int sctp_stream_alloc_out(struct sctp_stream *stream, __u16 outcnt,
39e090abd0SMarcelo Ricardo Leitner 				 gfp_t gfp)
40e090abd0SMarcelo Ricardo Leitner {
41e090abd0SMarcelo Ricardo Leitner 	struct sctp_stream_out *out;
42e090abd0SMarcelo Ricardo Leitner 
43e090abd0SMarcelo Ricardo Leitner 	out = kmalloc_array(outcnt, sizeof(*out), gfp);
44e090abd0SMarcelo Ricardo Leitner 	if (!out)
45e090abd0SMarcelo Ricardo Leitner 		return -ENOMEM;
46e090abd0SMarcelo Ricardo Leitner 
47e090abd0SMarcelo Ricardo Leitner 	if (stream->out) {
48e090abd0SMarcelo Ricardo Leitner 		memcpy(out, stream->out, min(outcnt, stream->outcnt) *
49e090abd0SMarcelo Ricardo Leitner 					 sizeof(*out));
50e090abd0SMarcelo Ricardo Leitner 		kfree(stream->out);
51e090abd0SMarcelo Ricardo Leitner 	}
52e090abd0SMarcelo Ricardo Leitner 
53e090abd0SMarcelo Ricardo Leitner 	if (outcnt > stream->outcnt)
54e090abd0SMarcelo Ricardo Leitner 		memset(out + stream->outcnt, 0,
55e090abd0SMarcelo Ricardo Leitner 		       (outcnt - stream->outcnt) * sizeof(*out));
56e090abd0SMarcelo Ricardo Leitner 
57e090abd0SMarcelo Ricardo Leitner 	stream->out = out;
58e090abd0SMarcelo Ricardo Leitner 
59e090abd0SMarcelo Ricardo Leitner 	return 0;
60e090abd0SMarcelo Ricardo Leitner }
61e090abd0SMarcelo Ricardo Leitner 
621fdb8d8fSMarcelo Ricardo Leitner static int sctp_stream_alloc_in(struct sctp_stream *stream, __u16 incnt,
631fdb8d8fSMarcelo Ricardo Leitner 				gfp_t gfp)
641fdb8d8fSMarcelo Ricardo Leitner {
651fdb8d8fSMarcelo Ricardo Leitner 	struct sctp_stream_in *in;
661fdb8d8fSMarcelo Ricardo Leitner 
671fdb8d8fSMarcelo Ricardo Leitner 	in = kmalloc_array(incnt, sizeof(*stream->in), gfp);
681fdb8d8fSMarcelo Ricardo Leitner 
691fdb8d8fSMarcelo Ricardo Leitner 	if (!in)
701fdb8d8fSMarcelo Ricardo Leitner 		return -ENOMEM;
711fdb8d8fSMarcelo Ricardo Leitner 
721fdb8d8fSMarcelo Ricardo Leitner 	if (stream->in) {
731fdb8d8fSMarcelo Ricardo Leitner 		memcpy(in, stream->in, min(incnt, stream->incnt) *
741fdb8d8fSMarcelo Ricardo Leitner 				       sizeof(*in));
751fdb8d8fSMarcelo Ricardo Leitner 		kfree(stream->in);
761fdb8d8fSMarcelo Ricardo Leitner 	}
771fdb8d8fSMarcelo Ricardo Leitner 
781fdb8d8fSMarcelo Ricardo Leitner 	if (incnt > stream->incnt)
791fdb8d8fSMarcelo Ricardo Leitner 		memset(in + stream->incnt, 0,
801fdb8d8fSMarcelo Ricardo Leitner 		       (incnt - stream->incnt) * sizeof(*in));
811fdb8d8fSMarcelo Ricardo Leitner 
821fdb8d8fSMarcelo Ricardo Leitner 	stream->in = in;
831fdb8d8fSMarcelo Ricardo Leitner 
841fdb8d8fSMarcelo Ricardo Leitner 	return 0;
851fdb8d8fSMarcelo Ricardo Leitner }
861fdb8d8fSMarcelo Ricardo Leitner 
87ff356414SXin Long int sctp_stream_init(struct sctp_stream *stream, __u16 outcnt, __u16 incnt,
88ff356414SXin Long 		     gfp_t gfp)
89a8386317SXin Long {
903dbcc105SXin Long 	int i;
913dbcc105SXin Long 
921ae2eaaaSMarcelo Ricardo Leitner 	gfp |= __GFP_NOWARN;
931ae2eaaaSMarcelo Ricardo Leitner 
943dbcc105SXin Long 	/* Initial stream->out size may be very big, so free it and alloc
951ae2eaaaSMarcelo Ricardo Leitner 	 * a new one with new outcnt to save memory if needed.
963dbcc105SXin Long 	 */
971ae2eaaaSMarcelo Ricardo Leitner 	if (outcnt == stream->outcnt)
981ae2eaaaSMarcelo Ricardo Leitner 		goto in;
991ae2eaaaSMarcelo Ricardo Leitner 
100e090abd0SMarcelo Ricardo Leitner 	i = sctp_stream_alloc_out(stream, outcnt, gfp);
101e090abd0SMarcelo Ricardo Leitner 	if (i)
102e090abd0SMarcelo Ricardo Leitner 		return i;
1033dbcc105SXin Long 
104ff356414SXin Long 	stream->outcnt = outcnt;
1053dbcc105SXin Long 	for (i = 0; i < stream->outcnt; i++)
1063dbcc105SXin Long 		stream->out[i].state = SCTP_STREAM_OPEN;
1073dbcc105SXin Long 
1081ae2eaaaSMarcelo Ricardo Leitner in:
109ff356414SXin Long 	if (!incnt)
110ff356414SXin Long 		return 0;
111ff356414SXin Long 
1121fdb8d8fSMarcelo Ricardo Leitner 	i = sctp_stream_alloc_in(stream, incnt, gfp);
1131fdb8d8fSMarcelo Ricardo Leitner 	if (i) {
114a8386317SXin Long 		kfree(stream->out);
115cee360abSXin Long 		stream->out = NULL;
116cee360abSXin Long 		return -ENOMEM;
117a8386317SXin Long 	}
118a8386317SXin Long 
119ff356414SXin Long 	stream->incnt = incnt;
120ff356414SXin Long 
1213dbcc105SXin Long 	return 0;
122a8386317SXin Long }
123a8386317SXin Long 
124a8386317SXin Long void sctp_stream_free(struct sctp_stream *stream)
125a8386317SXin Long {
126a8386317SXin Long 	kfree(stream->out);
127a8386317SXin Long 	kfree(stream->in);
128a8386317SXin Long }
129a8386317SXin Long 
130a8386317SXin Long void sctp_stream_clear(struct sctp_stream *stream)
131a8386317SXin Long {
132a8386317SXin Long 	int i;
133a8386317SXin Long 
134a8386317SXin Long 	for (i = 0; i < stream->outcnt; i++)
135a8386317SXin Long 		stream->out[i].ssn = 0;
136a8386317SXin Long 
137a8386317SXin Long 	for (i = 0; i < stream->incnt; i++)
138a8386317SXin Long 		stream->in[i].ssn = 0;
139a8386317SXin Long }
1407f9d68acSXin Long 
141cee360abSXin Long void sctp_stream_update(struct sctp_stream *stream, struct sctp_stream *new)
142cee360abSXin Long {
143cee360abSXin Long 	sctp_stream_free(stream);
144cee360abSXin Long 
145cee360abSXin Long 	stream->out = new->out;
146cee360abSXin Long 	stream->in  = new->in;
147cee360abSXin Long 	stream->outcnt = new->outcnt;
148cee360abSXin Long 	stream->incnt  = new->incnt;
149cee360abSXin Long 
150cee360abSXin Long 	new->out = NULL;
151cee360abSXin Long 	new->in  = NULL;
152cee360abSXin Long }
153cee360abSXin Long 
1547f9d68acSXin Long static int sctp_send_reconf(struct sctp_association *asoc,
1557f9d68acSXin Long 			    struct sctp_chunk *chunk)
1567f9d68acSXin Long {
1577f9d68acSXin Long 	struct net *net = sock_net(asoc->base.sk);
1587f9d68acSXin Long 	int retval = 0;
1597f9d68acSXin Long 
1607f9d68acSXin Long 	retval = sctp_primitive_RECONF(net, asoc, chunk);
1617f9d68acSXin Long 	if (retval)
1627f9d68acSXin Long 		sctp_chunk_free(chunk);
1637f9d68acSXin Long 
1647f9d68acSXin Long 	return retval;
1657f9d68acSXin Long }
1667f9d68acSXin Long 
1677f9d68acSXin Long int sctp_send_reset_streams(struct sctp_association *asoc,
1687f9d68acSXin Long 			    struct sctp_reset_streams *params)
1697f9d68acSXin Long {
170cee360abSXin Long 	struct sctp_stream *stream = &asoc->stream;
1717f9d68acSXin Long 	__u16 i, str_nums, *str_list;
1727f9d68acSXin Long 	struct sctp_chunk *chunk;
1737f9d68acSXin Long 	int retval = -EINVAL;
1747f9d68acSXin Long 	bool out, in;
1757f9d68acSXin Long 
1767f9d68acSXin Long 	if (!asoc->peer.reconf_capable ||
1777f9d68acSXin Long 	    !(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ)) {
1787f9d68acSXin Long 		retval = -ENOPROTOOPT;
1797f9d68acSXin Long 		goto out;
1807f9d68acSXin Long 	}
1817f9d68acSXin Long 
1827f9d68acSXin Long 	if (asoc->strreset_outstanding) {
1837f9d68acSXin Long 		retval = -EINPROGRESS;
1847f9d68acSXin Long 		goto out;
1857f9d68acSXin Long 	}
1867f9d68acSXin Long 
1877f9d68acSXin Long 	out = params->srs_flags & SCTP_STREAM_RESET_OUTGOING;
1887f9d68acSXin Long 	in  = params->srs_flags & SCTP_STREAM_RESET_INCOMING;
1897f9d68acSXin Long 	if (!out && !in)
1907f9d68acSXin Long 		goto out;
1917f9d68acSXin Long 
1927f9d68acSXin Long 	str_nums = params->srs_number_streams;
1937f9d68acSXin Long 	str_list = params->srs_stream_list;
1947f9d68acSXin Long 	if (out && str_nums)
1957f9d68acSXin Long 		for (i = 0; i < str_nums; i++)
1967f9d68acSXin Long 			if (str_list[i] >= stream->outcnt)
1977f9d68acSXin Long 				goto out;
1987f9d68acSXin Long 
1997f9d68acSXin Long 	if (in && str_nums)
2007f9d68acSXin Long 		for (i = 0; i < str_nums; i++)
2017f9d68acSXin Long 			if (str_list[i] >= stream->incnt)
2027f9d68acSXin Long 				goto out;
2037f9d68acSXin Long 
20416e1a919SXin Long 	for (i = 0; i < str_nums; i++)
20516e1a919SXin Long 		str_list[i] = htons(str_list[i]);
20616e1a919SXin Long 
2077f9d68acSXin Long 	chunk = sctp_make_strreset_req(asoc, str_nums, str_list, out, in);
20816e1a919SXin Long 
20916e1a919SXin Long 	for (i = 0; i < str_nums; i++)
21016e1a919SXin Long 		str_list[i] = ntohs(str_list[i]);
21116e1a919SXin Long 
212119aecbaSXin Long 	if (!chunk) {
213119aecbaSXin Long 		retval = -ENOMEM;
2147f9d68acSXin Long 		goto out;
215119aecbaSXin Long 	}
2167f9d68acSXin Long 
2177f9d68acSXin Long 	if (out) {
2187f9d68acSXin Long 		if (str_nums)
2197f9d68acSXin Long 			for (i = 0; i < str_nums; i++)
2207f9d68acSXin Long 				stream->out[str_list[i]].state =
2217f9d68acSXin Long 						       SCTP_STREAM_CLOSED;
2227f9d68acSXin Long 		else
2237f9d68acSXin Long 			for (i = 0; i < stream->outcnt; i++)
2247f9d68acSXin Long 				stream->out[i].state = SCTP_STREAM_CLOSED;
2257f9d68acSXin Long 	}
2267f9d68acSXin Long 
2277f9d68acSXin Long 	asoc->strreset_chunk = chunk;
2287f9d68acSXin Long 	sctp_chunk_hold(asoc->strreset_chunk);
2297f9d68acSXin Long 
2307f9d68acSXin Long 	retval = sctp_send_reconf(asoc, chunk);
2317f9d68acSXin Long 	if (retval) {
2327f9d68acSXin Long 		sctp_chunk_put(asoc->strreset_chunk);
2337f9d68acSXin Long 		asoc->strreset_chunk = NULL;
234119aecbaSXin Long 		if (!out)
235119aecbaSXin Long 			goto out;
236119aecbaSXin Long 
237119aecbaSXin Long 		if (str_nums)
238119aecbaSXin Long 			for (i = 0; i < str_nums; i++)
239119aecbaSXin Long 				stream->out[str_list[i]].state =
240119aecbaSXin Long 						       SCTP_STREAM_OPEN;
241119aecbaSXin Long 		else
242119aecbaSXin Long 			for (i = 0; i < stream->outcnt; i++)
243119aecbaSXin Long 				stream->out[i].state = SCTP_STREAM_OPEN;
244119aecbaSXin Long 
245119aecbaSXin Long 		goto out;
2467f9d68acSXin Long 	}
2477f9d68acSXin Long 
248119aecbaSXin Long 	asoc->strreset_outstanding = out + in;
249119aecbaSXin Long 
2507f9d68acSXin Long out:
2517f9d68acSXin Long 	return retval;
2527f9d68acSXin Long }
253a92ce1a4SXin Long 
254a92ce1a4SXin Long int sctp_send_reset_assoc(struct sctp_association *asoc)
255a92ce1a4SXin Long {
256cee360abSXin Long 	struct sctp_stream *stream = &asoc->stream;
257a92ce1a4SXin Long 	struct sctp_chunk *chunk = NULL;
258a92ce1a4SXin Long 	int retval;
259a92ce1a4SXin Long 	__u16 i;
260a92ce1a4SXin Long 
261a92ce1a4SXin Long 	if (!asoc->peer.reconf_capable ||
262a92ce1a4SXin Long 	    !(asoc->strreset_enable & SCTP_ENABLE_RESET_ASSOC_REQ))
263a92ce1a4SXin Long 		return -ENOPROTOOPT;
264a92ce1a4SXin Long 
265a92ce1a4SXin Long 	if (asoc->strreset_outstanding)
266a92ce1a4SXin Long 		return -EINPROGRESS;
267a92ce1a4SXin Long 
268a92ce1a4SXin Long 	chunk = sctp_make_strreset_tsnreq(asoc);
269a92ce1a4SXin Long 	if (!chunk)
270a92ce1a4SXin Long 		return -ENOMEM;
271a92ce1a4SXin Long 
272a92ce1a4SXin Long 	/* Block further xmit of data until this request is completed */
273cee360abSXin Long 	for (i = 0; i < stream->outcnt; i++)
274cee360abSXin Long 		stream->out[i].state = SCTP_STREAM_CLOSED;
275a92ce1a4SXin Long 
276a92ce1a4SXin Long 	asoc->strreset_chunk = chunk;
277a92ce1a4SXin Long 	sctp_chunk_hold(asoc->strreset_chunk);
278a92ce1a4SXin Long 
279a92ce1a4SXin Long 	retval = sctp_send_reconf(asoc, chunk);
280a92ce1a4SXin Long 	if (retval) {
281a92ce1a4SXin Long 		sctp_chunk_put(asoc->strreset_chunk);
282a92ce1a4SXin Long 		asoc->strreset_chunk = NULL;
283a92ce1a4SXin Long 
284cee360abSXin Long 		for (i = 0; i < stream->outcnt; i++)
285cee360abSXin Long 			stream->out[i].state = SCTP_STREAM_OPEN;
286a92ce1a4SXin Long 
287a92ce1a4SXin Long 		return retval;
288a92ce1a4SXin Long 	}
289a92ce1a4SXin Long 
290a92ce1a4SXin Long 	asoc->strreset_outstanding = 1;
291a92ce1a4SXin Long 
292a92ce1a4SXin Long 	return 0;
293a92ce1a4SXin Long }
294242bd2d5SXin Long 
295242bd2d5SXin Long int sctp_send_add_streams(struct sctp_association *asoc,
296242bd2d5SXin Long 			  struct sctp_add_streams *params)
297242bd2d5SXin Long {
298cee360abSXin Long 	struct sctp_stream *stream = &asoc->stream;
299242bd2d5SXin Long 	struct sctp_chunk *chunk = NULL;
300242bd2d5SXin Long 	int retval = -ENOMEM;
301242bd2d5SXin Long 	__u32 outcnt, incnt;
302242bd2d5SXin Long 	__u16 out, in;
303242bd2d5SXin Long 
304242bd2d5SXin Long 	if (!asoc->peer.reconf_capable ||
305242bd2d5SXin Long 	    !(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ)) {
306242bd2d5SXin Long 		retval = -ENOPROTOOPT;
307242bd2d5SXin Long 		goto out;
308242bd2d5SXin Long 	}
309242bd2d5SXin Long 
310242bd2d5SXin Long 	if (asoc->strreset_outstanding) {
311242bd2d5SXin Long 		retval = -EINPROGRESS;
312242bd2d5SXin Long 		goto out;
313242bd2d5SXin Long 	}
314242bd2d5SXin Long 
315242bd2d5SXin Long 	out = params->sas_outstrms;
316242bd2d5SXin Long 	in  = params->sas_instrms;
317242bd2d5SXin Long 	outcnt = stream->outcnt + out;
318242bd2d5SXin Long 	incnt = stream->incnt + in;
319242bd2d5SXin Long 	if (outcnt > SCTP_MAX_STREAM || incnt > SCTP_MAX_STREAM ||
320242bd2d5SXin Long 	    (!out && !in)) {
321242bd2d5SXin Long 		retval = -EINVAL;
322242bd2d5SXin Long 		goto out;
323242bd2d5SXin Long 	}
324242bd2d5SXin Long 
325242bd2d5SXin Long 	if (out) {
326e090abd0SMarcelo Ricardo Leitner 		retval = sctp_stream_alloc_out(stream, outcnt, GFP_KERNEL);
327e090abd0SMarcelo Ricardo Leitner 		if (retval)
328242bd2d5SXin Long 			goto out;
329242bd2d5SXin Long 	}
330242bd2d5SXin Long 
331242bd2d5SXin Long 	chunk = sctp_make_strreset_addstrm(asoc, out, in);
332242bd2d5SXin Long 	if (!chunk)
333242bd2d5SXin Long 		goto out;
334242bd2d5SXin Long 
335242bd2d5SXin Long 	asoc->strreset_chunk = chunk;
336242bd2d5SXin Long 	sctp_chunk_hold(asoc->strreset_chunk);
337242bd2d5SXin Long 
338242bd2d5SXin Long 	retval = sctp_send_reconf(asoc, chunk);
339242bd2d5SXin Long 	if (retval) {
340242bd2d5SXin Long 		sctp_chunk_put(asoc->strreset_chunk);
341242bd2d5SXin Long 		asoc->strreset_chunk = NULL;
342242bd2d5SXin Long 		goto out;
343242bd2d5SXin Long 	}
344242bd2d5SXin Long 
345242bd2d5SXin Long 	stream->incnt = incnt;
346242bd2d5SXin Long 	stream->outcnt = outcnt;
347242bd2d5SXin Long 
348242bd2d5SXin Long 	asoc->strreset_outstanding = !!out + !!in;
349242bd2d5SXin Long 
350242bd2d5SXin Long out:
351242bd2d5SXin Long 	return retval;
352242bd2d5SXin Long }
35381054476SXin Long 
3543c918704SXin Long static struct sctp_paramhdr *sctp_chunk_lookup_strreset_param(
35550a41591SXin Long 			struct sctp_association *asoc, __u32 resp_seq,
35650a41591SXin Long 			__be16 type)
35781054476SXin Long {
35881054476SXin Long 	struct sctp_chunk *chunk = asoc->strreset_chunk;
35981054476SXin Long 	struct sctp_reconf_chunk *hdr;
36081054476SXin Long 	union sctp_params param;
36181054476SXin Long 
36250a41591SXin Long 	if (!chunk)
36381054476SXin Long 		return NULL;
36481054476SXin Long 
36581054476SXin Long 	hdr = (struct sctp_reconf_chunk *)chunk->chunk_hdr;
36681054476SXin Long 	sctp_walk_params(param, hdr, params) {
36781054476SXin Long 		/* sctp_strreset_tsnreq is actually the basic structure
36881054476SXin Long 		 * of all stream reconf params, so it's safe to use it
36981054476SXin Long 		 * to access request_seq.
37081054476SXin Long 		 */
37181054476SXin Long 		struct sctp_strreset_tsnreq *req = param.v;
37281054476SXin Long 
37350a41591SXin Long 		if ((!resp_seq || req->request_seq == resp_seq) &&
37450a41591SXin Long 		    (!type || type == req->param_hdr.type))
37581054476SXin Long 			return param.v;
37681054476SXin Long 	}
37781054476SXin Long 
37881054476SXin Long 	return NULL;
37981054476SXin Long }
38081054476SXin Long 
381e4dc99c7SXin Long static void sctp_update_strreset_result(struct sctp_association *asoc,
382e4dc99c7SXin Long 					__u32 result)
383e4dc99c7SXin Long {
384e4dc99c7SXin Long 	asoc->strreset_result[1] = asoc->strreset_result[0];
385e4dc99c7SXin Long 	asoc->strreset_result[0] = result;
386e4dc99c7SXin Long }
387e4dc99c7SXin Long 
38881054476SXin Long struct sctp_chunk *sctp_process_strreset_outreq(
38981054476SXin Long 				struct sctp_association *asoc,
39081054476SXin Long 				union sctp_params param,
39181054476SXin Long 				struct sctp_ulpevent **evp)
39281054476SXin Long {
39381054476SXin Long 	struct sctp_strreset_outreq *outreq = param.v;
394cee360abSXin Long 	struct sctp_stream *stream = &asoc->stream;
39581054476SXin Long 	__u16 i, nums, flags = 0, *str_p = NULL;
39681054476SXin Long 	__u32 result = SCTP_STRRESET_DENIED;
39781054476SXin Long 	__u32 request_seq;
39881054476SXin Long 
39981054476SXin Long 	request_seq = ntohl(outreq->request_seq);
40081054476SXin Long 
40181054476SXin Long 	if (ntohl(outreq->send_reset_at_tsn) >
40281054476SXin Long 	    sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map)) {
40381054476SXin Long 		result = SCTP_STRRESET_IN_PROGRESS;
404e4dc99c7SXin Long 		goto err;
40581054476SXin Long 	}
40681054476SXin Long 
407e4dc99c7SXin Long 	if (TSN_lt(asoc->strreset_inseq, request_seq) ||
408e4dc99c7SXin Long 	    TSN_lt(request_seq, asoc->strreset_inseq - 2)) {
40981054476SXin Long 		result = SCTP_STRRESET_ERR_BAD_SEQNO;
410e4dc99c7SXin Long 		goto err;
411e4dc99c7SXin Long 	} else if (TSN_lt(request_seq, asoc->strreset_inseq)) {
412e4dc99c7SXin Long 		i = asoc->strreset_inseq - request_seq - 1;
413e4dc99c7SXin Long 		result = asoc->strreset_result[i];
414e4dc99c7SXin Long 		goto err;
41581054476SXin Long 	}
416e4dc99c7SXin Long 	asoc->strreset_inseq++;
41781054476SXin Long 
41881054476SXin Long 	/* Check strreset_enable after inseq inc, as sender cannot tell
41981054476SXin Long 	 * the peer doesn't enable strreset after receiving response with
42081054476SXin Long 	 * result denied, as well as to keep consistent with bsd.
42181054476SXin Long 	 */
42281054476SXin Long 	if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ))
42381054476SXin Long 		goto out;
42481054476SXin Long 
42581054476SXin Long 	if (asoc->strreset_chunk) {
42650a41591SXin Long 		if (!sctp_chunk_lookup_strreset_param(
42750a41591SXin Long 				asoc, outreq->response_seq,
42850a41591SXin Long 				SCTP_PARAM_RESET_IN_REQUEST)) {
42981054476SXin Long 			/* same process with outstanding isn't 0 */
43081054476SXin Long 			result = SCTP_STRRESET_ERR_IN_PROGRESS;
43181054476SXin Long 			goto out;
43281054476SXin Long 		}
43381054476SXin Long 
43481054476SXin Long 		asoc->strreset_outstanding--;
43581054476SXin Long 		asoc->strreset_outseq++;
43681054476SXin Long 
43781054476SXin Long 		if (!asoc->strreset_outstanding) {
43850a41591SXin Long 			struct sctp_transport *t;
43950a41591SXin Long 
44081054476SXin Long 			t = asoc->strreset_chunk->transport;
44181054476SXin Long 			if (del_timer(&t->reconf_timer))
44281054476SXin Long 				sctp_transport_put(t);
44381054476SXin Long 
44481054476SXin Long 			sctp_chunk_put(asoc->strreset_chunk);
44581054476SXin Long 			asoc->strreset_chunk = NULL;
44681054476SXin Long 		}
44781054476SXin Long 
44881054476SXin Long 		flags = SCTP_STREAM_RESET_INCOMING_SSN;
44981054476SXin Long 	}
45081054476SXin Long 
45181054476SXin Long 	nums = (ntohs(param.p->length) - sizeof(*outreq)) / 2;
45281054476SXin Long 	if (nums) {
45381054476SXin Long 		str_p = outreq->list_of_streams;
45481054476SXin Long 		for (i = 0; i < nums; i++) {
45581054476SXin Long 			if (ntohs(str_p[i]) >= stream->incnt) {
45681054476SXin Long 				result = SCTP_STRRESET_ERR_WRONG_SSN;
45781054476SXin Long 				goto out;
45881054476SXin Long 			}
45981054476SXin Long 		}
46081054476SXin Long 
46181054476SXin Long 		for (i = 0; i < nums; i++)
46281054476SXin Long 			stream->in[ntohs(str_p[i])].ssn = 0;
46381054476SXin Long 	} else {
46481054476SXin Long 		for (i = 0; i < stream->incnt; i++)
46581054476SXin Long 			stream->in[i].ssn = 0;
46681054476SXin Long 	}
46781054476SXin Long 
46881054476SXin Long 	result = SCTP_STRRESET_PERFORMED;
46981054476SXin Long 
47081054476SXin Long 	*evp = sctp_ulpevent_make_stream_reset_event(asoc,
47181054476SXin Long 		flags | SCTP_STREAM_RESET_OUTGOING_SSN, nums, str_p,
47281054476SXin Long 		GFP_ATOMIC);
47381054476SXin Long 
47481054476SXin Long out:
475e4dc99c7SXin Long 	sctp_update_strreset_result(asoc, result);
476e4dc99c7SXin Long err:
47781054476SXin Long 	return sctp_make_strreset_resp(asoc, result, request_seq);
47881054476SXin Long }
47916e1a919SXin Long 
48016e1a919SXin Long struct sctp_chunk *sctp_process_strreset_inreq(
48116e1a919SXin Long 				struct sctp_association *asoc,
48216e1a919SXin Long 				union sctp_params param,
48316e1a919SXin Long 				struct sctp_ulpevent **evp)
48416e1a919SXin Long {
48516e1a919SXin Long 	struct sctp_strreset_inreq *inreq = param.v;
486cee360abSXin Long 	struct sctp_stream *stream = &asoc->stream;
48716e1a919SXin Long 	__u32 result = SCTP_STRRESET_DENIED;
48816e1a919SXin Long 	struct sctp_chunk *chunk = NULL;
48916e1a919SXin Long 	__u16 i, nums, *str_p;
49016e1a919SXin Long 	__u32 request_seq;
49116e1a919SXin Long 
49216e1a919SXin Long 	request_seq = ntohl(inreq->request_seq);
493d0f025e6SXin Long 	if (TSN_lt(asoc->strreset_inseq, request_seq) ||
494d0f025e6SXin Long 	    TSN_lt(request_seq, asoc->strreset_inseq - 2)) {
49516e1a919SXin Long 		result = SCTP_STRRESET_ERR_BAD_SEQNO;
496d0f025e6SXin Long 		goto err;
497d0f025e6SXin Long 	} else if (TSN_lt(request_seq, asoc->strreset_inseq)) {
498d0f025e6SXin Long 		i = asoc->strreset_inseq - request_seq - 1;
499d0f025e6SXin Long 		result = asoc->strreset_result[i];
500d0f025e6SXin Long 		if (result == SCTP_STRRESET_PERFORMED)
501d0f025e6SXin Long 			return NULL;
502d0f025e6SXin Long 		goto err;
50316e1a919SXin Long 	}
504d0f025e6SXin Long 	asoc->strreset_inseq++;
50516e1a919SXin Long 
50616e1a919SXin Long 	if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ))
50716e1a919SXin Long 		goto out;
50816e1a919SXin Long 
50916e1a919SXin Long 	if (asoc->strreset_outstanding) {
51016e1a919SXin Long 		result = SCTP_STRRESET_ERR_IN_PROGRESS;
51116e1a919SXin Long 		goto out;
51216e1a919SXin Long 	}
51316e1a919SXin Long 
51416e1a919SXin Long 	nums = (ntohs(param.p->length) - sizeof(*inreq)) / 2;
51516e1a919SXin Long 	str_p = inreq->list_of_streams;
51616e1a919SXin Long 	for (i = 0; i < nums; i++) {
51716e1a919SXin Long 		if (ntohs(str_p[i]) >= stream->outcnt) {
51816e1a919SXin Long 			result = SCTP_STRRESET_ERR_WRONG_SSN;
51916e1a919SXin Long 			goto out;
52016e1a919SXin Long 		}
52116e1a919SXin Long 	}
52216e1a919SXin Long 
52316e1a919SXin Long 	chunk = sctp_make_strreset_req(asoc, nums, str_p, 1, 0);
52416e1a919SXin Long 	if (!chunk)
52516e1a919SXin Long 		goto out;
52616e1a919SXin Long 
52716e1a919SXin Long 	if (nums)
52816e1a919SXin Long 		for (i = 0; i < nums; i++)
52916e1a919SXin Long 			stream->out[ntohs(str_p[i])].state =
53016e1a919SXin Long 					       SCTP_STREAM_CLOSED;
53116e1a919SXin Long 	else
53216e1a919SXin Long 		for (i = 0; i < stream->outcnt; i++)
53316e1a919SXin Long 			stream->out[i].state = SCTP_STREAM_CLOSED;
53416e1a919SXin Long 
53516e1a919SXin Long 	asoc->strreset_chunk = chunk;
53616e1a919SXin Long 	asoc->strreset_outstanding = 1;
53716e1a919SXin Long 	sctp_chunk_hold(asoc->strreset_chunk);
53816e1a919SXin Long 
539d0f025e6SXin Long 	result = SCTP_STRRESET_PERFORMED;
540d0f025e6SXin Long 
54116e1a919SXin Long 	*evp = sctp_ulpevent_make_stream_reset_event(asoc,
54216e1a919SXin Long 		SCTP_STREAM_RESET_INCOMING_SSN, nums, str_p, GFP_ATOMIC);
54316e1a919SXin Long 
54416e1a919SXin Long out:
545d0f025e6SXin Long 	sctp_update_strreset_result(asoc, result);
546d0f025e6SXin Long err:
54716e1a919SXin Long 	if (!chunk)
54816e1a919SXin Long 		chunk =  sctp_make_strreset_resp(asoc, result, request_seq);
54916e1a919SXin Long 
55016e1a919SXin Long 	return chunk;
55116e1a919SXin Long }
552692787ceSXin Long 
553692787ceSXin Long struct sctp_chunk *sctp_process_strreset_tsnreq(
554692787ceSXin Long 				struct sctp_association *asoc,
555692787ceSXin Long 				union sctp_params param,
556692787ceSXin Long 				struct sctp_ulpevent **evp)
557692787ceSXin Long {
558692787ceSXin Long 	__u32 init_tsn = 0, next_tsn = 0, max_tsn_seen;
559692787ceSXin Long 	struct sctp_strreset_tsnreq *tsnreq = param.v;
560cee360abSXin Long 	struct sctp_stream *stream = &asoc->stream;
561692787ceSXin Long 	__u32 result = SCTP_STRRESET_DENIED;
562692787ceSXin Long 	__u32 request_seq;
563692787ceSXin Long 	__u16 i;
564692787ceSXin Long 
565692787ceSXin Long 	request_seq = ntohl(tsnreq->request_seq);
5666c801387SXin Long 	if (TSN_lt(asoc->strreset_inseq, request_seq) ||
5676c801387SXin Long 	    TSN_lt(request_seq, asoc->strreset_inseq - 2)) {
568692787ceSXin Long 		result = SCTP_STRRESET_ERR_BAD_SEQNO;
5696c801387SXin Long 		goto err;
5706c801387SXin Long 	} else if (TSN_lt(request_seq, asoc->strreset_inseq)) {
5716c801387SXin Long 		i = asoc->strreset_inseq - request_seq - 1;
5726c801387SXin Long 		result = asoc->strreset_result[i];
5736c801387SXin Long 		if (result == SCTP_STRRESET_PERFORMED) {
5746c801387SXin Long 			next_tsn = asoc->next_tsn;
5756c801387SXin Long 			init_tsn =
5766c801387SXin Long 				sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map) + 1;
577692787ceSXin Long 		}
5786c801387SXin Long 		goto err;
5796c801387SXin Long 	}
5806c801387SXin Long 	asoc->strreset_inseq++;
581692787ceSXin Long 
582692787ceSXin Long 	if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_ASSOC_REQ))
583692787ceSXin Long 		goto out;
584692787ceSXin Long 
585692787ceSXin Long 	if (asoc->strreset_outstanding) {
586692787ceSXin Long 		result = SCTP_STRRESET_ERR_IN_PROGRESS;
587692787ceSXin Long 		goto out;
588692787ceSXin Long 	}
589692787ceSXin Long 
590692787ceSXin Long 	/* G3: The same processing as though a SACK chunk with no gap report
591692787ceSXin Long 	 *     and a cumulative TSN ACK of the Sender's Next TSN minus 1 were
592692787ceSXin Long 	 *     received MUST be performed.
593692787ceSXin Long 	 */
594692787ceSXin Long 	max_tsn_seen = sctp_tsnmap_get_max_tsn_seen(&asoc->peer.tsn_map);
595692787ceSXin Long 	sctp_ulpq_reasm_flushtsn(&asoc->ulpq, max_tsn_seen);
596692787ceSXin Long 	sctp_ulpq_abort_pd(&asoc->ulpq, GFP_ATOMIC);
597692787ceSXin Long 
598692787ceSXin Long 	/* G1: Compute an appropriate value for the Receiver's Next TSN -- the
599692787ceSXin Long 	 *     TSN that the peer should use to send the next DATA chunk.  The
600692787ceSXin Long 	 *     value SHOULD be the smallest TSN not acknowledged by the
601692787ceSXin Long 	 *     receiver of the request plus 2^31.
602692787ceSXin Long 	 */
603692787ceSXin Long 	init_tsn = sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map) + (1 << 31);
604692787ceSXin Long 	sctp_tsnmap_init(&asoc->peer.tsn_map, SCTP_TSN_MAP_INITIAL,
605692787ceSXin Long 			 init_tsn, GFP_ATOMIC);
606692787ceSXin Long 
607692787ceSXin Long 	/* G4: The same processing as though a FWD-TSN chunk (as defined in
608692787ceSXin Long 	 *     [RFC3758]) with all streams affected and a new cumulative TSN
609692787ceSXin Long 	 *     ACK of the Receiver's Next TSN minus 1 were received MUST be
610692787ceSXin Long 	 *     performed.
611692787ceSXin Long 	 */
612692787ceSXin Long 	sctp_outq_free(&asoc->outqueue);
613692787ceSXin Long 
614692787ceSXin Long 	/* G2: Compute an appropriate value for the local endpoint's next TSN,
615692787ceSXin Long 	 *     i.e., the next TSN assigned by the receiver of the SSN/TSN reset
616692787ceSXin Long 	 *     chunk.  The value SHOULD be the highest TSN sent by the receiver
617692787ceSXin Long 	 *     of the request plus 1.
618692787ceSXin Long 	 */
619692787ceSXin Long 	next_tsn = asoc->next_tsn;
620692787ceSXin Long 	asoc->ctsn_ack_point = next_tsn - 1;
621692787ceSXin Long 	asoc->adv_peer_ack_point = asoc->ctsn_ack_point;
622692787ceSXin Long 
623692787ceSXin Long 	/* G5:  The next expected and outgoing SSNs MUST be reset to 0 for all
624692787ceSXin Long 	 *      incoming and outgoing streams.
625692787ceSXin Long 	 */
626692787ceSXin Long 	for (i = 0; i < stream->outcnt; i++)
627692787ceSXin Long 		stream->out[i].ssn = 0;
628692787ceSXin Long 	for (i = 0; i < stream->incnt; i++)
629692787ceSXin Long 		stream->in[i].ssn = 0;
630692787ceSXin Long 
631692787ceSXin Long 	result = SCTP_STRRESET_PERFORMED;
632692787ceSXin Long 
633692787ceSXin Long 	*evp = sctp_ulpevent_make_assoc_reset_event(asoc, 0, init_tsn,
634692787ceSXin Long 						    next_tsn, GFP_ATOMIC);
635692787ceSXin Long 
636692787ceSXin Long out:
6376c801387SXin Long 	sctp_update_strreset_result(asoc, result);
6386c801387SXin Long err:
639692787ceSXin Long 	return sctp_make_strreset_tsnresp(asoc, result, request_seq,
640692787ceSXin Long 					  next_tsn, init_tsn);
641692787ceSXin Long }
64250a41591SXin Long 
64350a41591SXin Long struct sctp_chunk *sctp_process_strreset_addstrm_out(
64450a41591SXin Long 				struct sctp_association *asoc,
64550a41591SXin Long 				union sctp_params param,
64650a41591SXin Long 				struct sctp_ulpevent **evp)
64750a41591SXin Long {
64850a41591SXin Long 	struct sctp_strreset_addstrm *addstrm = param.v;
649cee360abSXin Long 	struct sctp_stream *stream = &asoc->stream;
65050a41591SXin Long 	__u32 result = SCTP_STRRESET_DENIED;
65150a41591SXin Long 	__u32 request_seq, incnt;
652e4dc99c7SXin Long 	__u16 in, i;
65350a41591SXin Long 
65450a41591SXin Long 	request_seq = ntohl(addstrm->request_seq);
655e4dc99c7SXin Long 	if (TSN_lt(asoc->strreset_inseq, request_seq) ||
656e4dc99c7SXin Long 	    TSN_lt(request_seq, asoc->strreset_inseq - 2)) {
65750a41591SXin Long 		result = SCTP_STRRESET_ERR_BAD_SEQNO;
658e4dc99c7SXin Long 		goto err;
659e4dc99c7SXin Long 	} else if (TSN_lt(request_seq, asoc->strreset_inseq)) {
660e4dc99c7SXin Long 		i = asoc->strreset_inseq - request_seq - 1;
661e4dc99c7SXin Long 		result = asoc->strreset_result[i];
662e4dc99c7SXin Long 		goto err;
66350a41591SXin Long 	}
664e4dc99c7SXin Long 	asoc->strreset_inseq++;
66550a41591SXin Long 
66650a41591SXin Long 	if (!(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ))
66750a41591SXin Long 		goto out;
66850a41591SXin Long 
66950a41591SXin Long 	if (asoc->strreset_chunk) {
67050a41591SXin Long 		if (!sctp_chunk_lookup_strreset_param(
67150a41591SXin Long 			asoc, 0, SCTP_PARAM_RESET_ADD_IN_STREAMS)) {
67250a41591SXin Long 			/* same process with outstanding isn't 0 */
67350a41591SXin Long 			result = SCTP_STRRESET_ERR_IN_PROGRESS;
67450a41591SXin Long 			goto out;
67550a41591SXin Long 		}
67650a41591SXin Long 
67750a41591SXin Long 		asoc->strreset_outstanding--;
67850a41591SXin Long 		asoc->strreset_outseq++;
67950a41591SXin Long 
68050a41591SXin Long 		if (!asoc->strreset_outstanding) {
68150a41591SXin Long 			struct sctp_transport *t;
68250a41591SXin Long 
68350a41591SXin Long 			t = asoc->strreset_chunk->transport;
68450a41591SXin Long 			if (del_timer(&t->reconf_timer))
68550a41591SXin Long 				sctp_transport_put(t);
68650a41591SXin Long 
68750a41591SXin Long 			sctp_chunk_put(asoc->strreset_chunk);
68850a41591SXin Long 			asoc->strreset_chunk = NULL;
68950a41591SXin Long 		}
69050a41591SXin Long 	}
69150a41591SXin Long 
69250a41591SXin Long 	in = ntohs(addstrm->number_of_streams);
69350a41591SXin Long 	incnt = stream->incnt + in;
69450a41591SXin Long 	if (!in || incnt > SCTP_MAX_STREAM)
69550a41591SXin Long 		goto out;
69650a41591SXin Long 
6971fdb8d8fSMarcelo Ricardo Leitner 	if (sctp_stream_alloc_in(stream, incnt, GFP_ATOMIC))
69850a41591SXin Long 		goto out;
69950a41591SXin Long 
70050a41591SXin Long 	stream->incnt = incnt;
70150a41591SXin Long 
70250a41591SXin Long 	result = SCTP_STRRESET_PERFORMED;
70350a41591SXin Long 
70450a41591SXin Long 	*evp = sctp_ulpevent_make_stream_change_event(asoc,
70550a41591SXin Long 		0, ntohs(addstrm->number_of_streams), 0, GFP_ATOMIC);
70650a41591SXin Long 
70750a41591SXin Long out:
708e4dc99c7SXin Long 	sctp_update_strreset_result(asoc, result);
709e4dc99c7SXin Long err:
71050a41591SXin Long 	return sctp_make_strreset_resp(asoc, result, request_seq);
71150a41591SXin Long }
712c5c4ebb3SXin Long 
713c5c4ebb3SXin Long struct sctp_chunk *sctp_process_strreset_addstrm_in(
714c5c4ebb3SXin Long 				struct sctp_association *asoc,
715c5c4ebb3SXin Long 				union sctp_params param,
716c5c4ebb3SXin Long 				struct sctp_ulpevent **evp)
717c5c4ebb3SXin Long {
718c5c4ebb3SXin Long 	struct sctp_strreset_addstrm *addstrm = param.v;
719cee360abSXin Long 	struct sctp_stream *stream = &asoc->stream;
720c5c4ebb3SXin Long 	__u32 result = SCTP_STRRESET_DENIED;
721c5c4ebb3SXin Long 	struct sctp_chunk *chunk = NULL;
722c5c4ebb3SXin Long 	__u32 request_seq, outcnt;
723d0f025e6SXin Long 	__u16 out, i;
724e090abd0SMarcelo Ricardo Leitner 	int ret;
725c5c4ebb3SXin Long 
726c5c4ebb3SXin Long 	request_seq = ntohl(addstrm->request_seq);
727d0f025e6SXin Long 	if (TSN_lt(asoc->strreset_inseq, request_seq) ||
728d0f025e6SXin Long 	    TSN_lt(request_seq, asoc->strreset_inseq - 2)) {
729c5c4ebb3SXin Long 		result = SCTP_STRRESET_ERR_BAD_SEQNO;
730d0f025e6SXin Long 		goto err;
731d0f025e6SXin Long 	} else if (TSN_lt(request_seq, asoc->strreset_inseq)) {
732d0f025e6SXin Long 		i = asoc->strreset_inseq - request_seq - 1;
733d0f025e6SXin Long 		result = asoc->strreset_result[i];
734d0f025e6SXin Long 		if (result == SCTP_STRRESET_PERFORMED)
735d0f025e6SXin Long 			return NULL;
736d0f025e6SXin Long 		goto err;
737c5c4ebb3SXin Long 	}
738d0f025e6SXin Long 	asoc->strreset_inseq++;
739c5c4ebb3SXin Long 
740c5c4ebb3SXin Long 	if (!(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ))
741c5c4ebb3SXin Long 		goto out;
742c5c4ebb3SXin Long 
743c5c4ebb3SXin Long 	if (asoc->strreset_outstanding) {
744c5c4ebb3SXin Long 		result = SCTP_STRRESET_ERR_IN_PROGRESS;
745c5c4ebb3SXin Long 		goto out;
746c5c4ebb3SXin Long 	}
747c5c4ebb3SXin Long 
748c5c4ebb3SXin Long 	out = ntohs(addstrm->number_of_streams);
749c5c4ebb3SXin Long 	outcnt = stream->outcnt + out;
750c5c4ebb3SXin Long 	if (!out || outcnt > SCTP_MAX_STREAM)
751c5c4ebb3SXin Long 		goto out;
752c5c4ebb3SXin Long 
753e090abd0SMarcelo Ricardo Leitner 	ret = sctp_stream_alloc_out(stream, outcnt, GFP_ATOMIC);
754e090abd0SMarcelo Ricardo Leitner 	if (ret)
755c5c4ebb3SXin Long 		goto out;
756c5c4ebb3SXin Long 
757c5c4ebb3SXin Long 	chunk = sctp_make_strreset_addstrm(asoc, out, 0);
758c5c4ebb3SXin Long 	if (!chunk)
759c5c4ebb3SXin Long 		goto out;
760c5c4ebb3SXin Long 
761c5c4ebb3SXin Long 	asoc->strreset_chunk = chunk;
762c5c4ebb3SXin Long 	asoc->strreset_outstanding = 1;
763c5c4ebb3SXin Long 	sctp_chunk_hold(asoc->strreset_chunk);
764c5c4ebb3SXin Long 
765c5c4ebb3SXin Long 	stream->outcnt = outcnt;
766c5c4ebb3SXin Long 
767d0f025e6SXin Long 	result = SCTP_STRRESET_PERFORMED;
768d0f025e6SXin Long 
769c5c4ebb3SXin Long 	*evp = sctp_ulpevent_make_stream_change_event(asoc,
770c5c4ebb3SXin Long 		0, 0, ntohs(addstrm->number_of_streams), GFP_ATOMIC);
771c5c4ebb3SXin Long 
772c5c4ebb3SXin Long out:
773d0f025e6SXin Long 	sctp_update_strreset_result(asoc, result);
774d0f025e6SXin Long err:
775c5c4ebb3SXin Long 	if (!chunk)
776c5c4ebb3SXin Long 		chunk = sctp_make_strreset_resp(asoc, result, request_seq);
777c5c4ebb3SXin Long 
778c5c4ebb3SXin Long 	return chunk;
779c5c4ebb3SXin Long }
78011ae76e6SXin Long 
78111ae76e6SXin Long struct sctp_chunk *sctp_process_strreset_resp(
78211ae76e6SXin Long 				struct sctp_association *asoc,
78311ae76e6SXin Long 				union sctp_params param,
78411ae76e6SXin Long 				struct sctp_ulpevent **evp)
78511ae76e6SXin Long {
786cee360abSXin Long 	struct sctp_stream *stream = &asoc->stream;
78711ae76e6SXin Long 	struct sctp_strreset_resp *resp = param.v;
78811ae76e6SXin Long 	struct sctp_transport *t;
78911ae76e6SXin Long 	__u16 i, nums, flags = 0;
7903c918704SXin Long 	struct sctp_paramhdr *req;
79111ae76e6SXin Long 	__u32 result;
79211ae76e6SXin Long 
79311ae76e6SXin Long 	req = sctp_chunk_lookup_strreset_param(asoc, resp->response_seq, 0);
79411ae76e6SXin Long 	if (!req)
79511ae76e6SXin Long 		return NULL;
79611ae76e6SXin Long 
79711ae76e6SXin Long 	result = ntohl(resp->result);
79811ae76e6SXin Long 	if (result != SCTP_STRRESET_PERFORMED) {
79911ae76e6SXin Long 		/* if in progress, do nothing but retransmit */
80011ae76e6SXin Long 		if (result == SCTP_STRRESET_IN_PROGRESS)
80111ae76e6SXin Long 			return NULL;
80211ae76e6SXin Long 		else if (result == SCTP_STRRESET_DENIED)
80311ae76e6SXin Long 			flags = SCTP_STREAM_RESET_DENIED;
80411ae76e6SXin Long 		else
80511ae76e6SXin Long 			flags = SCTP_STREAM_RESET_FAILED;
80611ae76e6SXin Long 	}
80711ae76e6SXin Long 
80811ae76e6SXin Long 	if (req->type == SCTP_PARAM_RESET_OUT_REQUEST) {
80911ae76e6SXin Long 		struct sctp_strreset_outreq *outreq;
810edb12f2dSXin Long 		__u16 *str_p;
81111ae76e6SXin Long 
81211ae76e6SXin Long 		outreq = (struct sctp_strreset_outreq *)req;
813edb12f2dSXin Long 		str_p = outreq->list_of_streams;
81411ae76e6SXin Long 		nums = (ntohs(outreq->param_hdr.length) - sizeof(*outreq)) / 2;
81511ae76e6SXin Long 
81611ae76e6SXin Long 		if (result == SCTP_STRRESET_PERFORMED) {
81711ae76e6SXin Long 			if (nums) {
81811ae76e6SXin Long 				for (i = 0; i < nums; i++)
81911ae76e6SXin Long 					stream->out[ntohs(str_p[i])].ssn = 0;
82011ae76e6SXin Long 			} else {
82111ae76e6SXin Long 				for (i = 0; i < stream->outcnt; i++)
82211ae76e6SXin Long 					stream->out[i].ssn = 0;
82311ae76e6SXin Long 			}
82411ae76e6SXin Long 
82511ae76e6SXin Long 			flags = SCTP_STREAM_RESET_OUTGOING_SSN;
82611ae76e6SXin Long 		}
82711ae76e6SXin Long 
82811ae76e6SXin Long 		for (i = 0; i < stream->outcnt; i++)
82911ae76e6SXin Long 			stream->out[i].state = SCTP_STREAM_OPEN;
83011ae76e6SXin Long 
83111ae76e6SXin Long 		*evp = sctp_ulpevent_make_stream_reset_event(asoc, flags,
83211ae76e6SXin Long 			nums, str_p, GFP_ATOMIC);
83311ae76e6SXin Long 	} else if (req->type == SCTP_PARAM_RESET_IN_REQUEST) {
83411ae76e6SXin Long 		struct sctp_strreset_inreq *inreq;
835edb12f2dSXin Long 		__u16 *str_p;
83611ae76e6SXin Long 
83711ae76e6SXin Long 		/* if the result is performed, it's impossible for inreq */
83811ae76e6SXin Long 		if (result == SCTP_STRRESET_PERFORMED)
83911ae76e6SXin Long 			return NULL;
84011ae76e6SXin Long 
84111ae76e6SXin Long 		inreq = (struct sctp_strreset_inreq *)req;
842edb12f2dSXin Long 		str_p = inreq->list_of_streams;
84311ae76e6SXin Long 		nums = (ntohs(inreq->param_hdr.length) - sizeof(*inreq)) / 2;
84411ae76e6SXin Long 
84511ae76e6SXin Long 		*evp = sctp_ulpevent_make_stream_reset_event(asoc, flags,
84611ae76e6SXin Long 			nums, str_p, GFP_ATOMIC);
84711ae76e6SXin Long 	} else if (req->type == SCTP_PARAM_RESET_TSN_REQUEST) {
84811ae76e6SXin Long 		struct sctp_strreset_resptsn *resptsn;
84911ae76e6SXin Long 		__u32 stsn, rtsn;
85011ae76e6SXin Long 
85111ae76e6SXin Long 		/* check for resptsn, as sctp_verify_reconf didn't do it*/
85211ae76e6SXin Long 		if (ntohs(param.p->length) != sizeof(*resptsn))
85311ae76e6SXin Long 			return NULL;
85411ae76e6SXin Long 
85511ae76e6SXin Long 		resptsn = (struct sctp_strreset_resptsn *)resp;
85611ae76e6SXin Long 		stsn = ntohl(resptsn->senders_next_tsn);
85711ae76e6SXin Long 		rtsn = ntohl(resptsn->receivers_next_tsn);
85811ae76e6SXin Long 
85911ae76e6SXin Long 		if (result == SCTP_STRRESET_PERFORMED) {
86011ae76e6SXin Long 			__u32 mtsn = sctp_tsnmap_get_max_tsn_seen(
86111ae76e6SXin Long 						&asoc->peer.tsn_map);
86211ae76e6SXin Long 
86311ae76e6SXin Long 			sctp_ulpq_reasm_flushtsn(&asoc->ulpq, mtsn);
86411ae76e6SXin Long 			sctp_ulpq_abort_pd(&asoc->ulpq, GFP_ATOMIC);
86511ae76e6SXin Long 
86611ae76e6SXin Long 			sctp_tsnmap_init(&asoc->peer.tsn_map,
86711ae76e6SXin Long 					 SCTP_TSN_MAP_INITIAL,
86811ae76e6SXin Long 					 stsn, GFP_ATOMIC);
86911ae76e6SXin Long 
87011ae76e6SXin Long 			sctp_outq_free(&asoc->outqueue);
87111ae76e6SXin Long 
87211ae76e6SXin Long 			asoc->next_tsn = rtsn;
87311ae76e6SXin Long 			asoc->ctsn_ack_point = asoc->next_tsn - 1;
87411ae76e6SXin Long 			asoc->adv_peer_ack_point = asoc->ctsn_ack_point;
87511ae76e6SXin Long 
87611ae76e6SXin Long 			for (i = 0; i < stream->outcnt; i++)
87711ae76e6SXin Long 				stream->out[i].ssn = 0;
87811ae76e6SXin Long 			for (i = 0; i < stream->incnt; i++)
87911ae76e6SXin Long 				stream->in[i].ssn = 0;
88011ae76e6SXin Long 		}
88111ae76e6SXin Long 
88211ae76e6SXin Long 		for (i = 0; i < stream->outcnt; i++)
88311ae76e6SXin Long 			stream->out[i].state = SCTP_STREAM_OPEN;
88411ae76e6SXin Long 
88511ae76e6SXin Long 		*evp = sctp_ulpevent_make_assoc_reset_event(asoc, flags,
88611ae76e6SXin Long 			stsn, rtsn, GFP_ATOMIC);
88711ae76e6SXin Long 	} else if (req->type == SCTP_PARAM_RESET_ADD_OUT_STREAMS) {
88811ae76e6SXin Long 		struct sctp_strreset_addstrm *addstrm;
88911ae76e6SXin Long 		__u16 number;
89011ae76e6SXin Long 
89111ae76e6SXin Long 		addstrm = (struct sctp_strreset_addstrm *)req;
89211ae76e6SXin Long 		nums = ntohs(addstrm->number_of_streams);
89311ae76e6SXin Long 		number = stream->outcnt - nums;
89411ae76e6SXin Long 
89511ae76e6SXin Long 		if (result == SCTP_STRRESET_PERFORMED)
89611ae76e6SXin Long 			for (i = number; i < stream->outcnt; i++)
89711ae76e6SXin Long 				stream->out[i].state = SCTP_STREAM_OPEN;
89811ae76e6SXin Long 		else
89911ae76e6SXin Long 			stream->outcnt = number;
90011ae76e6SXin Long 
90111ae76e6SXin Long 		*evp = sctp_ulpevent_make_stream_change_event(asoc, flags,
90211ae76e6SXin Long 			0, nums, GFP_ATOMIC);
90311ae76e6SXin Long 	} else if (req->type == SCTP_PARAM_RESET_ADD_IN_STREAMS) {
90411ae76e6SXin Long 		struct sctp_strreset_addstrm *addstrm;
90511ae76e6SXin Long 
90611ae76e6SXin Long 		/* if the result is performed, it's impossible for addstrm in
90711ae76e6SXin Long 		 * request.
90811ae76e6SXin Long 		 */
90911ae76e6SXin Long 		if (result == SCTP_STRRESET_PERFORMED)
91011ae76e6SXin Long 			return NULL;
91111ae76e6SXin Long 
91211ae76e6SXin Long 		addstrm = (struct sctp_strreset_addstrm *)req;
91311ae76e6SXin Long 		nums = ntohs(addstrm->number_of_streams);
91411ae76e6SXin Long 
91511ae76e6SXin Long 		*evp = sctp_ulpevent_make_stream_change_event(asoc, flags,
91611ae76e6SXin Long 			nums, 0, GFP_ATOMIC);
91711ae76e6SXin Long 	}
91811ae76e6SXin Long 
91911ae76e6SXin Long 	asoc->strreset_outstanding--;
92011ae76e6SXin Long 	asoc->strreset_outseq++;
92111ae76e6SXin Long 
92211ae76e6SXin Long 	/* remove everything for this reconf request */
92311ae76e6SXin Long 	if (!asoc->strreset_outstanding) {
92411ae76e6SXin Long 		t = asoc->strreset_chunk->transport;
92511ae76e6SXin Long 		if (del_timer(&t->reconf_timer))
92611ae76e6SXin Long 			sctp_transport_put(t);
92711ae76e6SXin Long 
92811ae76e6SXin Long 		sctp_chunk_put(asoc->strreset_chunk);
92911ae76e6SXin Long 		asoc->strreset_chunk = NULL;
93011ae76e6SXin Long 	}
93111ae76e6SXin Long 
93211ae76e6SXin Long 	return NULL;
93311ae76e6SXin Long }
934