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 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. */ 675bbbbe32SMarcelo Ricardo Leitner sctp_chunk_fail(ch, 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 159e090abd0SMarcelo Ricardo Leitner i = sctp_stream_alloc_out(stream, outcnt, gfp); 160e090abd0SMarcelo Ricardo Leitner if (i) 161e090abd0SMarcelo Ricardo Leitner return i; 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: 170ff356414SXin Long if (!incnt) 1715bbbbe32SMarcelo Ricardo Leitner goto out; 172ff356414SXin Long 1731fdb8d8fSMarcelo Ricardo Leitner i = sctp_stream_alloc_in(stream, incnt, gfp); 1741fdb8d8fSMarcelo Ricardo Leitner if (i) { 1755bbbbe32SMarcelo Ricardo Leitner ret = -ENOMEM; 1765bbbbe32SMarcelo Ricardo Leitner goto free; 177a8386317SXin Long } 178a8386317SXin Long 179ff356414SXin Long stream->incnt = incnt; 1805bbbbe32SMarcelo Ricardo Leitner goto out; 181ff356414SXin Long 1825bbbbe32SMarcelo Ricardo Leitner free: 1835bbbbe32SMarcelo Ricardo Leitner sched->free(stream); 1845bbbbe32SMarcelo Ricardo Leitner kfree(stream->out); 1855bbbbe32SMarcelo Ricardo Leitner stream->out = NULL; 1865bbbbe32SMarcelo Ricardo Leitner out: 1875bbbbe32SMarcelo Ricardo Leitner return ret; 188a8386317SXin Long } 189a8386317SXin Long 190f952be79SMarcelo Ricardo Leitner int sctp_stream_init_ext(struct sctp_stream *stream, __u16 sid) 191f952be79SMarcelo Ricardo Leitner { 192f952be79SMarcelo Ricardo Leitner struct sctp_stream_out_ext *soute; 193f952be79SMarcelo Ricardo Leitner 194f952be79SMarcelo Ricardo Leitner soute = kzalloc(sizeof(*soute), GFP_KERNEL); 195f952be79SMarcelo Ricardo Leitner if (!soute) 196f952be79SMarcelo Ricardo Leitner return -ENOMEM; 197f952be79SMarcelo Ricardo Leitner stream->out[sid].ext = soute; 198f952be79SMarcelo Ricardo Leitner 1995bbbbe32SMarcelo Ricardo Leitner return sctp_sched_init_sid(stream, sid, GFP_KERNEL); 200f952be79SMarcelo Ricardo Leitner } 201f952be79SMarcelo Ricardo Leitner 202a8386317SXin Long void sctp_stream_free(struct sctp_stream *stream) 203a8386317SXin Long { 2045bbbbe32SMarcelo Ricardo Leitner struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream); 205f952be79SMarcelo Ricardo Leitner int i; 206f952be79SMarcelo Ricardo Leitner 2075bbbbe32SMarcelo Ricardo Leitner sched->free(stream); 208f952be79SMarcelo Ricardo Leitner for (i = 0; i < stream->outcnt; i++) 209f952be79SMarcelo Ricardo Leitner kfree(stream->out[i].ext); 210a8386317SXin Long kfree(stream->out); 211a8386317SXin Long kfree(stream->in); 212a8386317SXin Long } 213a8386317SXin Long 214a8386317SXin Long void sctp_stream_clear(struct sctp_stream *stream) 215a8386317SXin Long { 216a8386317SXin Long int i; 217a8386317SXin Long 218a8386317SXin Long for (i = 0; i < stream->outcnt; i++) 219a8386317SXin Long stream->out[i].ssn = 0; 220a8386317SXin Long 221a8386317SXin Long for (i = 0; i < stream->incnt; i++) 222a8386317SXin Long stream->in[i].ssn = 0; 223a8386317SXin Long } 2247f9d68acSXin Long 225cee360abSXin Long void sctp_stream_update(struct sctp_stream *stream, struct sctp_stream *new) 226cee360abSXin Long { 2275bbbbe32SMarcelo Ricardo Leitner struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream); 2285bbbbe32SMarcelo Ricardo Leitner 2295bbbbe32SMarcelo Ricardo Leitner sched->unsched_all(stream); 2305bbbbe32SMarcelo Ricardo Leitner sctp_stream_outq_migrate(stream, new, new->outcnt); 231cee360abSXin Long sctp_stream_free(stream); 232cee360abSXin Long 233cee360abSXin Long stream->out = new->out; 234cee360abSXin Long stream->in = new->in; 235cee360abSXin Long stream->outcnt = new->outcnt; 236cee360abSXin Long stream->incnt = new->incnt; 237cee360abSXin Long 2385bbbbe32SMarcelo Ricardo Leitner sched->sched_all(stream); 2395bbbbe32SMarcelo Ricardo Leitner 240cee360abSXin Long new->out = NULL; 241cee360abSXin Long new->in = NULL; 242cee360abSXin Long } 243cee360abSXin Long 2447f9d68acSXin Long static int sctp_send_reconf(struct sctp_association *asoc, 2457f9d68acSXin Long struct sctp_chunk *chunk) 2467f9d68acSXin Long { 2477f9d68acSXin Long struct net *net = sock_net(asoc->base.sk); 2487f9d68acSXin Long int retval = 0; 2497f9d68acSXin Long 2507f9d68acSXin Long retval = sctp_primitive_RECONF(net, asoc, chunk); 2517f9d68acSXin Long if (retval) 2527f9d68acSXin Long sctp_chunk_free(chunk); 2537f9d68acSXin Long 2547f9d68acSXin Long return retval; 2557f9d68acSXin Long } 2567f9d68acSXin Long 257d570a59cSXin Long static bool sctp_stream_outq_is_empty(struct sctp_stream *stream, 258d570a59cSXin Long __u16 str_nums, __be16 *str_list) 259d570a59cSXin Long { 260d570a59cSXin Long struct sctp_association *asoc; 261d570a59cSXin Long __u16 i; 262d570a59cSXin Long 263d570a59cSXin Long asoc = container_of(stream, struct sctp_association, stream); 264d570a59cSXin Long if (!asoc->outqueue.out_qlen) 265d570a59cSXin Long return true; 266d570a59cSXin Long 267d570a59cSXin Long if (!str_nums) 268d570a59cSXin Long return false; 269d570a59cSXin Long 270d570a59cSXin Long for (i = 0; i < str_nums; i++) { 271d570a59cSXin Long __u16 sid = ntohs(str_list[i]); 272d570a59cSXin Long 273d570a59cSXin Long if (stream->out[sid].ext && 274d570a59cSXin Long !list_empty(&stream->out[sid].ext->outq)) 275d570a59cSXin Long return false; 276d570a59cSXin Long } 277d570a59cSXin Long 278d570a59cSXin Long return true; 279d570a59cSXin Long } 280d570a59cSXin Long 2817f9d68acSXin Long int sctp_send_reset_streams(struct sctp_association *asoc, 2827f9d68acSXin Long struct sctp_reset_streams *params) 2837f9d68acSXin Long { 284cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 2857f9d68acSXin Long __u16 i, str_nums, *str_list; 2867f9d68acSXin Long struct sctp_chunk *chunk; 2877f9d68acSXin Long int retval = -EINVAL; 2881da4fc97SXin Long __be16 *nstr_list; 2897f9d68acSXin Long bool out, in; 2907f9d68acSXin Long 2917f9d68acSXin Long if (!asoc->peer.reconf_capable || 2927f9d68acSXin Long !(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ)) { 2937f9d68acSXin Long retval = -ENOPROTOOPT; 2947f9d68acSXin Long goto out; 2957f9d68acSXin Long } 2967f9d68acSXin Long 2977f9d68acSXin Long if (asoc->strreset_outstanding) { 2987f9d68acSXin Long retval = -EINPROGRESS; 2997f9d68acSXin Long goto out; 3007f9d68acSXin Long } 3017f9d68acSXin Long 3027f9d68acSXin Long out = params->srs_flags & SCTP_STREAM_RESET_OUTGOING; 3037f9d68acSXin Long in = params->srs_flags & SCTP_STREAM_RESET_INCOMING; 3047f9d68acSXin Long if (!out && !in) 3057f9d68acSXin Long goto out; 3067f9d68acSXin Long 3077f9d68acSXin Long str_nums = params->srs_number_streams; 3087f9d68acSXin Long str_list = params->srs_stream_list; 309423852f8SXin Long if (str_nums) { 310423852f8SXin Long int param_len = 0; 311423852f8SXin Long 312423852f8SXin Long if (out) { 3137f9d68acSXin Long for (i = 0; i < str_nums; i++) 3147f9d68acSXin Long if (str_list[i] >= stream->outcnt) 3157f9d68acSXin Long goto out; 3167f9d68acSXin Long 317423852f8SXin Long param_len = str_nums * sizeof(__u16) + 318423852f8SXin Long sizeof(struct sctp_strreset_outreq); 319423852f8SXin Long } 320423852f8SXin Long 321423852f8SXin Long if (in) { 3227f9d68acSXin Long for (i = 0; i < str_nums; i++) 3237f9d68acSXin Long if (str_list[i] >= stream->incnt) 3247f9d68acSXin Long goto out; 3257f9d68acSXin Long 326423852f8SXin Long param_len += str_nums * sizeof(__u16) + 327423852f8SXin Long sizeof(struct sctp_strreset_inreq); 328423852f8SXin Long } 329423852f8SXin Long 330423852f8SXin Long if (param_len > SCTP_MAX_CHUNK_LEN - 331423852f8SXin Long sizeof(struct sctp_reconf_chunk)) 332423852f8SXin Long goto out; 333423852f8SXin Long } 334423852f8SXin Long 3351da4fc97SXin Long nstr_list = kcalloc(str_nums, sizeof(__be16), GFP_KERNEL); 3361da4fc97SXin Long if (!nstr_list) { 3371da4fc97SXin Long retval = -ENOMEM; 3381da4fc97SXin Long goto out; 3391da4fc97SXin Long } 34016e1a919SXin Long 34116e1a919SXin Long for (i = 0; i < str_nums; i++) 3421da4fc97SXin Long nstr_list[i] = htons(str_list[i]); 3431da4fc97SXin Long 344d570a59cSXin Long if (out && !sctp_stream_outq_is_empty(stream, str_nums, nstr_list)) { 345d570a59cSXin Long retval = -EAGAIN; 346d570a59cSXin Long goto out; 347d570a59cSXin Long } 348d570a59cSXin Long 3491da4fc97SXin Long chunk = sctp_make_strreset_req(asoc, str_nums, nstr_list, out, in); 3501da4fc97SXin Long 3511da4fc97SXin Long kfree(nstr_list); 35216e1a919SXin Long 353119aecbaSXin Long if (!chunk) { 354119aecbaSXin Long retval = -ENOMEM; 3557f9d68acSXin Long goto out; 356119aecbaSXin Long } 3577f9d68acSXin Long 3587f9d68acSXin Long if (out) { 3597f9d68acSXin Long if (str_nums) 3607f9d68acSXin Long for (i = 0; i < str_nums; i++) 3617f9d68acSXin Long stream->out[str_list[i]].state = 3627f9d68acSXin Long SCTP_STREAM_CLOSED; 3637f9d68acSXin Long else 3647f9d68acSXin Long for (i = 0; i < stream->outcnt; i++) 3657f9d68acSXin Long stream->out[i].state = SCTP_STREAM_CLOSED; 3667f9d68acSXin Long } 3677f9d68acSXin Long 3687f9d68acSXin Long asoc->strreset_chunk = chunk; 3697f9d68acSXin Long sctp_chunk_hold(asoc->strreset_chunk); 3707f9d68acSXin Long 3717f9d68acSXin Long retval = sctp_send_reconf(asoc, chunk); 3727f9d68acSXin Long if (retval) { 3737f9d68acSXin Long sctp_chunk_put(asoc->strreset_chunk); 3747f9d68acSXin Long asoc->strreset_chunk = NULL; 375119aecbaSXin Long if (!out) 376119aecbaSXin Long goto out; 377119aecbaSXin Long 378119aecbaSXin Long if (str_nums) 379119aecbaSXin Long for (i = 0; i < str_nums; i++) 380119aecbaSXin Long stream->out[str_list[i]].state = 381119aecbaSXin Long SCTP_STREAM_OPEN; 382119aecbaSXin Long else 383119aecbaSXin Long for (i = 0; i < stream->outcnt; i++) 384119aecbaSXin Long stream->out[i].state = SCTP_STREAM_OPEN; 385119aecbaSXin Long 386119aecbaSXin Long goto out; 3877f9d68acSXin Long } 3887f9d68acSXin Long 389119aecbaSXin Long asoc->strreset_outstanding = out + in; 390119aecbaSXin Long 3917f9d68acSXin Long out: 3927f9d68acSXin Long return retval; 3937f9d68acSXin Long } 394a92ce1a4SXin Long 395a92ce1a4SXin Long int sctp_send_reset_assoc(struct sctp_association *asoc) 396a92ce1a4SXin Long { 397cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 398a92ce1a4SXin Long struct sctp_chunk *chunk = NULL; 399a92ce1a4SXin Long int retval; 400a92ce1a4SXin Long __u16 i; 401a92ce1a4SXin Long 402a92ce1a4SXin Long if (!asoc->peer.reconf_capable || 403a92ce1a4SXin Long !(asoc->strreset_enable & SCTP_ENABLE_RESET_ASSOC_REQ)) 404a92ce1a4SXin Long return -ENOPROTOOPT; 405a92ce1a4SXin Long 406a92ce1a4SXin Long if (asoc->strreset_outstanding) 407a92ce1a4SXin Long return -EINPROGRESS; 408a92ce1a4SXin Long 4095c6144a0SXin Long if (!sctp_outq_is_empty(&asoc->outqueue)) 4105c6144a0SXin Long return -EAGAIN; 4115c6144a0SXin Long 412a92ce1a4SXin Long chunk = sctp_make_strreset_tsnreq(asoc); 413a92ce1a4SXin Long if (!chunk) 414a92ce1a4SXin Long return -ENOMEM; 415a92ce1a4SXin Long 416a92ce1a4SXin Long /* Block further xmit of data until this request is completed */ 417cee360abSXin Long for (i = 0; i < stream->outcnt; i++) 418cee360abSXin Long stream->out[i].state = SCTP_STREAM_CLOSED; 419a92ce1a4SXin Long 420a92ce1a4SXin Long asoc->strreset_chunk = chunk; 421a92ce1a4SXin Long sctp_chunk_hold(asoc->strreset_chunk); 422a92ce1a4SXin Long 423a92ce1a4SXin Long retval = sctp_send_reconf(asoc, chunk); 424a92ce1a4SXin Long if (retval) { 425a92ce1a4SXin Long sctp_chunk_put(asoc->strreset_chunk); 426a92ce1a4SXin Long asoc->strreset_chunk = NULL; 427a92ce1a4SXin Long 428cee360abSXin Long for (i = 0; i < stream->outcnt; i++) 429cee360abSXin Long stream->out[i].state = SCTP_STREAM_OPEN; 430a92ce1a4SXin Long 431a92ce1a4SXin Long return retval; 432a92ce1a4SXin Long } 433a92ce1a4SXin Long 434a92ce1a4SXin Long asoc->strreset_outstanding = 1; 435a92ce1a4SXin Long 436a92ce1a4SXin Long return 0; 437a92ce1a4SXin Long } 438242bd2d5SXin Long 439242bd2d5SXin Long int sctp_send_add_streams(struct sctp_association *asoc, 440242bd2d5SXin Long struct sctp_add_streams *params) 441242bd2d5SXin Long { 442cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 443242bd2d5SXin Long struct sctp_chunk *chunk = NULL; 444dc82673fSWei Yongjun int retval; 445242bd2d5SXin Long __u32 outcnt, incnt; 446242bd2d5SXin Long __u16 out, in; 447242bd2d5SXin Long 448242bd2d5SXin Long if (!asoc->peer.reconf_capable || 449242bd2d5SXin Long !(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ)) { 450242bd2d5SXin Long retval = -ENOPROTOOPT; 451242bd2d5SXin Long goto out; 452242bd2d5SXin Long } 453242bd2d5SXin Long 454242bd2d5SXin Long if (asoc->strreset_outstanding) { 455242bd2d5SXin Long retval = -EINPROGRESS; 456242bd2d5SXin Long goto out; 457242bd2d5SXin Long } 458242bd2d5SXin Long 459242bd2d5SXin Long out = params->sas_outstrms; 460242bd2d5SXin Long in = params->sas_instrms; 461242bd2d5SXin Long outcnt = stream->outcnt + out; 462242bd2d5SXin Long incnt = stream->incnt + in; 463242bd2d5SXin Long if (outcnt > SCTP_MAX_STREAM || incnt > SCTP_MAX_STREAM || 464242bd2d5SXin Long (!out && !in)) { 465242bd2d5SXin Long retval = -EINVAL; 466242bd2d5SXin Long goto out; 467242bd2d5SXin Long } 468242bd2d5SXin Long 469242bd2d5SXin Long if (out) { 470e090abd0SMarcelo Ricardo Leitner retval = sctp_stream_alloc_out(stream, outcnt, GFP_KERNEL); 471e090abd0SMarcelo Ricardo Leitner if (retval) 472242bd2d5SXin Long goto out; 473242bd2d5SXin Long } 474242bd2d5SXin Long 475242bd2d5SXin Long chunk = sctp_make_strreset_addstrm(asoc, out, in); 476dc82673fSWei Yongjun if (!chunk) { 477dc82673fSWei Yongjun retval = -ENOMEM; 478242bd2d5SXin Long goto out; 479dc82673fSWei Yongjun } 480242bd2d5SXin Long 481242bd2d5SXin Long asoc->strreset_chunk = chunk; 482242bd2d5SXin Long sctp_chunk_hold(asoc->strreset_chunk); 483242bd2d5SXin Long 484242bd2d5SXin Long retval = sctp_send_reconf(asoc, chunk); 485242bd2d5SXin Long if (retval) { 486242bd2d5SXin Long sctp_chunk_put(asoc->strreset_chunk); 487242bd2d5SXin Long asoc->strreset_chunk = NULL; 488242bd2d5SXin Long goto out; 489242bd2d5SXin Long } 490242bd2d5SXin Long 491242bd2d5SXin Long stream->incnt = incnt; 492242bd2d5SXin Long stream->outcnt = outcnt; 493242bd2d5SXin Long 494242bd2d5SXin Long asoc->strreset_outstanding = !!out + !!in; 495242bd2d5SXin Long 496242bd2d5SXin Long out: 497242bd2d5SXin Long return retval; 498242bd2d5SXin Long } 49981054476SXin Long 5003c918704SXin Long static struct sctp_paramhdr *sctp_chunk_lookup_strreset_param( 5011da4fc97SXin Long struct sctp_association *asoc, __be32 resp_seq, 50250a41591SXin Long __be16 type) 50381054476SXin Long { 50481054476SXin Long struct sctp_chunk *chunk = asoc->strreset_chunk; 50581054476SXin Long struct sctp_reconf_chunk *hdr; 50681054476SXin Long union sctp_params param; 50781054476SXin Long 50850a41591SXin Long if (!chunk) 50981054476SXin Long return NULL; 51081054476SXin Long 51181054476SXin Long hdr = (struct sctp_reconf_chunk *)chunk->chunk_hdr; 51281054476SXin Long sctp_walk_params(param, hdr, params) { 51381054476SXin Long /* sctp_strreset_tsnreq is actually the basic structure 51481054476SXin Long * of all stream reconf params, so it's safe to use it 51581054476SXin Long * to access request_seq. 51681054476SXin Long */ 51781054476SXin Long struct sctp_strreset_tsnreq *req = param.v; 51881054476SXin Long 51950a41591SXin Long if ((!resp_seq || req->request_seq == resp_seq) && 52050a41591SXin Long (!type || type == req->param_hdr.type)) 52181054476SXin Long return param.v; 52281054476SXin Long } 52381054476SXin Long 52481054476SXin Long return NULL; 52581054476SXin Long } 52681054476SXin Long 527e4dc99c7SXin Long static void sctp_update_strreset_result(struct sctp_association *asoc, 528e4dc99c7SXin Long __u32 result) 529e4dc99c7SXin Long { 530e4dc99c7SXin Long asoc->strreset_result[1] = asoc->strreset_result[0]; 531e4dc99c7SXin Long asoc->strreset_result[0] = result; 532e4dc99c7SXin Long } 533e4dc99c7SXin Long 53481054476SXin Long struct sctp_chunk *sctp_process_strreset_outreq( 53581054476SXin Long struct sctp_association *asoc, 53681054476SXin Long union sctp_params param, 53781054476SXin Long struct sctp_ulpevent **evp) 53881054476SXin Long { 53981054476SXin Long struct sctp_strreset_outreq *outreq = param.v; 540cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 54181054476SXin Long __u32 result = SCTP_STRRESET_DENIED; 5421da4fc97SXin Long __u16 i, nums, flags = 0; 5431da4fc97SXin Long __be16 *str_p = NULL; 54481054476SXin Long __u32 request_seq; 54581054476SXin Long 54681054476SXin Long request_seq = ntohl(outreq->request_seq); 54781054476SXin Long 54881054476SXin Long if (ntohl(outreq->send_reset_at_tsn) > 54981054476SXin Long sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map)) { 55081054476SXin Long result = SCTP_STRRESET_IN_PROGRESS; 551e4dc99c7SXin Long goto err; 55281054476SXin Long } 55381054476SXin Long 554e4dc99c7SXin Long if (TSN_lt(asoc->strreset_inseq, request_seq) || 555e4dc99c7SXin Long TSN_lt(request_seq, asoc->strreset_inseq - 2)) { 55681054476SXin Long result = SCTP_STRRESET_ERR_BAD_SEQNO; 557e4dc99c7SXin Long goto err; 558e4dc99c7SXin Long } else if (TSN_lt(request_seq, asoc->strreset_inseq)) { 559e4dc99c7SXin Long i = asoc->strreset_inseq - request_seq - 1; 560e4dc99c7SXin Long result = asoc->strreset_result[i]; 561e4dc99c7SXin Long goto err; 56281054476SXin Long } 563e4dc99c7SXin Long asoc->strreset_inseq++; 56481054476SXin Long 56581054476SXin Long /* Check strreset_enable after inseq inc, as sender cannot tell 56681054476SXin Long * the peer doesn't enable strreset after receiving response with 56781054476SXin Long * result denied, as well as to keep consistent with bsd. 56881054476SXin Long */ 56981054476SXin Long if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ)) 57081054476SXin Long goto out; 57181054476SXin Long 57281054476SXin Long if (asoc->strreset_chunk) { 57350a41591SXin Long if (!sctp_chunk_lookup_strreset_param( 57450a41591SXin Long asoc, outreq->response_seq, 57550a41591SXin Long SCTP_PARAM_RESET_IN_REQUEST)) { 57681054476SXin Long /* same process with outstanding isn't 0 */ 57781054476SXin Long result = SCTP_STRRESET_ERR_IN_PROGRESS; 57881054476SXin Long goto out; 57981054476SXin Long } 58081054476SXin Long 58181054476SXin Long asoc->strreset_outstanding--; 58281054476SXin Long asoc->strreset_outseq++; 58381054476SXin Long 58481054476SXin Long if (!asoc->strreset_outstanding) { 58550a41591SXin Long struct sctp_transport *t; 58650a41591SXin Long 58781054476SXin Long t = asoc->strreset_chunk->transport; 58881054476SXin Long if (del_timer(&t->reconf_timer)) 58981054476SXin Long sctp_transport_put(t); 59081054476SXin Long 59181054476SXin Long sctp_chunk_put(asoc->strreset_chunk); 59281054476SXin Long asoc->strreset_chunk = NULL; 59381054476SXin Long } 59481054476SXin Long 59581054476SXin Long flags = SCTP_STREAM_RESET_INCOMING_SSN; 59681054476SXin Long } 59781054476SXin Long 5983aa623daSXin Long nums = (ntohs(param.p->length) - sizeof(*outreq)) / sizeof(__u16); 59981054476SXin Long if (nums) { 60081054476SXin Long str_p = outreq->list_of_streams; 60181054476SXin Long for (i = 0; i < nums; i++) { 60281054476SXin Long if (ntohs(str_p[i]) >= stream->incnt) { 60381054476SXin Long result = SCTP_STRRESET_ERR_WRONG_SSN; 60481054476SXin Long goto out; 60581054476SXin Long } 60681054476SXin Long } 60781054476SXin Long 60881054476SXin Long for (i = 0; i < nums; i++) 60981054476SXin Long stream->in[ntohs(str_p[i])].ssn = 0; 61081054476SXin Long } else { 61181054476SXin Long for (i = 0; i < stream->incnt; i++) 61281054476SXin Long stream->in[i].ssn = 0; 61381054476SXin Long } 61481054476SXin Long 61581054476SXin Long result = SCTP_STRRESET_PERFORMED; 61681054476SXin Long 61781054476SXin Long *evp = sctp_ulpevent_make_stream_reset_event(asoc, 61881054476SXin Long flags | SCTP_STREAM_RESET_OUTGOING_SSN, nums, str_p, 61981054476SXin Long GFP_ATOMIC); 62081054476SXin Long 62181054476SXin Long out: 622e4dc99c7SXin Long sctp_update_strreset_result(asoc, result); 623e4dc99c7SXin Long err: 62481054476SXin Long return sctp_make_strreset_resp(asoc, result, request_seq); 62581054476SXin Long } 62616e1a919SXin Long 62716e1a919SXin Long struct sctp_chunk *sctp_process_strreset_inreq( 62816e1a919SXin Long struct sctp_association *asoc, 62916e1a919SXin Long union sctp_params param, 63016e1a919SXin Long struct sctp_ulpevent **evp) 63116e1a919SXin Long { 63216e1a919SXin Long struct sctp_strreset_inreq *inreq = param.v; 633cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 63416e1a919SXin Long __u32 result = SCTP_STRRESET_DENIED; 63516e1a919SXin Long struct sctp_chunk *chunk = NULL; 63616e1a919SXin Long __u32 request_seq; 6371da4fc97SXin Long __u16 i, nums; 6381da4fc97SXin Long __be16 *str_p; 63916e1a919SXin Long 64016e1a919SXin Long request_seq = ntohl(inreq->request_seq); 641d0f025e6SXin Long if (TSN_lt(asoc->strreset_inseq, request_seq) || 642d0f025e6SXin Long TSN_lt(request_seq, asoc->strreset_inseq - 2)) { 64316e1a919SXin Long result = SCTP_STRRESET_ERR_BAD_SEQNO; 644d0f025e6SXin Long goto err; 645d0f025e6SXin Long } else if (TSN_lt(request_seq, asoc->strreset_inseq)) { 646d0f025e6SXin Long i = asoc->strreset_inseq - request_seq - 1; 647d0f025e6SXin Long result = asoc->strreset_result[i]; 648d0f025e6SXin Long if (result == SCTP_STRRESET_PERFORMED) 649d0f025e6SXin Long return NULL; 650d0f025e6SXin Long goto err; 65116e1a919SXin Long } 652d0f025e6SXin Long asoc->strreset_inseq++; 65316e1a919SXin Long 65416e1a919SXin Long if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ)) 65516e1a919SXin Long goto out; 65616e1a919SXin Long 65716e1a919SXin Long if (asoc->strreset_outstanding) { 65816e1a919SXin Long result = SCTP_STRRESET_ERR_IN_PROGRESS; 65916e1a919SXin Long goto out; 66016e1a919SXin Long } 66116e1a919SXin Long 6623aa623daSXin Long nums = (ntohs(param.p->length) - sizeof(*inreq)) / sizeof(__u16); 66316e1a919SXin Long str_p = inreq->list_of_streams; 66416e1a919SXin Long for (i = 0; i < nums; i++) { 66516e1a919SXin Long if (ntohs(str_p[i]) >= stream->outcnt) { 66616e1a919SXin Long result = SCTP_STRRESET_ERR_WRONG_SSN; 66716e1a919SXin Long goto out; 66816e1a919SXin Long } 66916e1a919SXin Long } 67016e1a919SXin Long 671d570a59cSXin Long if (!sctp_stream_outq_is_empty(stream, nums, str_p)) { 672d570a59cSXin Long result = SCTP_STRRESET_IN_PROGRESS; 673d570a59cSXin Long asoc->strreset_inseq--; 674d570a59cSXin Long goto err; 675d570a59cSXin Long } 676d570a59cSXin Long 67716e1a919SXin Long chunk = sctp_make_strreset_req(asoc, nums, str_p, 1, 0); 67816e1a919SXin Long if (!chunk) 67916e1a919SXin Long goto out; 68016e1a919SXin Long 68116e1a919SXin Long if (nums) 68216e1a919SXin Long for (i = 0; i < nums; i++) 68316e1a919SXin Long stream->out[ntohs(str_p[i])].state = 68416e1a919SXin Long SCTP_STREAM_CLOSED; 68516e1a919SXin Long else 68616e1a919SXin Long for (i = 0; i < stream->outcnt; i++) 68716e1a919SXin Long stream->out[i].state = SCTP_STREAM_CLOSED; 68816e1a919SXin Long 68916e1a919SXin Long asoc->strreset_chunk = chunk; 69016e1a919SXin Long asoc->strreset_outstanding = 1; 69116e1a919SXin Long sctp_chunk_hold(asoc->strreset_chunk); 69216e1a919SXin Long 693d0f025e6SXin Long result = SCTP_STRRESET_PERFORMED; 694d0f025e6SXin Long 69516e1a919SXin Long *evp = sctp_ulpevent_make_stream_reset_event(asoc, 69616e1a919SXin Long SCTP_STREAM_RESET_INCOMING_SSN, nums, str_p, GFP_ATOMIC); 69716e1a919SXin Long 69816e1a919SXin Long out: 699d0f025e6SXin Long sctp_update_strreset_result(asoc, result); 700d0f025e6SXin Long err: 70116e1a919SXin Long if (!chunk) 70216e1a919SXin Long chunk = sctp_make_strreset_resp(asoc, result, request_seq); 70316e1a919SXin Long 70416e1a919SXin Long return chunk; 70516e1a919SXin Long } 706692787ceSXin Long 707692787ceSXin Long struct sctp_chunk *sctp_process_strreset_tsnreq( 708692787ceSXin Long struct sctp_association *asoc, 709692787ceSXin Long union sctp_params param, 710692787ceSXin Long struct sctp_ulpevent **evp) 711692787ceSXin Long { 712692787ceSXin Long __u32 init_tsn = 0, next_tsn = 0, max_tsn_seen; 713692787ceSXin Long struct sctp_strreset_tsnreq *tsnreq = param.v; 714cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 715692787ceSXin Long __u32 result = SCTP_STRRESET_DENIED; 716692787ceSXin Long __u32 request_seq; 717692787ceSXin Long __u16 i; 718692787ceSXin Long 719692787ceSXin Long request_seq = ntohl(tsnreq->request_seq); 7206c801387SXin Long if (TSN_lt(asoc->strreset_inseq, request_seq) || 7216c801387SXin Long TSN_lt(request_seq, asoc->strreset_inseq - 2)) { 722692787ceSXin Long result = SCTP_STRRESET_ERR_BAD_SEQNO; 7236c801387SXin Long goto err; 7246c801387SXin Long } else if (TSN_lt(request_seq, asoc->strreset_inseq)) { 7256c801387SXin Long i = asoc->strreset_inseq - request_seq - 1; 7266c801387SXin Long result = asoc->strreset_result[i]; 7276c801387SXin Long if (result == SCTP_STRRESET_PERFORMED) { 7286c801387SXin Long next_tsn = asoc->next_tsn; 7296c801387SXin Long init_tsn = 7306c801387SXin Long sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map) + 1; 731692787ceSXin Long } 7326c801387SXin Long goto err; 7336c801387SXin Long } 7345c6144a0SXin Long 7355c6144a0SXin Long if (!sctp_outq_is_empty(&asoc->outqueue)) { 7365c6144a0SXin Long result = SCTP_STRRESET_IN_PROGRESS; 7375c6144a0SXin Long goto err; 7385c6144a0SXin Long } 7395c6144a0SXin Long 7406c801387SXin Long asoc->strreset_inseq++; 741692787ceSXin Long 742692787ceSXin Long if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_ASSOC_REQ)) 743692787ceSXin Long goto out; 744692787ceSXin Long 745692787ceSXin Long if (asoc->strreset_outstanding) { 746692787ceSXin Long result = SCTP_STRRESET_ERR_IN_PROGRESS; 747692787ceSXin Long goto out; 748692787ceSXin Long } 749692787ceSXin Long 750159f2a74SXin Long /* G4: The same processing as though a FWD-TSN chunk (as defined in 751159f2a74SXin Long * [RFC3758]) with all streams affected and a new cumulative TSN 752159f2a74SXin Long * ACK of the Receiver's Next TSN minus 1 were received MUST be 753159f2a74SXin Long * performed. 754692787ceSXin Long */ 755692787ceSXin Long max_tsn_seen = sctp_tsnmap_get_max_tsn_seen(&asoc->peer.tsn_map); 756692787ceSXin Long sctp_ulpq_reasm_flushtsn(&asoc->ulpq, max_tsn_seen); 757692787ceSXin Long sctp_ulpq_abort_pd(&asoc->ulpq, GFP_ATOMIC); 758692787ceSXin Long 759692787ceSXin Long /* G1: Compute an appropriate value for the Receiver's Next TSN -- the 760692787ceSXin Long * TSN that the peer should use to send the next DATA chunk. The 761692787ceSXin Long * value SHOULD be the smallest TSN not acknowledged by the 762692787ceSXin Long * receiver of the request plus 2^31. 763692787ceSXin Long */ 764692787ceSXin Long init_tsn = sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map) + (1 << 31); 765692787ceSXin Long sctp_tsnmap_init(&asoc->peer.tsn_map, SCTP_TSN_MAP_INITIAL, 766692787ceSXin Long init_tsn, GFP_ATOMIC); 767692787ceSXin Long 768159f2a74SXin Long /* G3: The same processing as though a SACK chunk with no gap report 769159f2a74SXin Long * and a cumulative TSN ACK of the Sender's Next TSN minus 1 were 770159f2a74SXin Long * received MUST be performed. 771692787ceSXin Long */ 772692787ceSXin Long sctp_outq_free(&asoc->outqueue); 773692787ceSXin Long 774692787ceSXin Long /* G2: Compute an appropriate value for the local endpoint's next TSN, 775692787ceSXin Long * i.e., the next TSN assigned by the receiver of the SSN/TSN reset 776692787ceSXin Long * chunk. The value SHOULD be the highest TSN sent by the receiver 777692787ceSXin Long * of the request plus 1. 778692787ceSXin Long */ 779692787ceSXin Long next_tsn = asoc->next_tsn; 780692787ceSXin Long asoc->ctsn_ack_point = next_tsn - 1; 781692787ceSXin Long asoc->adv_peer_ack_point = asoc->ctsn_ack_point; 782692787ceSXin Long 783692787ceSXin Long /* G5: The next expected and outgoing SSNs MUST be reset to 0 for all 784692787ceSXin Long * incoming and outgoing streams. 785692787ceSXin Long */ 786692787ceSXin Long for (i = 0; i < stream->outcnt; i++) 787692787ceSXin Long stream->out[i].ssn = 0; 788692787ceSXin Long for (i = 0; i < stream->incnt; i++) 789692787ceSXin Long stream->in[i].ssn = 0; 790692787ceSXin Long 791692787ceSXin Long result = SCTP_STRRESET_PERFORMED; 792692787ceSXin Long 793692787ceSXin Long *evp = sctp_ulpevent_make_assoc_reset_event(asoc, 0, init_tsn, 794692787ceSXin Long next_tsn, GFP_ATOMIC); 795692787ceSXin Long 796692787ceSXin Long out: 7976c801387SXin Long sctp_update_strreset_result(asoc, result); 7986c801387SXin Long err: 799692787ceSXin Long return sctp_make_strreset_tsnresp(asoc, result, request_seq, 800692787ceSXin Long next_tsn, init_tsn); 801692787ceSXin Long } 80250a41591SXin Long 80350a41591SXin Long struct sctp_chunk *sctp_process_strreset_addstrm_out( 80450a41591SXin Long struct sctp_association *asoc, 80550a41591SXin Long union sctp_params param, 80650a41591SXin Long struct sctp_ulpevent **evp) 80750a41591SXin Long { 80850a41591SXin Long struct sctp_strreset_addstrm *addstrm = param.v; 809cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 81050a41591SXin Long __u32 result = SCTP_STRRESET_DENIED; 81150a41591SXin Long __u32 request_seq, incnt; 812e4dc99c7SXin Long __u16 in, i; 81350a41591SXin Long 81450a41591SXin Long request_seq = ntohl(addstrm->request_seq); 815e4dc99c7SXin Long if (TSN_lt(asoc->strreset_inseq, request_seq) || 816e4dc99c7SXin Long TSN_lt(request_seq, asoc->strreset_inseq - 2)) { 81750a41591SXin Long result = SCTP_STRRESET_ERR_BAD_SEQNO; 818e4dc99c7SXin Long goto err; 819e4dc99c7SXin Long } else if (TSN_lt(request_seq, asoc->strreset_inseq)) { 820e4dc99c7SXin Long i = asoc->strreset_inseq - request_seq - 1; 821e4dc99c7SXin Long result = asoc->strreset_result[i]; 822e4dc99c7SXin Long goto err; 82350a41591SXin Long } 824e4dc99c7SXin Long asoc->strreset_inseq++; 82550a41591SXin Long 82650a41591SXin Long if (!(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ)) 82750a41591SXin Long goto out; 82850a41591SXin Long 82950a41591SXin Long if (asoc->strreset_chunk) { 83050a41591SXin Long if (!sctp_chunk_lookup_strreset_param( 83150a41591SXin Long asoc, 0, SCTP_PARAM_RESET_ADD_IN_STREAMS)) { 83250a41591SXin Long /* same process with outstanding isn't 0 */ 83350a41591SXin Long result = SCTP_STRRESET_ERR_IN_PROGRESS; 83450a41591SXin Long goto out; 83550a41591SXin Long } 83650a41591SXin Long 83750a41591SXin Long asoc->strreset_outstanding--; 83850a41591SXin Long asoc->strreset_outseq++; 83950a41591SXin Long 84050a41591SXin Long if (!asoc->strreset_outstanding) { 84150a41591SXin Long struct sctp_transport *t; 84250a41591SXin Long 84350a41591SXin Long t = asoc->strreset_chunk->transport; 84450a41591SXin Long if (del_timer(&t->reconf_timer)) 84550a41591SXin Long sctp_transport_put(t); 84650a41591SXin Long 84750a41591SXin Long sctp_chunk_put(asoc->strreset_chunk); 84850a41591SXin Long asoc->strreset_chunk = NULL; 84950a41591SXin Long } 85050a41591SXin Long } 85150a41591SXin Long 85250a41591SXin Long in = ntohs(addstrm->number_of_streams); 85350a41591SXin Long incnt = stream->incnt + in; 85450a41591SXin Long if (!in || incnt > SCTP_MAX_STREAM) 85550a41591SXin Long goto out; 85650a41591SXin Long 8571fdb8d8fSMarcelo Ricardo Leitner if (sctp_stream_alloc_in(stream, incnt, GFP_ATOMIC)) 85850a41591SXin Long goto out; 85950a41591SXin Long 86050a41591SXin Long stream->incnt = incnt; 86150a41591SXin Long 86250a41591SXin Long result = SCTP_STRRESET_PERFORMED; 86350a41591SXin Long 86450a41591SXin Long *evp = sctp_ulpevent_make_stream_change_event(asoc, 86550a41591SXin Long 0, ntohs(addstrm->number_of_streams), 0, GFP_ATOMIC); 86650a41591SXin Long 86750a41591SXin Long out: 868e4dc99c7SXin Long sctp_update_strreset_result(asoc, result); 869e4dc99c7SXin Long err: 87050a41591SXin Long return sctp_make_strreset_resp(asoc, result, request_seq); 87150a41591SXin Long } 872c5c4ebb3SXin Long 873c5c4ebb3SXin Long struct sctp_chunk *sctp_process_strreset_addstrm_in( 874c5c4ebb3SXin Long struct sctp_association *asoc, 875c5c4ebb3SXin Long union sctp_params param, 876c5c4ebb3SXin Long struct sctp_ulpevent **evp) 877c5c4ebb3SXin Long { 878c5c4ebb3SXin Long struct sctp_strreset_addstrm *addstrm = param.v; 879cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 880c5c4ebb3SXin Long __u32 result = SCTP_STRRESET_DENIED; 881c5c4ebb3SXin Long struct sctp_chunk *chunk = NULL; 882c5c4ebb3SXin Long __u32 request_seq, outcnt; 883d0f025e6SXin Long __u16 out, i; 884e090abd0SMarcelo Ricardo Leitner int ret; 885c5c4ebb3SXin Long 886c5c4ebb3SXin Long request_seq = ntohl(addstrm->request_seq); 887d0f025e6SXin Long if (TSN_lt(asoc->strreset_inseq, request_seq) || 888d0f025e6SXin Long TSN_lt(request_seq, asoc->strreset_inseq - 2)) { 889c5c4ebb3SXin Long result = SCTP_STRRESET_ERR_BAD_SEQNO; 890d0f025e6SXin Long goto err; 891d0f025e6SXin Long } else if (TSN_lt(request_seq, asoc->strreset_inseq)) { 892d0f025e6SXin Long i = asoc->strreset_inseq - request_seq - 1; 893d0f025e6SXin Long result = asoc->strreset_result[i]; 894d0f025e6SXin Long if (result == SCTP_STRRESET_PERFORMED) 895d0f025e6SXin Long return NULL; 896d0f025e6SXin Long goto err; 897c5c4ebb3SXin Long } 898d0f025e6SXin Long asoc->strreset_inseq++; 899c5c4ebb3SXin Long 900c5c4ebb3SXin Long if (!(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ)) 901c5c4ebb3SXin Long goto out; 902c5c4ebb3SXin Long 903c5c4ebb3SXin Long if (asoc->strreset_outstanding) { 904c5c4ebb3SXin Long result = SCTP_STRRESET_ERR_IN_PROGRESS; 905c5c4ebb3SXin Long goto out; 906c5c4ebb3SXin Long } 907c5c4ebb3SXin Long 908c5c4ebb3SXin Long out = ntohs(addstrm->number_of_streams); 909c5c4ebb3SXin Long outcnt = stream->outcnt + out; 910c5c4ebb3SXin Long if (!out || outcnt > SCTP_MAX_STREAM) 911c5c4ebb3SXin Long goto out; 912c5c4ebb3SXin Long 913e090abd0SMarcelo Ricardo Leitner ret = sctp_stream_alloc_out(stream, outcnt, GFP_ATOMIC); 914e090abd0SMarcelo Ricardo Leitner if (ret) 915c5c4ebb3SXin Long goto out; 916c5c4ebb3SXin Long 917c5c4ebb3SXin Long chunk = sctp_make_strreset_addstrm(asoc, out, 0); 918c5c4ebb3SXin Long if (!chunk) 919c5c4ebb3SXin Long goto out; 920c5c4ebb3SXin Long 921c5c4ebb3SXin Long asoc->strreset_chunk = chunk; 922c5c4ebb3SXin Long asoc->strreset_outstanding = 1; 923c5c4ebb3SXin Long sctp_chunk_hold(asoc->strreset_chunk); 924c5c4ebb3SXin Long 925c5c4ebb3SXin Long stream->outcnt = outcnt; 926c5c4ebb3SXin Long 927d0f025e6SXin Long result = SCTP_STRRESET_PERFORMED; 928d0f025e6SXin Long 929c5c4ebb3SXin Long *evp = sctp_ulpevent_make_stream_change_event(asoc, 930c5c4ebb3SXin Long 0, 0, ntohs(addstrm->number_of_streams), GFP_ATOMIC); 931c5c4ebb3SXin Long 932c5c4ebb3SXin Long out: 933d0f025e6SXin Long sctp_update_strreset_result(asoc, result); 934d0f025e6SXin Long err: 935c5c4ebb3SXin Long if (!chunk) 936c5c4ebb3SXin Long chunk = sctp_make_strreset_resp(asoc, result, request_seq); 937c5c4ebb3SXin Long 938c5c4ebb3SXin Long return chunk; 939c5c4ebb3SXin Long } 94011ae76e6SXin Long 94111ae76e6SXin Long struct sctp_chunk *sctp_process_strreset_resp( 94211ae76e6SXin Long struct sctp_association *asoc, 94311ae76e6SXin Long union sctp_params param, 94411ae76e6SXin Long struct sctp_ulpevent **evp) 94511ae76e6SXin Long { 946cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 94711ae76e6SXin Long struct sctp_strreset_resp *resp = param.v; 94811ae76e6SXin Long struct sctp_transport *t; 94911ae76e6SXin Long __u16 i, nums, flags = 0; 9503c918704SXin Long struct sctp_paramhdr *req; 95111ae76e6SXin Long __u32 result; 95211ae76e6SXin Long 95311ae76e6SXin Long req = sctp_chunk_lookup_strreset_param(asoc, resp->response_seq, 0); 95411ae76e6SXin Long if (!req) 95511ae76e6SXin Long return NULL; 95611ae76e6SXin Long 95711ae76e6SXin Long result = ntohl(resp->result); 95811ae76e6SXin Long if (result != SCTP_STRRESET_PERFORMED) { 95911ae76e6SXin Long /* if in progress, do nothing but retransmit */ 96011ae76e6SXin Long if (result == SCTP_STRRESET_IN_PROGRESS) 96111ae76e6SXin Long return NULL; 96211ae76e6SXin Long else if (result == SCTP_STRRESET_DENIED) 96311ae76e6SXin Long flags = SCTP_STREAM_RESET_DENIED; 96411ae76e6SXin Long else 96511ae76e6SXin Long flags = SCTP_STREAM_RESET_FAILED; 96611ae76e6SXin Long } 96711ae76e6SXin Long 96811ae76e6SXin Long if (req->type == SCTP_PARAM_RESET_OUT_REQUEST) { 96911ae76e6SXin Long struct sctp_strreset_outreq *outreq; 9701da4fc97SXin Long __be16 *str_p; 97111ae76e6SXin Long 97211ae76e6SXin Long outreq = (struct sctp_strreset_outreq *)req; 973edb12f2dSXin Long str_p = outreq->list_of_streams; 9743aa623daSXin Long nums = (ntohs(outreq->param_hdr.length) - sizeof(*outreq)) / 9753aa623daSXin Long sizeof(__u16); 97611ae76e6SXin Long 97711ae76e6SXin Long if (result == SCTP_STRRESET_PERFORMED) { 97811ae76e6SXin Long if (nums) { 97911ae76e6SXin Long for (i = 0; i < nums; i++) 98011ae76e6SXin Long stream->out[ntohs(str_p[i])].ssn = 0; 98111ae76e6SXin Long } else { 98211ae76e6SXin Long for (i = 0; i < stream->outcnt; i++) 98311ae76e6SXin Long stream->out[i].ssn = 0; 98411ae76e6SXin Long } 98511ae76e6SXin Long 98611ae76e6SXin Long flags = SCTP_STREAM_RESET_OUTGOING_SSN; 98711ae76e6SXin Long } 98811ae76e6SXin Long 98911ae76e6SXin Long for (i = 0; i < stream->outcnt; i++) 99011ae76e6SXin Long stream->out[i].state = SCTP_STREAM_OPEN; 99111ae76e6SXin Long 99211ae76e6SXin Long *evp = sctp_ulpevent_make_stream_reset_event(asoc, flags, 99311ae76e6SXin Long nums, str_p, GFP_ATOMIC); 99411ae76e6SXin Long } else if (req->type == SCTP_PARAM_RESET_IN_REQUEST) { 99511ae76e6SXin Long struct sctp_strreset_inreq *inreq; 9961da4fc97SXin Long __be16 *str_p; 99711ae76e6SXin Long 99811ae76e6SXin Long /* if the result is performed, it's impossible for inreq */ 99911ae76e6SXin Long if (result == SCTP_STRRESET_PERFORMED) 100011ae76e6SXin Long return NULL; 100111ae76e6SXin Long 100211ae76e6SXin Long inreq = (struct sctp_strreset_inreq *)req; 1003edb12f2dSXin Long str_p = inreq->list_of_streams; 10043aa623daSXin Long nums = (ntohs(inreq->param_hdr.length) - sizeof(*inreq)) / 10053aa623daSXin Long sizeof(__u16); 100611ae76e6SXin Long 100711ae76e6SXin Long *evp = sctp_ulpevent_make_stream_reset_event(asoc, flags, 100811ae76e6SXin Long nums, str_p, GFP_ATOMIC); 100911ae76e6SXin Long } else if (req->type == SCTP_PARAM_RESET_TSN_REQUEST) { 101011ae76e6SXin Long struct sctp_strreset_resptsn *resptsn; 101111ae76e6SXin Long __u32 stsn, rtsn; 101211ae76e6SXin Long 101311ae76e6SXin Long /* check for resptsn, as sctp_verify_reconf didn't do it*/ 101411ae76e6SXin Long if (ntohs(param.p->length) != sizeof(*resptsn)) 101511ae76e6SXin Long return NULL; 101611ae76e6SXin Long 101711ae76e6SXin Long resptsn = (struct sctp_strreset_resptsn *)resp; 101811ae76e6SXin Long stsn = ntohl(resptsn->senders_next_tsn); 101911ae76e6SXin Long rtsn = ntohl(resptsn->receivers_next_tsn); 102011ae76e6SXin Long 102111ae76e6SXin Long if (result == SCTP_STRRESET_PERFORMED) { 102211ae76e6SXin Long __u32 mtsn = sctp_tsnmap_get_max_tsn_seen( 102311ae76e6SXin Long &asoc->peer.tsn_map); 1024159f2a74SXin Long LIST_HEAD(temp); 102511ae76e6SXin Long 102611ae76e6SXin Long sctp_ulpq_reasm_flushtsn(&asoc->ulpq, mtsn); 102711ae76e6SXin Long sctp_ulpq_abort_pd(&asoc->ulpq, GFP_ATOMIC); 102811ae76e6SXin Long 102911ae76e6SXin Long sctp_tsnmap_init(&asoc->peer.tsn_map, 103011ae76e6SXin Long SCTP_TSN_MAP_INITIAL, 103111ae76e6SXin Long stsn, GFP_ATOMIC); 103211ae76e6SXin Long 1033159f2a74SXin Long /* Clean up sacked and abandoned queues only. As the 1034159f2a74SXin Long * out_chunk_list may not be empty, splice it to temp, 1035159f2a74SXin Long * then get it back after sctp_outq_free is done. 1036159f2a74SXin Long */ 1037159f2a74SXin Long list_splice_init(&asoc->outqueue.out_chunk_list, &temp); 103811ae76e6SXin Long sctp_outq_free(&asoc->outqueue); 1039159f2a74SXin Long list_splice_init(&temp, &asoc->outqueue.out_chunk_list); 104011ae76e6SXin Long 104111ae76e6SXin Long asoc->next_tsn = rtsn; 104211ae76e6SXin Long asoc->ctsn_ack_point = asoc->next_tsn - 1; 104311ae76e6SXin Long asoc->adv_peer_ack_point = asoc->ctsn_ack_point; 104411ae76e6SXin Long 104511ae76e6SXin Long for (i = 0; i < stream->outcnt; i++) 104611ae76e6SXin Long stream->out[i].ssn = 0; 104711ae76e6SXin Long for (i = 0; i < stream->incnt; i++) 104811ae76e6SXin Long stream->in[i].ssn = 0; 104911ae76e6SXin Long } 105011ae76e6SXin Long 105111ae76e6SXin Long for (i = 0; i < stream->outcnt; i++) 105211ae76e6SXin Long stream->out[i].state = SCTP_STREAM_OPEN; 105311ae76e6SXin Long 105411ae76e6SXin Long *evp = sctp_ulpevent_make_assoc_reset_event(asoc, flags, 105511ae76e6SXin Long stsn, rtsn, GFP_ATOMIC); 105611ae76e6SXin Long } else if (req->type == SCTP_PARAM_RESET_ADD_OUT_STREAMS) { 105711ae76e6SXin Long struct sctp_strreset_addstrm *addstrm; 105811ae76e6SXin Long __u16 number; 105911ae76e6SXin Long 106011ae76e6SXin Long addstrm = (struct sctp_strreset_addstrm *)req; 106111ae76e6SXin Long nums = ntohs(addstrm->number_of_streams); 106211ae76e6SXin Long number = stream->outcnt - nums; 106311ae76e6SXin Long 106411ae76e6SXin Long if (result == SCTP_STRRESET_PERFORMED) 106511ae76e6SXin Long for (i = number; i < stream->outcnt; i++) 106611ae76e6SXin Long stream->out[i].state = SCTP_STREAM_OPEN; 106711ae76e6SXin Long else 106811ae76e6SXin Long stream->outcnt = number; 106911ae76e6SXin Long 107011ae76e6SXin Long *evp = sctp_ulpevent_make_stream_change_event(asoc, flags, 107111ae76e6SXin Long 0, nums, GFP_ATOMIC); 107211ae76e6SXin Long } else if (req->type == SCTP_PARAM_RESET_ADD_IN_STREAMS) { 107311ae76e6SXin Long struct sctp_strreset_addstrm *addstrm; 107411ae76e6SXin Long 107511ae76e6SXin Long /* if the result is performed, it's impossible for addstrm in 107611ae76e6SXin Long * request. 107711ae76e6SXin Long */ 107811ae76e6SXin Long if (result == SCTP_STRRESET_PERFORMED) 107911ae76e6SXin Long return NULL; 108011ae76e6SXin Long 108111ae76e6SXin Long addstrm = (struct sctp_strreset_addstrm *)req; 108211ae76e6SXin Long nums = ntohs(addstrm->number_of_streams); 108311ae76e6SXin Long 108411ae76e6SXin Long *evp = sctp_ulpevent_make_stream_change_event(asoc, flags, 108511ae76e6SXin Long nums, 0, GFP_ATOMIC); 108611ae76e6SXin Long } 108711ae76e6SXin Long 108811ae76e6SXin Long asoc->strreset_outstanding--; 108911ae76e6SXin Long asoc->strreset_outseq++; 109011ae76e6SXin Long 109111ae76e6SXin Long /* remove everything for this reconf request */ 109211ae76e6SXin Long if (!asoc->strreset_outstanding) { 109311ae76e6SXin Long t = asoc->strreset_chunk->transport; 109411ae76e6SXin Long if (del_timer(&t->reconf_timer)) 109511ae76e6SXin Long sctp_transport_put(t); 109611ae76e6SXin Long 109711ae76e6SXin Long sctp_chunk_put(asoc->strreset_chunk); 109811ae76e6SXin Long asoc->strreset_chunk = NULL; 109911ae76e6SXin Long } 110011ae76e6SXin Long 110111ae76e6SXin Long return NULL; 110211ae76e6SXin Long } 1103