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 * 9fae8b6f4SXin Long * This file contains sctp stream maniuplation primitives and helpers. 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 355bbbbe32SMarcelo Ricardo Leitner #include <linux/list.h> 36a8386317SXin Long #include <net/sctp/sctp.h> 377f9d68acSXin Long #include <net/sctp/sm.h> 385bbbbe32SMarcelo Ricardo Leitner #include <net/sctp/stream_sched.h> 395bbbbe32SMarcelo Ricardo Leitner 405bbbbe32SMarcelo Ricardo Leitner /* Migrates chunks from stream queues to new stream queues if needed, 415bbbbe32SMarcelo Ricardo Leitner * but not across associations. Also, removes those chunks to streams 425bbbbe32SMarcelo Ricardo Leitner * higher than the new max. 435bbbbe32SMarcelo Ricardo Leitner */ 445bbbbe32SMarcelo Ricardo Leitner static void sctp_stream_outq_migrate(struct sctp_stream *stream, 455bbbbe32SMarcelo Ricardo Leitner struct sctp_stream *new, __u16 outcnt) 465bbbbe32SMarcelo Ricardo Leitner { 475bbbbe32SMarcelo Ricardo Leitner struct sctp_association *asoc; 485bbbbe32SMarcelo Ricardo Leitner struct sctp_chunk *ch, *temp; 495bbbbe32SMarcelo Ricardo Leitner struct sctp_outq *outq; 505bbbbe32SMarcelo Ricardo Leitner int i; 515bbbbe32SMarcelo Ricardo Leitner 525bbbbe32SMarcelo Ricardo Leitner asoc = container_of(stream, struct sctp_association, stream); 535bbbbe32SMarcelo Ricardo Leitner outq = &asoc->outqueue; 545bbbbe32SMarcelo Ricardo Leitner 555bbbbe32SMarcelo Ricardo Leitner list_for_each_entry_safe(ch, temp, &outq->out_chunk_list, list) { 565bbbbe32SMarcelo Ricardo Leitner __u16 sid = sctp_chunk_stream_no(ch); 575bbbbe32SMarcelo Ricardo Leitner 585bbbbe32SMarcelo Ricardo Leitner if (sid < outcnt) 595bbbbe32SMarcelo Ricardo Leitner continue; 605bbbbe32SMarcelo Ricardo Leitner 615bbbbe32SMarcelo Ricardo Leitner sctp_sched_dequeue_common(outq, ch); 625bbbbe32SMarcelo Ricardo Leitner /* No need to call dequeue_done here because 635bbbbe32SMarcelo Ricardo Leitner * the chunks are not scheduled by now. 645bbbbe32SMarcelo Ricardo Leitner */ 655bbbbe32SMarcelo Ricardo Leitner 665bbbbe32SMarcelo Ricardo Leitner /* Mark as failed send. */ 6708f46070SXin Long sctp_chunk_fail(ch, (__force __u32)SCTP_ERROR_INV_STRM); 685bbbbe32SMarcelo Ricardo Leitner if (asoc->peer.prsctp_capable && 695bbbbe32SMarcelo Ricardo Leitner SCTP_PR_PRIO_ENABLED(ch->sinfo.sinfo_flags)) 705bbbbe32SMarcelo Ricardo Leitner asoc->sent_cnt_removable--; 715bbbbe32SMarcelo Ricardo Leitner 725bbbbe32SMarcelo Ricardo Leitner sctp_chunk_free(ch); 735bbbbe32SMarcelo Ricardo Leitner } 745bbbbe32SMarcelo Ricardo Leitner 755bbbbe32SMarcelo Ricardo Leitner if (new) { 765bbbbe32SMarcelo Ricardo Leitner /* Here we actually move the old ext stuff into the new 775bbbbe32SMarcelo Ricardo Leitner * buffer, because we want to keep it. Then 785bbbbe32SMarcelo Ricardo Leitner * sctp_stream_update will swap ->out pointers. 795bbbbe32SMarcelo Ricardo Leitner */ 805bbbbe32SMarcelo Ricardo Leitner for (i = 0; i < outcnt; i++) { 815bbbbe32SMarcelo Ricardo Leitner kfree(new->out[i].ext); 825bbbbe32SMarcelo Ricardo Leitner new->out[i].ext = stream->out[i].ext; 835bbbbe32SMarcelo Ricardo Leitner stream->out[i].ext = NULL; 845bbbbe32SMarcelo Ricardo Leitner } 855bbbbe32SMarcelo Ricardo Leitner } 865bbbbe32SMarcelo Ricardo Leitner 875bbbbe32SMarcelo Ricardo Leitner for (i = outcnt; i < stream->outcnt; i++) 885bbbbe32SMarcelo Ricardo Leitner kfree(stream->out[i].ext); 895bbbbe32SMarcelo Ricardo Leitner } 90a8386317SXin Long 91e090abd0SMarcelo Ricardo Leitner static int sctp_stream_alloc_out(struct sctp_stream *stream, __u16 outcnt, 92e090abd0SMarcelo Ricardo Leitner gfp_t gfp) 93e090abd0SMarcelo Ricardo Leitner { 94e090abd0SMarcelo Ricardo Leitner struct sctp_stream_out *out; 95e090abd0SMarcelo Ricardo Leitner 96e090abd0SMarcelo Ricardo Leitner out = kmalloc_array(outcnt, sizeof(*out), gfp); 97e090abd0SMarcelo Ricardo Leitner if (!out) 98e090abd0SMarcelo Ricardo Leitner return -ENOMEM; 99e090abd0SMarcelo Ricardo Leitner 100e090abd0SMarcelo Ricardo Leitner if (stream->out) { 101e090abd0SMarcelo Ricardo Leitner memcpy(out, stream->out, min(outcnt, stream->outcnt) * 102e090abd0SMarcelo Ricardo Leitner sizeof(*out)); 103e090abd0SMarcelo Ricardo Leitner kfree(stream->out); 104e090abd0SMarcelo Ricardo Leitner } 105e090abd0SMarcelo Ricardo Leitner 106e090abd0SMarcelo Ricardo Leitner if (outcnt > stream->outcnt) 107e090abd0SMarcelo Ricardo Leitner memset(out + stream->outcnt, 0, 108e090abd0SMarcelo Ricardo Leitner (outcnt - stream->outcnt) * sizeof(*out)); 109e090abd0SMarcelo Ricardo Leitner 110e090abd0SMarcelo Ricardo Leitner stream->out = out; 111e090abd0SMarcelo Ricardo Leitner 112e090abd0SMarcelo Ricardo Leitner return 0; 113e090abd0SMarcelo Ricardo Leitner } 114e090abd0SMarcelo Ricardo Leitner 1151fdb8d8fSMarcelo Ricardo Leitner static int sctp_stream_alloc_in(struct sctp_stream *stream, __u16 incnt, 1161fdb8d8fSMarcelo Ricardo Leitner gfp_t gfp) 1171fdb8d8fSMarcelo Ricardo Leitner { 1181fdb8d8fSMarcelo Ricardo Leitner struct sctp_stream_in *in; 1191fdb8d8fSMarcelo Ricardo Leitner 1201fdb8d8fSMarcelo Ricardo Leitner in = kmalloc_array(incnt, sizeof(*stream->in), gfp); 1211fdb8d8fSMarcelo Ricardo Leitner 1221fdb8d8fSMarcelo Ricardo Leitner if (!in) 1231fdb8d8fSMarcelo Ricardo Leitner return -ENOMEM; 1241fdb8d8fSMarcelo Ricardo Leitner 1251fdb8d8fSMarcelo Ricardo Leitner if (stream->in) { 1261fdb8d8fSMarcelo Ricardo Leitner memcpy(in, stream->in, min(incnt, stream->incnt) * 1271fdb8d8fSMarcelo Ricardo Leitner sizeof(*in)); 1281fdb8d8fSMarcelo Ricardo Leitner kfree(stream->in); 1291fdb8d8fSMarcelo Ricardo Leitner } 1301fdb8d8fSMarcelo Ricardo Leitner 1311fdb8d8fSMarcelo Ricardo Leitner if (incnt > stream->incnt) 1321fdb8d8fSMarcelo Ricardo Leitner memset(in + stream->incnt, 0, 1331fdb8d8fSMarcelo Ricardo Leitner (incnt - stream->incnt) * sizeof(*in)); 1341fdb8d8fSMarcelo Ricardo Leitner 1351fdb8d8fSMarcelo Ricardo Leitner stream->in = in; 1361fdb8d8fSMarcelo Ricardo Leitner 1371fdb8d8fSMarcelo Ricardo Leitner return 0; 1381fdb8d8fSMarcelo Ricardo Leitner } 1391fdb8d8fSMarcelo Ricardo Leitner 140ff356414SXin Long int sctp_stream_init(struct sctp_stream *stream, __u16 outcnt, __u16 incnt, 141ff356414SXin Long gfp_t gfp) 142a8386317SXin Long { 1435bbbbe32SMarcelo Ricardo Leitner struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream); 1445bbbbe32SMarcelo Ricardo Leitner int i, ret = 0; 1453dbcc105SXin Long 1461ae2eaaaSMarcelo Ricardo Leitner gfp |= __GFP_NOWARN; 1471ae2eaaaSMarcelo Ricardo Leitner 1483dbcc105SXin Long /* Initial stream->out size may be very big, so free it and alloc 1491ae2eaaaSMarcelo Ricardo Leitner * a new one with new outcnt to save memory if needed. 1503dbcc105SXin Long */ 1511ae2eaaaSMarcelo Ricardo Leitner if (outcnt == stream->outcnt) 1521ae2eaaaSMarcelo Ricardo Leitner goto in; 1531ae2eaaaSMarcelo Ricardo Leitner 1545bbbbe32SMarcelo Ricardo Leitner /* Filter out chunks queued on streams that won't exist anymore */ 1555bbbbe32SMarcelo Ricardo Leitner sched->unsched_all(stream); 1565bbbbe32SMarcelo Ricardo Leitner sctp_stream_outq_migrate(stream, NULL, outcnt); 1575bbbbe32SMarcelo Ricardo Leitner sched->sched_all(stream); 1585bbbbe32SMarcelo Ricardo Leitner 15979d08951SMarcelo Ricardo Leitner ret = sctp_stream_alloc_out(stream, outcnt, gfp); 16079d08951SMarcelo Ricardo Leitner if (ret) 16179d08951SMarcelo Ricardo Leitner goto out; 1623dbcc105SXin Long 163ff356414SXin Long stream->outcnt = outcnt; 1643dbcc105SXin Long for (i = 0; i < stream->outcnt; i++) 1653dbcc105SXin Long stream->out[i].state = SCTP_STREAM_OPEN; 1663dbcc105SXin Long 1675bbbbe32SMarcelo Ricardo Leitner sched->init(stream); 1685bbbbe32SMarcelo Ricardo Leitner 1691ae2eaaaSMarcelo Ricardo Leitner in: 1700c3f6f65SXin Long sctp_stream_interleave_init(stream); 171ff356414SXin Long if (!incnt) 1725bbbbe32SMarcelo Ricardo Leitner goto out; 173ff356414SXin Long 17479d08951SMarcelo Ricardo Leitner ret = sctp_stream_alloc_in(stream, incnt, gfp); 17579d08951SMarcelo Ricardo Leitner if (ret) { 1765bbbbe32SMarcelo Ricardo Leitner sched->free(stream); 1775bbbbe32SMarcelo Ricardo Leitner kfree(stream->out); 1785bbbbe32SMarcelo Ricardo Leitner stream->out = NULL; 17979d08951SMarcelo Ricardo Leitner stream->outcnt = 0; 18079d08951SMarcelo Ricardo Leitner goto out; 18179d08951SMarcelo Ricardo Leitner } 18279d08951SMarcelo Ricardo Leitner 18379d08951SMarcelo Ricardo Leitner stream->incnt = incnt; 18479d08951SMarcelo Ricardo Leitner 1855bbbbe32SMarcelo Ricardo Leitner out: 1865bbbbe32SMarcelo Ricardo Leitner return ret; 187a8386317SXin Long } 188a8386317SXin Long 189f952be79SMarcelo Ricardo Leitner int sctp_stream_init_ext(struct sctp_stream *stream, __u16 sid) 190f952be79SMarcelo Ricardo Leitner { 191f952be79SMarcelo Ricardo Leitner struct sctp_stream_out_ext *soute; 192f952be79SMarcelo Ricardo Leitner 193f952be79SMarcelo Ricardo Leitner soute = kzalloc(sizeof(*soute), GFP_KERNEL); 194f952be79SMarcelo Ricardo Leitner if (!soute) 195f952be79SMarcelo Ricardo Leitner return -ENOMEM; 196f952be79SMarcelo Ricardo Leitner stream->out[sid].ext = soute; 197f952be79SMarcelo Ricardo Leitner 1985bbbbe32SMarcelo Ricardo Leitner return sctp_sched_init_sid(stream, sid, GFP_KERNEL); 199f952be79SMarcelo Ricardo Leitner } 200f952be79SMarcelo Ricardo Leitner 201a8386317SXin Long void sctp_stream_free(struct sctp_stream *stream) 202a8386317SXin Long { 2035bbbbe32SMarcelo Ricardo Leitner struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream); 204f952be79SMarcelo Ricardo Leitner int i; 205f952be79SMarcelo Ricardo Leitner 2065bbbbe32SMarcelo Ricardo Leitner sched->free(stream); 207f952be79SMarcelo Ricardo Leitner for (i = 0; i < stream->outcnt; i++) 208f952be79SMarcelo Ricardo Leitner kfree(stream->out[i].ext); 209a8386317SXin Long kfree(stream->out); 210a8386317SXin Long kfree(stream->in); 211a8386317SXin Long } 212a8386317SXin Long 213a8386317SXin Long void sctp_stream_clear(struct sctp_stream *stream) 214a8386317SXin Long { 215a8386317SXin Long int i; 216a8386317SXin Long 217107e2425SXin Long for (i = 0; i < stream->outcnt; i++) { 218107e2425SXin Long stream->out[i].mid = 0; 219107e2425SXin Long stream->out[i].mid_uo = 0; 220107e2425SXin Long } 221a8386317SXin Long 222a8386317SXin Long for (i = 0; i < stream->incnt; i++) 223107e2425SXin Long stream->in[i].mid = 0; 224a8386317SXin Long } 2257f9d68acSXin Long 226cee360abSXin Long void sctp_stream_update(struct sctp_stream *stream, struct sctp_stream *new) 227cee360abSXin Long { 2285bbbbe32SMarcelo Ricardo Leitner struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream); 2295bbbbe32SMarcelo Ricardo Leitner 2305bbbbe32SMarcelo Ricardo Leitner sched->unsched_all(stream); 2315bbbbe32SMarcelo Ricardo Leitner sctp_stream_outq_migrate(stream, new, new->outcnt); 232cee360abSXin Long sctp_stream_free(stream); 233cee360abSXin Long 234cee360abSXin Long stream->out = new->out; 235cee360abSXin Long stream->in = new->in; 236cee360abSXin Long stream->outcnt = new->outcnt; 237cee360abSXin Long stream->incnt = new->incnt; 238cee360abSXin Long 2395bbbbe32SMarcelo Ricardo Leitner sched->sched_all(stream); 2405bbbbe32SMarcelo Ricardo Leitner 241cee360abSXin Long new->out = NULL; 242cee360abSXin Long new->in = NULL; 2436a9a27d5SXin Long new->outcnt = 0; 2446a9a27d5SXin Long new->incnt = 0; 245cee360abSXin Long } 246cee360abSXin Long 2477f9d68acSXin Long static int sctp_send_reconf(struct sctp_association *asoc, 2487f9d68acSXin Long struct sctp_chunk *chunk) 2497f9d68acSXin Long { 2507f9d68acSXin Long struct net *net = sock_net(asoc->base.sk); 2517f9d68acSXin Long int retval = 0; 2527f9d68acSXin Long 2537f9d68acSXin Long retval = sctp_primitive_RECONF(net, asoc, chunk); 2547f9d68acSXin Long if (retval) 2557f9d68acSXin Long sctp_chunk_free(chunk); 2567f9d68acSXin Long 2577f9d68acSXin Long return retval; 2587f9d68acSXin Long } 2597f9d68acSXin Long 260d570a59cSXin Long static bool sctp_stream_outq_is_empty(struct sctp_stream *stream, 261d570a59cSXin Long __u16 str_nums, __be16 *str_list) 262d570a59cSXin Long { 263d570a59cSXin Long struct sctp_association *asoc; 264d570a59cSXin Long __u16 i; 265d570a59cSXin Long 266d570a59cSXin Long asoc = container_of(stream, struct sctp_association, stream); 267d570a59cSXin Long if (!asoc->outqueue.out_qlen) 268d570a59cSXin Long return true; 269d570a59cSXin Long 270d570a59cSXin Long if (!str_nums) 271d570a59cSXin Long return false; 272d570a59cSXin Long 273d570a59cSXin Long for (i = 0; i < str_nums; i++) { 274d570a59cSXin Long __u16 sid = ntohs(str_list[i]); 275d570a59cSXin Long 276d570a59cSXin Long if (stream->out[sid].ext && 277d570a59cSXin Long !list_empty(&stream->out[sid].ext->outq)) 278d570a59cSXin Long return false; 279d570a59cSXin Long } 280d570a59cSXin Long 281d570a59cSXin Long return true; 282d570a59cSXin Long } 283d570a59cSXin Long 2847f9d68acSXin Long int sctp_send_reset_streams(struct sctp_association *asoc, 2857f9d68acSXin Long struct sctp_reset_streams *params) 2867f9d68acSXin Long { 287cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 2887f9d68acSXin Long __u16 i, str_nums, *str_list; 2897f9d68acSXin Long struct sctp_chunk *chunk; 2907f9d68acSXin Long int retval = -EINVAL; 2911da4fc97SXin Long __be16 *nstr_list; 2927f9d68acSXin Long bool out, in; 2937f9d68acSXin Long 2947f9d68acSXin Long if (!asoc->peer.reconf_capable || 2957f9d68acSXin Long !(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ)) { 2967f9d68acSXin Long retval = -ENOPROTOOPT; 2977f9d68acSXin Long goto out; 2987f9d68acSXin Long } 2997f9d68acSXin Long 3007f9d68acSXin Long if (asoc->strreset_outstanding) { 3017f9d68acSXin Long retval = -EINPROGRESS; 3027f9d68acSXin Long goto out; 3037f9d68acSXin Long } 3047f9d68acSXin Long 3057f9d68acSXin Long out = params->srs_flags & SCTP_STREAM_RESET_OUTGOING; 3067f9d68acSXin Long in = params->srs_flags & SCTP_STREAM_RESET_INCOMING; 3077f9d68acSXin Long if (!out && !in) 3087f9d68acSXin Long goto out; 3097f9d68acSXin Long 3107f9d68acSXin Long str_nums = params->srs_number_streams; 3117f9d68acSXin Long str_list = params->srs_stream_list; 312423852f8SXin Long if (str_nums) { 313423852f8SXin Long int param_len = 0; 314423852f8SXin Long 315423852f8SXin Long if (out) { 3167f9d68acSXin Long for (i = 0; i < str_nums; i++) 3177f9d68acSXin Long if (str_list[i] >= stream->outcnt) 3187f9d68acSXin Long goto out; 3197f9d68acSXin Long 320423852f8SXin Long param_len = str_nums * sizeof(__u16) + 321423852f8SXin Long sizeof(struct sctp_strreset_outreq); 322423852f8SXin Long } 323423852f8SXin Long 324423852f8SXin Long if (in) { 3257f9d68acSXin Long for (i = 0; i < str_nums; i++) 3267f9d68acSXin Long if (str_list[i] >= stream->incnt) 3277f9d68acSXin Long goto out; 3287f9d68acSXin Long 329423852f8SXin Long param_len += str_nums * sizeof(__u16) + 330423852f8SXin Long sizeof(struct sctp_strreset_inreq); 331423852f8SXin Long } 332423852f8SXin Long 333423852f8SXin Long if (param_len > SCTP_MAX_CHUNK_LEN - 334423852f8SXin Long sizeof(struct sctp_reconf_chunk)) 335423852f8SXin Long goto out; 336423852f8SXin Long } 337423852f8SXin Long 3381da4fc97SXin Long nstr_list = kcalloc(str_nums, sizeof(__be16), GFP_KERNEL); 3391da4fc97SXin Long if (!nstr_list) { 3401da4fc97SXin Long retval = -ENOMEM; 3411da4fc97SXin Long goto out; 3421da4fc97SXin Long } 34316e1a919SXin Long 34416e1a919SXin Long for (i = 0; i < str_nums; i++) 3451da4fc97SXin Long nstr_list[i] = htons(str_list[i]); 3461da4fc97SXin Long 347d570a59cSXin Long if (out && !sctp_stream_outq_is_empty(stream, str_nums, nstr_list)) { 348d570a59cSXin Long retval = -EAGAIN; 349d570a59cSXin Long goto out; 350d570a59cSXin Long } 351d570a59cSXin Long 3521da4fc97SXin Long chunk = sctp_make_strreset_req(asoc, str_nums, nstr_list, out, in); 3531da4fc97SXin Long 3541da4fc97SXin Long kfree(nstr_list); 35516e1a919SXin Long 356119aecbaSXin Long if (!chunk) { 357119aecbaSXin Long retval = -ENOMEM; 3587f9d68acSXin Long goto out; 359119aecbaSXin Long } 3607f9d68acSXin Long 3617f9d68acSXin Long if (out) { 3627f9d68acSXin Long if (str_nums) 3637f9d68acSXin Long for (i = 0; i < str_nums; i++) 3647f9d68acSXin Long stream->out[str_list[i]].state = 3657f9d68acSXin Long SCTP_STREAM_CLOSED; 3667f9d68acSXin Long else 3677f9d68acSXin Long for (i = 0; i < stream->outcnt; i++) 3687f9d68acSXin Long stream->out[i].state = SCTP_STREAM_CLOSED; 3697f9d68acSXin Long } 3707f9d68acSXin Long 3717f9d68acSXin Long asoc->strreset_chunk = chunk; 3727f9d68acSXin Long sctp_chunk_hold(asoc->strreset_chunk); 3737f9d68acSXin Long 3747f9d68acSXin Long retval = sctp_send_reconf(asoc, chunk); 3757f9d68acSXin Long if (retval) { 3767f9d68acSXin Long sctp_chunk_put(asoc->strreset_chunk); 3777f9d68acSXin Long asoc->strreset_chunk = NULL; 378119aecbaSXin Long if (!out) 379119aecbaSXin Long goto out; 380119aecbaSXin Long 381119aecbaSXin Long if (str_nums) 382119aecbaSXin Long for (i = 0; i < str_nums; i++) 383119aecbaSXin Long stream->out[str_list[i]].state = 384119aecbaSXin Long SCTP_STREAM_OPEN; 385119aecbaSXin Long else 386119aecbaSXin Long for (i = 0; i < stream->outcnt; i++) 387119aecbaSXin Long stream->out[i].state = SCTP_STREAM_OPEN; 388119aecbaSXin Long 389119aecbaSXin Long goto out; 3907f9d68acSXin Long } 3917f9d68acSXin Long 392119aecbaSXin Long asoc->strreset_outstanding = out + in; 393119aecbaSXin Long 3947f9d68acSXin Long out: 3957f9d68acSXin Long return retval; 3967f9d68acSXin Long } 397a92ce1a4SXin Long 398a92ce1a4SXin Long int sctp_send_reset_assoc(struct sctp_association *asoc) 399a92ce1a4SXin Long { 400cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 401a92ce1a4SXin Long struct sctp_chunk *chunk = NULL; 402a92ce1a4SXin Long int retval; 403a92ce1a4SXin Long __u16 i; 404a92ce1a4SXin Long 405a92ce1a4SXin Long if (!asoc->peer.reconf_capable || 406a92ce1a4SXin Long !(asoc->strreset_enable & SCTP_ENABLE_RESET_ASSOC_REQ)) 407a92ce1a4SXin Long return -ENOPROTOOPT; 408a92ce1a4SXin Long 409a92ce1a4SXin Long if (asoc->strreset_outstanding) 410a92ce1a4SXin Long return -EINPROGRESS; 411a92ce1a4SXin Long 4125c6144a0SXin Long if (!sctp_outq_is_empty(&asoc->outqueue)) 4135c6144a0SXin Long return -EAGAIN; 4145c6144a0SXin Long 415a92ce1a4SXin Long chunk = sctp_make_strreset_tsnreq(asoc); 416a92ce1a4SXin Long if (!chunk) 417a92ce1a4SXin Long return -ENOMEM; 418a92ce1a4SXin Long 419a92ce1a4SXin Long /* Block further xmit of data until this request is completed */ 420cee360abSXin Long for (i = 0; i < stream->outcnt; i++) 421cee360abSXin Long stream->out[i].state = SCTP_STREAM_CLOSED; 422a92ce1a4SXin Long 423a92ce1a4SXin Long asoc->strreset_chunk = chunk; 424a92ce1a4SXin Long sctp_chunk_hold(asoc->strreset_chunk); 425a92ce1a4SXin Long 426a92ce1a4SXin Long retval = sctp_send_reconf(asoc, chunk); 427a92ce1a4SXin Long if (retval) { 428a92ce1a4SXin Long sctp_chunk_put(asoc->strreset_chunk); 429a92ce1a4SXin Long asoc->strreset_chunk = NULL; 430a92ce1a4SXin Long 431cee360abSXin Long for (i = 0; i < stream->outcnt; i++) 432cee360abSXin Long stream->out[i].state = SCTP_STREAM_OPEN; 433a92ce1a4SXin Long 434a92ce1a4SXin Long return retval; 435a92ce1a4SXin Long } 436a92ce1a4SXin Long 437a92ce1a4SXin Long asoc->strreset_outstanding = 1; 438a92ce1a4SXin Long 439a92ce1a4SXin Long return 0; 440a92ce1a4SXin Long } 441242bd2d5SXin Long 442242bd2d5SXin Long int sctp_send_add_streams(struct sctp_association *asoc, 443242bd2d5SXin Long struct sctp_add_streams *params) 444242bd2d5SXin Long { 445cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 446242bd2d5SXin Long struct sctp_chunk *chunk = NULL; 447dc82673fSWei Yongjun int retval; 448242bd2d5SXin Long __u32 outcnt, incnt; 449242bd2d5SXin Long __u16 out, in; 450242bd2d5SXin Long 451242bd2d5SXin Long if (!asoc->peer.reconf_capable || 452242bd2d5SXin Long !(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ)) { 453242bd2d5SXin Long retval = -ENOPROTOOPT; 454242bd2d5SXin Long goto out; 455242bd2d5SXin Long } 456242bd2d5SXin Long 457242bd2d5SXin Long if (asoc->strreset_outstanding) { 458242bd2d5SXin Long retval = -EINPROGRESS; 459242bd2d5SXin Long goto out; 460242bd2d5SXin Long } 461242bd2d5SXin Long 462242bd2d5SXin Long out = params->sas_outstrms; 463242bd2d5SXin Long in = params->sas_instrms; 464242bd2d5SXin Long outcnt = stream->outcnt + out; 465242bd2d5SXin Long incnt = stream->incnt + in; 466242bd2d5SXin Long if (outcnt > SCTP_MAX_STREAM || incnt > SCTP_MAX_STREAM || 467242bd2d5SXin Long (!out && !in)) { 468242bd2d5SXin Long retval = -EINVAL; 469242bd2d5SXin Long goto out; 470242bd2d5SXin Long } 471242bd2d5SXin Long 472242bd2d5SXin Long if (out) { 473e090abd0SMarcelo Ricardo Leitner retval = sctp_stream_alloc_out(stream, outcnt, GFP_KERNEL); 474e090abd0SMarcelo Ricardo Leitner if (retval) 475242bd2d5SXin Long goto out; 476242bd2d5SXin Long } 477242bd2d5SXin Long 478242bd2d5SXin Long chunk = sctp_make_strreset_addstrm(asoc, out, in); 479dc82673fSWei Yongjun if (!chunk) { 480dc82673fSWei Yongjun retval = -ENOMEM; 481242bd2d5SXin Long goto out; 482dc82673fSWei Yongjun } 483242bd2d5SXin Long 484242bd2d5SXin Long asoc->strreset_chunk = chunk; 485242bd2d5SXin Long sctp_chunk_hold(asoc->strreset_chunk); 486242bd2d5SXin Long 487242bd2d5SXin Long retval = sctp_send_reconf(asoc, chunk); 488242bd2d5SXin Long if (retval) { 489242bd2d5SXin Long sctp_chunk_put(asoc->strreset_chunk); 490242bd2d5SXin Long asoc->strreset_chunk = NULL; 491242bd2d5SXin Long goto out; 492242bd2d5SXin Long } 493242bd2d5SXin Long 494242bd2d5SXin Long stream->incnt = incnt; 495242bd2d5SXin Long stream->outcnt = outcnt; 496242bd2d5SXin Long 497242bd2d5SXin Long asoc->strreset_outstanding = !!out + !!in; 498242bd2d5SXin Long 499242bd2d5SXin Long out: 500242bd2d5SXin Long return retval; 501242bd2d5SXin Long } 50281054476SXin Long 5033c918704SXin Long static struct sctp_paramhdr *sctp_chunk_lookup_strreset_param( 5041da4fc97SXin Long struct sctp_association *asoc, __be32 resp_seq, 50550a41591SXin Long __be16 type) 50681054476SXin Long { 50781054476SXin Long struct sctp_chunk *chunk = asoc->strreset_chunk; 50881054476SXin Long struct sctp_reconf_chunk *hdr; 50981054476SXin Long union sctp_params param; 51081054476SXin Long 51150a41591SXin Long if (!chunk) 51281054476SXin Long return NULL; 51381054476SXin Long 51481054476SXin Long hdr = (struct sctp_reconf_chunk *)chunk->chunk_hdr; 51581054476SXin Long sctp_walk_params(param, hdr, params) { 51681054476SXin Long /* sctp_strreset_tsnreq is actually the basic structure 51781054476SXin Long * of all stream reconf params, so it's safe to use it 51881054476SXin Long * to access request_seq. 51981054476SXin Long */ 52081054476SXin Long struct sctp_strreset_tsnreq *req = param.v; 52181054476SXin Long 52250a41591SXin Long if ((!resp_seq || req->request_seq == resp_seq) && 52350a41591SXin Long (!type || type == req->param_hdr.type)) 52481054476SXin Long return param.v; 52581054476SXin Long } 52681054476SXin Long 52781054476SXin Long return NULL; 52881054476SXin Long } 52981054476SXin Long 530e4dc99c7SXin Long static void sctp_update_strreset_result(struct sctp_association *asoc, 531e4dc99c7SXin Long __u32 result) 532e4dc99c7SXin Long { 533e4dc99c7SXin Long asoc->strreset_result[1] = asoc->strreset_result[0]; 534e4dc99c7SXin Long asoc->strreset_result[0] = result; 535e4dc99c7SXin Long } 536e4dc99c7SXin Long 53781054476SXin Long struct sctp_chunk *sctp_process_strreset_outreq( 53881054476SXin Long struct sctp_association *asoc, 53981054476SXin Long union sctp_params param, 54081054476SXin Long struct sctp_ulpevent **evp) 54181054476SXin Long { 54281054476SXin Long struct sctp_strreset_outreq *outreq = param.v; 543cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 54481054476SXin Long __u32 result = SCTP_STRRESET_DENIED; 5451da4fc97SXin Long __u16 i, nums, flags = 0; 5461da4fc97SXin Long __be16 *str_p = NULL; 54781054476SXin Long __u32 request_seq; 54881054476SXin Long 54981054476SXin Long request_seq = ntohl(outreq->request_seq); 55081054476SXin Long 55181054476SXin Long if (ntohl(outreq->send_reset_at_tsn) > 55281054476SXin Long sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map)) { 55381054476SXin Long result = SCTP_STRRESET_IN_PROGRESS; 554e4dc99c7SXin Long goto err; 55581054476SXin Long } 55681054476SXin Long 557e4dc99c7SXin Long if (TSN_lt(asoc->strreset_inseq, request_seq) || 558e4dc99c7SXin Long TSN_lt(request_seq, asoc->strreset_inseq - 2)) { 55981054476SXin Long result = SCTP_STRRESET_ERR_BAD_SEQNO; 560e4dc99c7SXin Long goto err; 561e4dc99c7SXin Long } else if (TSN_lt(request_seq, asoc->strreset_inseq)) { 562e4dc99c7SXin Long i = asoc->strreset_inseq - request_seq - 1; 563e4dc99c7SXin Long result = asoc->strreset_result[i]; 564e4dc99c7SXin Long goto err; 56581054476SXin Long } 566e4dc99c7SXin Long asoc->strreset_inseq++; 56781054476SXin Long 56881054476SXin Long /* Check strreset_enable after inseq inc, as sender cannot tell 56981054476SXin Long * the peer doesn't enable strreset after receiving response with 57081054476SXin Long * result denied, as well as to keep consistent with bsd. 57181054476SXin Long */ 57281054476SXin Long if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ)) 57381054476SXin Long goto out; 57481054476SXin Long 57581054476SXin Long if (asoc->strreset_chunk) { 57650a41591SXin Long if (!sctp_chunk_lookup_strreset_param( 57750a41591SXin Long asoc, outreq->response_seq, 57850a41591SXin Long SCTP_PARAM_RESET_IN_REQUEST)) { 57981054476SXin Long /* same process with outstanding isn't 0 */ 58081054476SXin Long result = SCTP_STRRESET_ERR_IN_PROGRESS; 58181054476SXin Long goto out; 58281054476SXin Long } 58381054476SXin Long 58481054476SXin Long asoc->strreset_outstanding--; 58581054476SXin Long asoc->strreset_outseq++; 58681054476SXin Long 58781054476SXin Long if (!asoc->strreset_outstanding) { 58850a41591SXin Long struct sctp_transport *t; 58950a41591SXin Long 59081054476SXin Long t = asoc->strreset_chunk->transport; 59181054476SXin Long if (del_timer(&t->reconf_timer)) 59281054476SXin Long sctp_transport_put(t); 59381054476SXin Long 59481054476SXin Long sctp_chunk_put(asoc->strreset_chunk); 59581054476SXin Long asoc->strreset_chunk = NULL; 59681054476SXin Long } 59781054476SXin Long 59881054476SXin Long flags = SCTP_STREAM_RESET_INCOMING_SSN; 59981054476SXin Long } 60081054476SXin Long 6013aa623daSXin Long nums = (ntohs(param.p->length) - sizeof(*outreq)) / sizeof(__u16); 60281054476SXin Long if (nums) { 60381054476SXin Long str_p = outreq->list_of_streams; 60481054476SXin Long for (i = 0; i < nums; i++) { 60581054476SXin Long if (ntohs(str_p[i]) >= stream->incnt) { 60681054476SXin Long result = SCTP_STRRESET_ERR_WRONG_SSN; 60781054476SXin Long goto out; 60881054476SXin Long } 60981054476SXin Long } 61081054476SXin Long 61181054476SXin Long for (i = 0; i < nums; i++) 612107e2425SXin Long stream->in[ntohs(str_p[i])].mid = 0; 61381054476SXin Long } else { 61481054476SXin Long for (i = 0; i < stream->incnt; i++) 615107e2425SXin Long stream->in[i].mid = 0; 61681054476SXin Long } 61781054476SXin Long 61881054476SXin Long result = SCTP_STRRESET_PERFORMED; 61981054476SXin Long 62081054476SXin Long *evp = sctp_ulpevent_make_stream_reset_event(asoc, 62181054476SXin Long flags | SCTP_STREAM_RESET_OUTGOING_SSN, nums, str_p, 62281054476SXin Long GFP_ATOMIC); 62381054476SXin Long 62481054476SXin Long out: 625e4dc99c7SXin Long sctp_update_strreset_result(asoc, result); 626e4dc99c7SXin Long err: 62781054476SXin Long return sctp_make_strreset_resp(asoc, result, request_seq); 62881054476SXin Long } 62916e1a919SXin Long 63016e1a919SXin Long struct sctp_chunk *sctp_process_strreset_inreq( 63116e1a919SXin Long struct sctp_association *asoc, 63216e1a919SXin Long union sctp_params param, 63316e1a919SXin Long struct sctp_ulpevent **evp) 63416e1a919SXin Long { 63516e1a919SXin Long struct sctp_strreset_inreq *inreq = param.v; 636cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 63716e1a919SXin Long __u32 result = SCTP_STRRESET_DENIED; 63816e1a919SXin Long struct sctp_chunk *chunk = NULL; 63916e1a919SXin Long __u32 request_seq; 6401da4fc97SXin Long __u16 i, nums; 6411da4fc97SXin Long __be16 *str_p; 64216e1a919SXin Long 64316e1a919SXin Long request_seq = ntohl(inreq->request_seq); 644d0f025e6SXin Long if (TSN_lt(asoc->strreset_inseq, request_seq) || 645d0f025e6SXin Long TSN_lt(request_seq, asoc->strreset_inseq - 2)) { 64616e1a919SXin Long result = SCTP_STRRESET_ERR_BAD_SEQNO; 647d0f025e6SXin Long goto err; 648d0f025e6SXin Long } else if (TSN_lt(request_seq, asoc->strreset_inseq)) { 649d0f025e6SXin Long i = asoc->strreset_inseq - request_seq - 1; 650d0f025e6SXin Long result = asoc->strreset_result[i]; 651d0f025e6SXin Long if (result == SCTP_STRRESET_PERFORMED) 652d0f025e6SXin Long return NULL; 653d0f025e6SXin Long goto err; 65416e1a919SXin Long } 655d0f025e6SXin Long asoc->strreset_inseq++; 65616e1a919SXin Long 65716e1a919SXin Long if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ)) 65816e1a919SXin Long goto out; 65916e1a919SXin Long 66016e1a919SXin Long if (asoc->strreset_outstanding) { 66116e1a919SXin Long result = SCTP_STRRESET_ERR_IN_PROGRESS; 66216e1a919SXin Long goto out; 66316e1a919SXin Long } 66416e1a919SXin Long 6653aa623daSXin Long nums = (ntohs(param.p->length) - sizeof(*inreq)) / sizeof(__u16); 66616e1a919SXin Long str_p = inreq->list_of_streams; 66716e1a919SXin Long for (i = 0; i < nums; i++) { 66816e1a919SXin Long if (ntohs(str_p[i]) >= stream->outcnt) { 66916e1a919SXin Long result = SCTP_STRRESET_ERR_WRONG_SSN; 67016e1a919SXin Long goto out; 67116e1a919SXin Long } 67216e1a919SXin Long } 67316e1a919SXin Long 674d570a59cSXin Long if (!sctp_stream_outq_is_empty(stream, nums, str_p)) { 675d570a59cSXin Long result = SCTP_STRRESET_IN_PROGRESS; 676d570a59cSXin Long asoc->strreset_inseq--; 677d570a59cSXin Long goto err; 678d570a59cSXin Long } 679d570a59cSXin Long 68016e1a919SXin Long chunk = sctp_make_strreset_req(asoc, nums, str_p, 1, 0); 68116e1a919SXin Long if (!chunk) 68216e1a919SXin Long goto out; 68316e1a919SXin Long 68416e1a919SXin Long if (nums) 68516e1a919SXin Long for (i = 0; i < nums; i++) 68616e1a919SXin Long stream->out[ntohs(str_p[i])].state = 68716e1a919SXin Long SCTP_STREAM_CLOSED; 68816e1a919SXin Long else 68916e1a919SXin Long for (i = 0; i < stream->outcnt; i++) 69016e1a919SXin Long stream->out[i].state = SCTP_STREAM_CLOSED; 69116e1a919SXin Long 69216e1a919SXin Long asoc->strreset_chunk = chunk; 69316e1a919SXin Long asoc->strreset_outstanding = 1; 69416e1a919SXin Long sctp_chunk_hold(asoc->strreset_chunk); 69516e1a919SXin Long 696d0f025e6SXin Long result = SCTP_STRRESET_PERFORMED; 697d0f025e6SXin Long 69816e1a919SXin Long *evp = sctp_ulpevent_make_stream_reset_event(asoc, 69916e1a919SXin Long SCTP_STREAM_RESET_INCOMING_SSN, nums, str_p, GFP_ATOMIC); 70016e1a919SXin Long 70116e1a919SXin Long out: 702d0f025e6SXin Long sctp_update_strreset_result(asoc, result); 703d0f025e6SXin Long err: 70416e1a919SXin Long if (!chunk) 70516e1a919SXin Long chunk = sctp_make_strreset_resp(asoc, result, request_seq); 70616e1a919SXin Long 70716e1a919SXin Long return chunk; 70816e1a919SXin Long } 709692787ceSXin Long 710692787ceSXin Long struct sctp_chunk *sctp_process_strreset_tsnreq( 711692787ceSXin Long struct sctp_association *asoc, 712692787ceSXin Long union sctp_params param, 713692787ceSXin Long struct sctp_ulpevent **evp) 714692787ceSXin Long { 715692787ceSXin Long __u32 init_tsn = 0, next_tsn = 0, max_tsn_seen; 716692787ceSXin Long struct sctp_strreset_tsnreq *tsnreq = param.v; 717cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 718692787ceSXin Long __u32 result = SCTP_STRRESET_DENIED; 719692787ceSXin Long __u32 request_seq; 720692787ceSXin Long __u16 i; 721692787ceSXin Long 722692787ceSXin Long request_seq = ntohl(tsnreq->request_seq); 7236c801387SXin Long if (TSN_lt(asoc->strreset_inseq, request_seq) || 7246c801387SXin Long TSN_lt(request_seq, asoc->strreset_inseq - 2)) { 725692787ceSXin Long result = SCTP_STRRESET_ERR_BAD_SEQNO; 7266c801387SXin Long goto err; 7276c801387SXin Long } else if (TSN_lt(request_seq, asoc->strreset_inseq)) { 7286c801387SXin Long i = asoc->strreset_inseq - request_seq - 1; 7296c801387SXin Long result = asoc->strreset_result[i]; 7306c801387SXin Long if (result == SCTP_STRRESET_PERFORMED) { 73152a39589SXin Long next_tsn = asoc->ctsn_ack_point + 1; 7326c801387SXin Long init_tsn = 7336c801387SXin Long sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map) + 1; 734692787ceSXin Long } 7356c801387SXin Long goto err; 7366c801387SXin Long } 7375c6144a0SXin Long 7385c6144a0SXin Long if (!sctp_outq_is_empty(&asoc->outqueue)) { 7395c6144a0SXin Long result = SCTP_STRRESET_IN_PROGRESS; 7405c6144a0SXin Long goto err; 7415c6144a0SXin Long } 7425c6144a0SXin Long 7436c801387SXin Long asoc->strreset_inseq++; 744692787ceSXin Long 745692787ceSXin Long if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_ASSOC_REQ)) 746692787ceSXin Long goto out; 747692787ceSXin Long 748692787ceSXin Long if (asoc->strreset_outstanding) { 749692787ceSXin Long result = SCTP_STRRESET_ERR_IN_PROGRESS; 750692787ceSXin Long goto out; 751692787ceSXin Long } 752692787ceSXin Long 753159f2a74SXin Long /* G4: The same processing as though a FWD-TSN chunk (as defined in 754159f2a74SXin Long * [RFC3758]) with all streams affected and a new cumulative TSN 755159f2a74SXin Long * ACK of the Receiver's Next TSN minus 1 were received MUST be 756159f2a74SXin Long * performed. 757692787ceSXin Long */ 758692787ceSXin Long max_tsn_seen = sctp_tsnmap_get_max_tsn_seen(&asoc->peer.tsn_map); 75947b20a88SXin Long asoc->stream.si->report_ftsn(&asoc->ulpq, max_tsn_seen); 760692787ceSXin Long 761692787ceSXin Long /* G1: Compute an appropriate value for the Receiver's Next TSN -- the 762692787ceSXin Long * TSN that the peer should use to send the next DATA chunk. The 763692787ceSXin Long * value SHOULD be the smallest TSN not acknowledged by the 764692787ceSXin Long * receiver of the request plus 2^31. 765692787ceSXin Long */ 766692787ceSXin Long init_tsn = sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map) + (1 << 31); 767692787ceSXin Long sctp_tsnmap_init(&asoc->peer.tsn_map, SCTP_TSN_MAP_INITIAL, 768692787ceSXin Long init_tsn, GFP_ATOMIC); 769692787ceSXin Long 770159f2a74SXin Long /* G3: The same processing as though a SACK chunk with no gap report 771159f2a74SXin Long * and a cumulative TSN ACK of the Sender's Next TSN minus 1 were 772159f2a74SXin Long * received MUST be performed. 773692787ceSXin Long */ 774692787ceSXin Long sctp_outq_free(&asoc->outqueue); 775692787ceSXin Long 776692787ceSXin Long /* G2: Compute an appropriate value for the local endpoint's next TSN, 777692787ceSXin Long * i.e., the next TSN assigned by the receiver of the SSN/TSN reset 778692787ceSXin Long * chunk. The value SHOULD be the highest TSN sent by the receiver 779692787ceSXin Long * of the request plus 1. 780692787ceSXin Long */ 781692787ceSXin Long next_tsn = asoc->next_tsn; 782692787ceSXin Long asoc->ctsn_ack_point = next_tsn - 1; 783692787ceSXin Long asoc->adv_peer_ack_point = asoc->ctsn_ack_point; 784692787ceSXin Long 785692787ceSXin Long /* G5: The next expected and outgoing SSNs MUST be reset to 0 for all 786692787ceSXin Long * incoming and outgoing streams. 787692787ceSXin Long */ 788107e2425SXin Long for (i = 0; i < stream->outcnt; i++) { 789107e2425SXin Long stream->out[i].mid = 0; 790107e2425SXin Long stream->out[i].mid_uo = 0; 791107e2425SXin Long } 792692787ceSXin Long for (i = 0; i < stream->incnt; i++) 793107e2425SXin Long stream->in[i].mid = 0; 794692787ceSXin Long 795692787ceSXin Long result = SCTP_STRRESET_PERFORMED; 796692787ceSXin Long 797692787ceSXin Long *evp = sctp_ulpevent_make_assoc_reset_event(asoc, 0, init_tsn, 798692787ceSXin Long next_tsn, GFP_ATOMIC); 799692787ceSXin Long 800692787ceSXin Long out: 8016c801387SXin Long sctp_update_strreset_result(asoc, result); 8026c801387SXin Long err: 803692787ceSXin Long return sctp_make_strreset_tsnresp(asoc, result, request_seq, 804692787ceSXin Long next_tsn, init_tsn); 805692787ceSXin Long } 80650a41591SXin Long 80750a41591SXin Long struct sctp_chunk *sctp_process_strreset_addstrm_out( 80850a41591SXin Long struct sctp_association *asoc, 80950a41591SXin Long union sctp_params param, 81050a41591SXin Long struct sctp_ulpevent **evp) 81150a41591SXin Long { 81250a41591SXin Long struct sctp_strreset_addstrm *addstrm = param.v; 813cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 81450a41591SXin Long __u32 result = SCTP_STRRESET_DENIED; 81550a41591SXin Long __u32 request_seq, incnt; 816e4dc99c7SXin Long __u16 in, i; 81750a41591SXin Long 81850a41591SXin Long request_seq = ntohl(addstrm->request_seq); 819e4dc99c7SXin Long if (TSN_lt(asoc->strreset_inseq, request_seq) || 820e4dc99c7SXin Long TSN_lt(request_seq, asoc->strreset_inseq - 2)) { 82150a41591SXin Long result = SCTP_STRRESET_ERR_BAD_SEQNO; 822e4dc99c7SXin Long goto err; 823e4dc99c7SXin Long } else if (TSN_lt(request_seq, asoc->strreset_inseq)) { 824e4dc99c7SXin Long i = asoc->strreset_inseq - request_seq - 1; 825e4dc99c7SXin Long result = asoc->strreset_result[i]; 826e4dc99c7SXin Long goto err; 82750a41591SXin Long } 828e4dc99c7SXin Long asoc->strreset_inseq++; 82950a41591SXin Long 83050a41591SXin Long if (!(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ)) 83150a41591SXin Long goto out; 83250a41591SXin Long 83350a41591SXin Long if (asoc->strreset_chunk) { 83450a41591SXin Long if (!sctp_chunk_lookup_strreset_param( 83550a41591SXin Long asoc, 0, SCTP_PARAM_RESET_ADD_IN_STREAMS)) { 83650a41591SXin Long /* same process with outstanding isn't 0 */ 83750a41591SXin Long result = SCTP_STRRESET_ERR_IN_PROGRESS; 83850a41591SXin Long goto out; 83950a41591SXin Long } 84050a41591SXin Long 84150a41591SXin Long asoc->strreset_outstanding--; 84250a41591SXin Long asoc->strreset_outseq++; 84350a41591SXin Long 84450a41591SXin Long if (!asoc->strreset_outstanding) { 84550a41591SXin Long struct sctp_transport *t; 84650a41591SXin Long 84750a41591SXin Long t = asoc->strreset_chunk->transport; 84850a41591SXin Long if (del_timer(&t->reconf_timer)) 84950a41591SXin Long sctp_transport_put(t); 85050a41591SXin Long 85150a41591SXin Long sctp_chunk_put(asoc->strreset_chunk); 85250a41591SXin Long asoc->strreset_chunk = NULL; 85350a41591SXin Long } 85450a41591SXin Long } 85550a41591SXin Long 85650a41591SXin Long in = ntohs(addstrm->number_of_streams); 85750a41591SXin Long incnt = stream->incnt + in; 85850a41591SXin Long if (!in || incnt > SCTP_MAX_STREAM) 85950a41591SXin Long goto out; 86050a41591SXin Long 8611fdb8d8fSMarcelo Ricardo Leitner if (sctp_stream_alloc_in(stream, incnt, GFP_ATOMIC)) 86250a41591SXin Long goto out; 86350a41591SXin Long 86450a41591SXin Long stream->incnt = incnt; 86550a41591SXin Long 86650a41591SXin Long result = SCTP_STRRESET_PERFORMED; 86750a41591SXin Long 86850a41591SXin Long *evp = sctp_ulpevent_make_stream_change_event(asoc, 86950a41591SXin Long 0, ntohs(addstrm->number_of_streams), 0, GFP_ATOMIC); 87050a41591SXin Long 87150a41591SXin Long out: 872e4dc99c7SXin Long sctp_update_strreset_result(asoc, result); 873e4dc99c7SXin Long err: 87450a41591SXin Long return sctp_make_strreset_resp(asoc, result, request_seq); 87550a41591SXin Long } 876c5c4ebb3SXin Long 877c5c4ebb3SXin Long struct sctp_chunk *sctp_process_strreset_addstrm_in( 878c5c4ebb3SXin Long struct sctp_association *asoc, 879c5c4ebb3SXin Long union sctp_params param, 880c5c4ebb3SXin Long struct sctp_ulpevent **evp) 881c5c4ebb3SXin Long { 882c5c4ebb3SXin Long struct sctp_strreset_addstrm *addstrm = param.v; 883cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 884c5c4ebb3SXin Long __u32 result = SCTP_STRRESET_DENIED; 885c5c4ebb3SXin Long struct sctp_chunk *chunk = NULL; 886c5c4ebb3SXin Long __u32 request_seq, outcnt; 887d0f025e6SXin Long __u16 out, i; 888e090abd0SMarcelo Ricardo Leitner int ret; 889c5c4ebb3SXin Long 890c5c4ebb3SXin Long request_seq = ntohl(addstrm->request_seq); 891d0f025e6SXin Long if (TSN_lt(asoc->strreset_inseq, request_seq) || 892d0f025e6SXin Long TSN_lt(request_seq, asoc->strreset_inseq - 2)) { 893c5c4ebb3SXin Long result = SCTP_STRRESET_ERR_BAD_SEQNO; 894d0f025e6SXin Long goto err; 895d0f025e6SXin Long } else if (TSN_lt(request_seq, asoc->strreset_inseq)) { 896d0f025e6SXin Long i = asoc->strreset_inseq - request_seq - 1; 897d0f025e6SXin Long result = asoc->strreset_result[i]; 898d0f025e6SXin Long if (result == SCTP_STRRESET_PERFORMED) 899d0f025e6SXin Long return NULL; 900d0f025e6SXin Long goto err; 901c5c4ebb3SXin Long } 902d0f025e6SXin Long asoc->strreset_inseq++; 903c5c4ebb3SXin Long 904c5c4ebb3SXin Long if (!(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ)) 905c5c4ebb3SXin Long goto out; 906c5c4ebb3SXin Long 907c5c4ebb3SXin Long if (asoc->strreset_outstanding) { 908c5c4ebb3SXin Long result = SCTP_STRRESET_ERR_IN_PROGRESS; 909c5c4ebb3SXin Long goto out; 910c5c4ebb3SXin Long } 911c5c4ebb3SXin Long 912c5c4ebb3SXin Long out = ntohs(addstrm->number_of_streams); 913c5c4ebb3SXin Long outcnt = stream->outcnt + out; 914c5c4ebb3SXin Long if (!out || outcnt > SCTP_MAX_STREAM) 915c5c4ebb3SXin Long goto out; 916c5c4ebb3SXin Long 917e090abd0SMarcelo Ricardo Leitner ret = sctp_stream_alloc_out(stream, outcnt, GFP_ATOMIC); 918e090abd0SMarcelo Ricardo Leitner if (ret) 919c5c4ebb3SXin Long goto out; 920c5c4ebb3SXin Long 921c5c4ebb3SXin Long chunk = sctp_make_strreset_addstrm(asoc, out, 0); 922c5c4ebb3SXin Long if (!chunk) 923c5c4ebb3SXin Long goto out; 924c5c4ebb3SXin Long 925c5c4ebb3SXin Long asoc->strreset_chunk = chunk; 926c5c4ebb3SXin Long asoc->strreset_outstanding = 1; 927c5c4ebb3SXin Long sctp_chunk_hold(asoc->strreset_chunk); 928c5c4ebb3SXin Long 929c5c4ebb3SXin Long stream->outcnt = outcnt; 930c5c4ebb3SXin Long 931d0f025e6SXin Long result = SCTP_STRRESET_PERFORMED; 932d0f025e6SXin Long 933c5c4ebb3SXin Long *evp = sctp_ulpevent_make_stream_change_event(asoc, 934c5c4ebb3SXin Long 0, 0, ntohs(addstrm->number_of_streams), GFP_ATOMIC); 935c5c4ebb3SXin Long 936c5c4ebb3SXin Long out: 937d0f025e6SXin Long sctp_update_strreset_result(asoc, result); 938d0f025e6SXin Long err: 939c5c4ebb3SXin Long if (!chunk) 940c5c4ebb3SXin Long chunk = sctp_make_strreset_resp(asoc, result, request_seq); 941c5c4ebb3SXin Long 942c5c4ebb3SXin Long return chunk; 943c5c4ebb3SXin Long } 94411ae76e6SXin Long 94511ae76e6SXin Long struct sctp_chunk *sctp_process_strreset_resp( 94611ae76e6SXin Long struct sctp_association *asoc, 94711ae76e6SXin Long union sctp_params param, 94811ae76e6SXin Long struct sctp_ulpevent **evp) 94911ae76e6SXin Long { 950cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 95111ae76e6SXin Long struct sctp_strreset_resp *resp = param.v; 95211ae76e6SXin Long struct sctp_transport *t; 95311ae76e6SXin Long __u16 i, nums, flags = 0; 9543c918704SXin Long struct sctp_paramhdr *req; 95511ae76e6SXin Long __u32 result; 95611ae76e6SXin Long 95711ae76e6SXin Long req = sctp_chunk_lookup_strreset_param(asoc, resp->response_seq, 0); 95811ae76e6SXin Long if (!req) 95911ae76e6SXin Long return NULL; 96011ae76e6SXin Long 96111ae76e6SXin Long result = ntohl(resp->result); 96211ae76e6SXin Long if (result != SCTP_STRRESET_PERFORMED) { 96311ae76e6SXin Long /* if in progress, do nothing but retransmit */ 96411ae76e6SXin Long if (result == SCTP_STRRESET_IN_PROGRESS) 96511ae76e6SXin Long return NULL; 96611ae76e6SXin Long else if (result == SCTP_STRRESET_DENIED) 96711ae76e6SXin Long flags = SCTP_STREAM_RESET_DENIED; 96811ae76e6SXin Long else 96911ae76e6SXin Long flags = SCTP_STREAM_RESET_FAILED; 97011ae76e6SXin Long } 97111ae76e6SXin Long 97211ae76e6SXin Long if (req->type == SCTP_PARAM_RESET_OUT_REQUEST) { 97311ae76e6SXin Long struct sctp_strreset_outreq *outreq; 9741da4fc97SXin Long __be16 *str_p; 97511ae76e6SXin Long 97611ae76e6SXin Long outreq = (struct sctp_strreset_outreq *)req; 977edb12f2dSXin Long str_p = outreq->list_of_streams; 9783aa623daSXin Long nums = (ntohs(outreq->param_hdr.length) - sizeof(*outreq)) / 9793aa623daSXin Long sizeof(__u16); 98011ae76e6SXin Long 98111ae76e6SXin Long if (result == SCTP_STRRESET_PERFORMED) { 98211ae76e6SXin Long if (nums) { 983107e2425SXin Long for (i = 0; i < nums; i++) { 984107e2425SXin Long stream->out[ntohs(str_p[i])].mid = 0; 985107e2425SXin Long stream->out[ntohs(str_p[i])].mid_uo = 0; 986107e2425SXin Long } 98711ae76e6SXin Long } else { 988107e2425SXin Long for (i = 0; i < stream->outcnt; i++) { 989107e2425SXin Long stream->out[i].mid = 0; 990107e2425SXin Long stream->out[i].mid_uo = 0; 991107e2425SXin Long } 99211ae76e6SXin Long } 99311ae76e6SXin Long 99411ae76e6SXin Long flags = SCTP_STREAM_RESET_OUTGOING_SSN; 99511ae76e6SXin Long } 99611ae76e6SXin Long 99711ae76e6SXin Long for (i = 0; i < stream->outcnt; i++) 99811ae76e6SXin Long stream->out[i].state = SCTP_STREAM_OPEN; 99911ae76e6SXin Long 100011ae76e6SXin Long *evp = sctp_ulpevent_make_stream_reset_event(asoc, flags, 100111ae76e6SXin Long nums, str_p, GFP_ATOMIC); 100211ae76e6SXin Long } else if (req->type == SCTP_PARAM_RESET_IN_REQUEST) { 100311ae76e6SXin Long struct sctp_strreset_inreq *inreq; 10041da4fc97SXin Long __be16 *str_p; 100511ae76e6SXin Long 100611ae76e6SXin Long /* if the result is performed, it's impossible for inreq */ 100711ae76e6SXin Long if (result == SCTP_STRRESET_PERFORMED) 100811ae76e6SXin Long return NULL; 100911ae76e6SXin Long 101011ae76e6SXin Long inreq = (struct sctp_strreset_inreq *)req; 1011edb12f2dSXin Long str_p = inreq->list_of_streams; 10123aa623daSXin Long nums = (ntohs(inreq->param_hdr.length) - sizeof(*inreq)) / 10133aa623daSXin Long sizeof(__u16); 101411ae76e6SXin Long 101511ae76e6SXin Long *evp = sctp_ulpevent_make_stream_reset_event(asoc, flags, 101611ae76e6SXin Long nums, str_p, GFP_ATOMIC); 101711ae76e6SXin Long } else if (req->type == SCTP_PARAM_RESET_TSN_REQUEST) { 101811ae76e6SXin Long struct sctp_strreset_resptsn *resptsn; 101911ae76e6SXin Long __u32 stsn, rtsn; 102011ae76e6SXin Long 102111ae76e6SXin Long /* check for resptsn, as sctp_verify_reconf didn't do it*/ 102211ae76e6SXin Long if (ntohs(param.p->length) != sizeof(*resptsn)) 102311ae76e6SXin Long return NULL; 102411ae76e6SXin Long 102511ae76e6SXin Long resptsn = (struct sctp_strreset_resptsn *)resp; 102611ae76e6SXin Long stsn = ntohl(resptsn->senders_next_tsn); 102711ae76e6SXin Long rtsn = ntohl(resptsn->receivers_next_tsn); 102811ae76e6SXin Long 102911ae76e6SXin Long if (result == SCTP_STRRESET_PERFORMED) { 103011ae76e6SXin Long __u32 mtsn = sctp_tsnmap_get_max_tsn_seen( 103111ae76e6SXin Long &asoc->peer.tsn_map); 1032159f2a74SXin Long LIST_HEAD(temp); 103311ae76e6SXin Long 103447b20a88SXin Long asoc->stream.si->report_ftsn(&asoc->ulpq, mtsn); 103511ae76e6SXin Long 103611ae76e6SXin Long sctp_tsnmap_init(&asoc->peer.tsn_map, 103711ae76e6SXin Long SCTP_TSN_MAP_INITIAL, 103811ae76e6SXin Long stsn, GFP_ATOMIC); 103911ae76e6SXin Long 1040159f2a74SXin Long /* Clean up sacked and abandoned queues only. As the 1041159f2a74SXin Long * out_chunk_list may not be empty, splice it to temp, 1042159f2a74SXin Long * then get it back after sctp_outq_free is done. 1043159f2a74SXin Long */ 1044159f2a74SXin Long list_splice_init(&asoc->outqueue.out_chunk_list, &temp); 104511ae76e6SXin Long sctp_outq_free(&asoc->outqueue); 1046159f2a74SXin Long list_splice_init(&temp, &asoc->outqueue.out_chunk_list); 104711ae76e6SXin Long 104811ae76e6SXin Long asoc->next_tsn = rtsn; 104911ae76e6SXin Long asoc->ctsn_ack_point = asoc->next_tsn - 1; 105011ae76e6SXin Long asoc->adv_peer_ack_point = asoc->ctsn_ack_point; 105111ae76e6SXin Long 1052107e2425SXin Long for (i = 0; i < stream->outcnt; i++) { 1053107e2425SXin Long stream->out[i].mid = 0; 1054107e2425SXin Long stream->out[i].mid_uo = 0; 1055107e2425SXin Long } 105611ae76e6SXin Long for (i = 0; i < stream->incnt; i++) 1057107e2425SXin Long stream->in[i].mid = 0; 105811ae76e6SXin Long } 105911ae76e6SXin Long 106011ae76e6SXin Long for (i = 0; i < stream->outcnt; i++) 106111ae76e6SXin Long stream->out[i].state = SCTP_STREAM_OPEN; 106211ae76e6SXin Long 106311ae76e6SXin Long *evp = sctp_ulpevent_make_assoc_reset_event(asoc, flags, 106411ae76e6SXin Long stsn, rtsn, GFP_ATOMIC); 106511ae76e6SXin Long } else if (req->type == SCTP_PARAM_RESET_ADD_OUT_STREAMS) { 106611ae76e6SXin Long struct sctp_strreset_addstrm *addstrm; 106711ae76e6SXin Long __u16 number; 106811ae76e6SXin Long 106911ae76e6SXin Long addstrm = (struct sctp_strreset_addstrm *)req; 107011ae76e6SXin Long nums = ntohs(addstrm->number_of_streams); 107111ae76e6SXin Long number = stream->outcnt - nums; 107211ae76e6SXin Long 107311ae76e6SXin Long if (result == SCTP_STRRESET_PERFORMED) 107411ae76e6SXin Long for (i = number; i < stream->outcnt; i++) 107511ae76e6SXin Long stream->out[i].state = SCTP_STREAM_OPEN; 107611ae76e6SXin Long else 107711ae76e6SXin Long stream->outcnt = number; 107811ae76e6SXin Long 107911ae76e6SXin Long *evp = sctp_ulpevent_make_stream_change_event(asoc, flags, 108011ae76e6SXin Long 0, nums, GFP_ATOMIC); 108111ae76e6SXin Long } else if (req->type == SCTP_PARAM_RESET_ADD_IN_STREAMS) { 108211ae76e6SXin Long struct sctp_strreset_addstrm *addstrm; 108311ae76e6SXin Long 108411ae76e6SXin Long /* if the result is performed, it's impossible for addstrm in 108511ae76e6SXin Long * request. 108611ae76e6SXin Long */ 108711ae76e6SXin Long if (result == SCTP_STRRESET_PERFORMED) 108811ae76e6SXin Long return NULL; 108911ae76e6SXin Long 109011ae76e6SXin Long addstrm = (struct sctp_strreset_addstrm *)req; 109111ae76e6SXin Long nums = ntohs(addstrm->number_of_streams); 109211ae76e6SXin Long 109311ae76e6SXin Long *evp = sctp_ulpevent_make_stream_change_event(asoc, flags, 109411ae76e6SXin Long nums, 0, GFP_ATOMIC); 109511ae76e6SXin Long } 109611ae76e6SXin Long 109711ae76e6SXin Long asoc->strreset_outstanding--; 109811ae76e6SXin Long asoc->strreset_outseq++; 109911ae76e6SXin Long 110011ae76e6SXin Long /* remove everything for this reconf request */ 110111ae76e6SXin Long if (!asoc->strreset_outstanding) { 110211ae76e6SXin Long t = asoc->strreset_chunk->transport; 110311ae76e6SXin Long if (del_timer(&t->reconf_timer)) 110411ae76e6SXin Long sctp_transport_put(t); 110511ae76e6SXin Long 110611ae76e6SXin Long sctp_chunk_put(asoc->strreset_chunk); 110711ae76e6SXin Long asoc->strreset_chunk = NULL; 110811ae76e6SXin Long } 110911ae76e6SXin Long 111011ae76e6SXin Long return NULL; 111111ae76e6SXin Long } 1112