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 2577f9d68acSXin Long int sctp_send_reset_streams(struct sctp_association *asoc, 2587f9d68acSXin Long struct sctp_reset_streams *params) 2597f9d68acSXin Long { 260cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 2617f9d68acSXin Long __u16 i, str_nums, *str_list; 2627f9d68acSXin Long struct sctp_chunk *chunk; 2637f9d68acSXin Long int retval = -EINVAL; 2641da4fc97SXin Long __be16 *nstr_list; 2657f9d68acSXin Long bool out, in; 2667f9d68acSXin Long 2677f9d68acSXin Long if (!asoc->peer.reconf_capable || 2687f9d68acSXin Long !(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ)) { 2697f9d68acSXin Long retval = -ENOPROTOOPT; 2707f9d68acSXin Long goto out; 2717f9d68acSXin Long } 2727f9d68acSXin Long 2737f9d68acSXin Long if (asoc->strreset_outstanding) { 2747f9d68acSXin Long retval = -EINPROGRESS; 2757f9d68acSXin Long goto out; 2767f9d68acSXin Long } 2777f9d68acSXin Long 2787f9d68acSXin Long out = params->srs_flags & SCTP_STREAM_RESET_OUTGOING; 2797f9d68acSXin Long in = params->srs_flags & SCTP_STREAM_RESET_INCOMING; 2807f9d68acSXin Long if (!out && !in) 2817f9d68acSXin Long goto out; 2827f9d68acSXin Long 2837f9d68acSXin Long str_nums = params->srs_number_streams; 2847f9d68acSXin Long str_list = params->srs_stream_list; 2857f9d68acSXin Long if (out && str_nums) 2867f9d68acSXin Long for (i = 0; i < str_nums; i++) 2877f9d68acSXin Long if (str_list[i] >= stream->outcnt) 2887f9d68acSXin Long goto out; 2897f9d68acSXin Long 2907f9d68acSXin Long if (in && str_nums) 2917f9d68acSXin Long for (i = 0; i < str_nums; i++) 2927f9d68acSXin Long if (str_list[i] >= stream->incnt) 2937f9d68acSXin Long goto out; 2947f9d68acSXin Long 2951da4fc97SXin Long nstr_list = kcalloc(str_nums, sizeof(__be16), GFP_KERNEL); 2961da4fc97SXin Long if (!nstr_list) { 2971da4fc97SXin Long retval = -ENOMEM; 2981da4fc97SXin Long goto out; 2991da4fc97SXin Long } 30016e1a919SXin Long 30116e1a919SXin Long for (i = 0; i < str_nums; i++) 3021da4fc97SXin Long nstr_list[i] = htons(str_list[i]); 3031da4fc97SXin Long 3041da4fc97SXin Long chunk = sctp_make_strreset_req(asoc, str_nums, nstr_list, out, in); 3051da4fc97SXin Long 3061da4fc97SXin Long kfree(nstr_list); 30716e1a919SXin Long 308119aecbaSXin Long if (!chunk) { 309119aecbaSXin Long retval = -ENOMEM; 3107f9d68acSXin Long goto out; 311119aecbaSXin Long } 3127f9d68acSXin Long 3137f9d68acSXin Long if (out) { 3147f9d68acSXin Long if (str_nums) 3157f9d68acSXin Long for (i = 0; i < str_nums; i++) 3167f9d68acSXin Long stream->out[str_list[i]].state = 3177f9d68acSXin Long SCTP_STREAM_CLOSED; 3187f9d68acSXin Long else 3197f9d68acSXin Long for (i = 0; i < stream->outcnt; i++) 3207f9d68acSXin Long stream->out[i].state = SCTP_STREAM_CLOSED; 3217f9d68acSXin Long } 3227f9d68acSXin Long 3237f9d68acSXin Long asoc->strreset_chunk = chunk; 3247f9d68acSXin Long sctp_chunk_hold(asoc->strreset_chunk); 3257f9d68acSXin Long 3267f9d68acSXin Long retval = sctp_send_reconf(asoc, chunk); 3277f9d68acSXin Long if (retval) { 3287f9d68acSXin Long sctp_chunk_put(asoc->strreset_chunk); 3297f9d68acSXin Long asoc->strreset_chunk = NULL; 330119aecbaSXin Long if (!out) 331119aecbaSXin Long goto out; 332119aecbaSXin Long 333119aecbaSXin Long if (str_nums) 334119aecbaSXin Long for (i = 0; i < str_nums; i++) 335119aecbaSXin Long stream->out[str_list[i]].state = 336119aecbaSXin Long SCTP_STREAM_OPEN; 337119aecbaSXin Long else 338119aecbaSXin Long for (i = 0; i < stream->outcnt; i++) 339119aecbaSXin Long stream->out[i].state = SCTP_STREAM_OPEN; 340119aecbaSXin Long 341119aecbaSXin Long goto out; 3427f9d68acSXin Long } 3437f9d68acSXin Long 344119aecbaSXin Long asoc->strreset_outstanding = out + in; 345119aecbaSXin Long 3467f9d68acSXin Long out: 3477f9d68acSXin Long return retval; 3487f9d68acSXin Long } 349a92ce1a4SXin Long 350a92ce1a4SXin Long int sctp_send_reset_assoc(struct sctp_association *asoc) 351a92ce1a4SXin Long { 352cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 353a92ce1a4SXin Long struct sctp_chunk *chunk = NULL; 354a92ce1a4SXin Long int retval; 355a92ce1a4SXin Long __u16 i; 356a92ce1a4SXin Long 357a92ce1a4SXin Long if (!asoc->peer.reconf_capable || 358a92ce1a4SXin Long !(asoc->strreset_enable & SCTP_ENABLE_RESET_ASSOC_REQ)) 359a92ce1a4SXin Long return -ENOPROTOOPT; 360a92ce1a4SXin Long 361a92ce1a4SXin Long if (asoc->strreset_outstanding) 362a92ce1a4SXin Long return -EINPROGRESS; 363a92ce1a4SXin Long 364a92ce1a4SXin Long chunk = sctp_make_strreset_tsnreq(asoc); 365a92ce1a4SXin Long if (!chunk) 366a92ce1a4SXin Long return -ENOMEM; 367a92ce1a4SXin Long 368a92ce1a4SXin Long /* Block further xmit of data until this request is completed */ 369cee360abSXin Long for (i = 0; i < stream->outcnt; i++) 370cee360abSXin Long stream->out[i].state = SCTP_STREAM_CLOSED; 371a92ce1a4SXin Long 372a92ce1a4SXin Long asoc->strreset_chunk = chunk; 373a92ce1a4SXin Long sctp_chunk_hold(asoc->strreset_chunk); 374a92ce1a4SXin Long 375a92ce1a4SXin Long retval = sctp_send_reconf(asoc, chunk); 376a92ce1a4SXin Long if (retval) { 377a92ce1a4SXin Long sctp_chunk_put(asoc->strreset_chunk); 378a92ce1a4SXin Long asoc->strreset_chunk = NULL; 379a92ce1a4SXin Long 380cee360abSXin Long for (i = 0; i < stream->outcnt; i++) 381cee360abSXin Long stream->out[i].state = SCTP_STREAM_OPEN; 382a92ce1a4SXin Long 383a92ce1a4SXin Long return retval; 384a92ce1a4SXin Long } 385a92ce1a4SXin Long 386a92ce1a4SXin Long asoc->strreset_outstanding = 1; 387a92ce1a4SXin Long 388a92ce1a4SXin Long return 0; 389a92ce1a4SXin Long } 390242bd2d5SXin Long 391242bd2d5SXin Long int sctp_send_add_streams(struct sctp_association *asoc, 392242bd2d5SXin Long struct sctp_add_streams *params) 393242bd2d5SXin Long { 394cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 395242bd2d5SXin Long struct sctp_chunk *chunk = NULL; 396dc82673fSWei Yongjun int retval; 397242bd2d5SXin Long __u32 outcnt, incnt; 398242bd2d5SXin Long __u16 out, in; 399242bd2d5SXin Long 400242bd2d5SXin Long if (!asoc->peer.reconf_capable || 401242bd2d5SXin Long !(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ)) { 402242bd2d5SXin Long retval = -ENOPROTOOPT; 403242bd2d5SXin Long goto out; 404242bd2d5SXin Long } 405242bd2d5SXin Long 406242bd2d5SXin Long if (asoc->strreset_outstanding) { 407242bd2d5SXin Long retval = -EINPROGRESS; 408242bd2d5SXin Long goto out; 409242bd2d5SXin Long } 410242bd2d5SXin Long 411242bd2d5SXin Long out = params->sas_outstrms; 412242bd2d5SXin Long in = params->sas_instrms; 413242bd2d5SXin Long outcnt = stream->outcnt + out; 414242bd2d5SXin Long incnt = stream->incnt + in; 415242bd2d5SXin Long if (outcnt > SCTP_MAX_STREAM || incnt > SCTP_MAX_STREAM || 416242bd2d5SXin Long (!out && !in)) { 417242bd2d5SXin Long retval = -EINVAL; 418242bd2d5SXin Long goto out; 419242bd2d5SXin Long } 420242bd2d5SXin Long 421242bd2d5SXin Long if (out) { 422e090abd0SMarcelo Ricardo Leitner retval = sctp_stream_alloc_out(stream, outcnt, GFP_KERNEL); 423e090abd0SMarcelo Ricardo Leitner if (retval) 424242bd2d5SXin Long goto out; 425242bd2d5SXin Long } 426242bd2d5SXin Long 427242bd2d5SXin Long chunk = sctp_make_strreset_addstrm(asoc, out, in); 428dc82673fSWei Yongjun if (!chunk) { 429dc82673fSWei Yongjun retval = -ENOMEM; 430242bd2d5SXin Long goto out; 431dc82673fSWei Yongjun } 432242bd2d5SXin Long 433242bd2d5SXin Long asoc->strreset_chunk = chunk; 434242bd2d5SXin Long sctp_chunk_hold(asoc->strreset_chunk); 435242bd2d5SXin Long 436242bd2d5SXin Long retval = sctp_send_reconf(asoc, chunk); 437242bd2d5SXin Long if (retval) { 438242bd2d5SXin Long sctp_chunk_put(asoc->strreset_chunk); 439242bd2d5SXin Long asoc->strreset_chunk = NULL; 440242bd2d5SXin Long goto out; 441242bd2d5SXin Long } 442242bd2d5SXin Long 443242bd2d5SXin Long stream->incnt = incnt; 444242bd2d5SXin Long stream->outcnt = outcnt; 445242bd2d5SXin Long 446242bd2d5SXin Long asoc->strreset_outstanding = !!out + !!in; 447242bd2d5SXin Long 448242bd2d5SXin Long out: 449242bd2d5SXin Long return retval; 450242bd2d5SXin Long } 45181054476SXin Long 4523c918704SXin Long static struct sctp_paramhdr *sctp_chunk_lookup_strreset_param( 4531da4fc97SXin Long struct sctp_association *asoc, __be32 resp_seq, 45450a41591SXin Long __be16 type) 45581054476SXin Long { 45681054476SXin Long struct sctp_chunk *chunk = asoc->strreset_chunk; 45781054476SXin Long struct sctp_reconf_chunk *hdr; 45881054476SXin Long union sctp_params param; 45981054476SXin Long 46050a41591SXin Long if (!chunk) 46181054476SXin Long return NULL; 46281054476SXin Long 46381054476SXin Long hdr = (struct sctp_reconf_chunk *)chunk->chunk_hdr; 46481054476SXin Long sctp_walk_params(param, hdr, params) { 46581054476SXin Long /* sctp_strreset_tsnreq is actually the basic structure 46681054476SXin Long * of all stream reconf params, so it's safe to use it 46781054476SXin Long * to access request_seq. 46881054476SXin Long */ 46981054476SXin Long struct sctp_strreset_tsnreq *req = param.v; 47081054476SXin Long 47150a41591SXin Long if ((!resp_seq || req->request_seq == resp_seq) && 47250a41591SXin Long (!type || type == req->param_hdr.type)) 47381054476SXin Long return param.v; 47481054476SXin Long } 47581054476SXin Long 47681054476SXin Long return NULL; 47781054476SXin Long } 47881054476SXin Long 479e4dc99c7SXin Long static void sctp_update_strreset_result(struct sctp_association *asoc, 480e4dc99c7SXin Long __u32 result) 481e4dc99c7SXin Long { 482e4dc99c7SXin Long asoc->strreset_result[1] = asoc->strreset_result[0]; 483e4dc99c7SXin Long asoc->strreset_result[0] = result; 484e4dc99c7SXin Long } 485e4dc99c7SXin Long 48681054476SXin Long struct sctp_chunk *sctp_process_strreset_outreq( 48781054476SXin Long struct sctp_association *asoc, 48881054476SXin Long union sctp_params param, 48981054476SXin Long struct sctp_ulpevent **evp) 49081054476SXin Long { 49181054476SXin Long struct sctp_strreset_outreq *outreq = param.v; 492cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 49381054476SXin Long __u32 result = SCTP_STRRESET_DENIED; 4941da4fc97SXin Long __u16 i, nums, flags = 0; 4951da4fc97SXin Long __be16 *str_p = NULL; 49681054476SXin Long __u32 request_seq; 49781054476SXin Long 49881054476SXin Long request_seq = ntohl(outreq->request_seq); 49981054476SXin Long 50081054476SXin Long if (ntohl(outreq->send_reset_at_tsn) > 50181054476SXin Long sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map)) { 50281054476SXin Long result = SCTP_STRRESET_IN_PROGRESS; 503e4dc99c7SXin Long goto err; 50481054476SXin Long } 50581054476SXin Long 506e4dc99c7SXin Long if (TSN_lt(asoc->strreset_inseq, request_seq) || 507e4dc99c7SXin Long TSN_lt(request_seq, asoc->strreset_inseq - 2)) { 50881054476SXin Long result = SCTP_STRRESET_ERR_BAD_SEQNO; 509e4dc99c7SXin Long goto err; 510e4dc99c7SXin Long } else if (TSN_lt(request_seq, asoc->strreset_inseq)) { 511e4dc99c7SXin Long i = asoc->strreset_inseq - request_seq - 1; 512e4dc99c7SXin Long result = asoc->strreset_result[i]; 513e4dc99c7SXin Long goto err; 51481054476SXin Long } 515e4dc99c7SXin Long asoc->strreset_inseq++; 51681054476SXin Long 51781054476SXin Long /* Check strreset_enable after inseq inc, as sender cannot tell 51881054476SXin Long * the peer doesn't enable strreset after receiving response with 51981054476SXin Long * result denied, as well as to keep consistent with bsd. 52081054476SXin Long */ 52181054476SXin Long if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ)) 52281054476SXin Long goto out; 52381054476SXin Long 52481054476SXin Long if (asoc->strreset_chunk) { 52550a41591SXin Long if (!sctp_chunk_lookup_strreset_param( 52650a41591SXin Long asoc, outreq->response_seq, 52750a41591SXin Long SCTP_PARAM_RESET_IN_REQUEST)) { 52881054476SXin Long /* same process with outstanding isn't 0 */ 52981054476SXin Long result = SCTP_STRRESET_ERR_IN_PROGRESS; 53081054476SXin Long goto out; 53181054476SXin Long } 53281054476SXin Long 53381054476SXin Long asoc->strreset_outstanding--; 53481054476SXin Long asoc->strreset_outseq++; 53581054476SXin Long 53681054476SXin Long if (!asoc->strreset_outstanding) { 53750a41591SXin Long struct sctp_transport *t; 53850a41591SXin Long 53981054476SXin Long t = asoc->strreset_chunk->transport; 54081054476SXin Long if (del_timer(&t->reconf_timer)) 54181054476SXin Long sctp_transport_put(t); 54281054476SXin Long 54381054476SXin Long sctp_chunk_put(asoc->strreset_chunk); 54481054476SXin Long asoc->strreset_chunk = NULL; 54581054476SXin Long } 54681054476SXin Long 54781054476SXin Long flags = SCTP_STREAM_RESET_INCOMING_SSN; 54881054476SXin Long } 54981054476SXin Long 55081054476SXin Long nums = (ntohs(param.p->length) - sizeof(*outreq)) / 2; 55181054476SXin Long if (nums) { 55281054476SXin Long str_p = outreq->list_of_streams; 55381054476SXin Long for (i = 0; i < nums; i++) { 55481054476SXin Long if (ntohs(str_p[i]) >= stream->incnt) { 55581054476SXin Long result = SCTP_STRRESET_ERR_WRONG_SSN; 55681054476SXin Long goto out; 55781054476SXin Long } 55881054476SXin Long } 55981054476SXin Long 56081054476SXin Long for (i = 0; i < nums; i++) 56181054476SXin Long stream->in[ntohs(str_p[i])].ssn = 0; 56281054476SXin Long } else { 56381054476SXin Long for (i = 0; i < stream->incnt; i++) 56481054476SXin Long stream->in[i].ssn = 0; 56581054476SXin Long } 56681054476SXin Long 56781054476SXin Long result = SCTP_STRRESET_PERFORMED; 56881054476SXin Long 56981054476SXin Long *evp = sctp_ulpevent_make_stream_reset_event(asoc, 57081054476SXin Long flags | SCTP_STREAM_RESET_OUTGOING_SSN, nums, str_p, 57181054476SXin Long GFP_ATOMIC); 57281054476SXin Long 57381054476SXin Long out: 574e4dc99c7SXin Long sctp_update_strreset_result(asoc, result); 575e4dc99c7SXin Long err: 57681054476SXin Long return sctp_make_strreset_resp(asoc, result, request_seq); 57781054476SXin Long } 57816e1a919SXin Long 57916e1a919SXin Long struct sctp_chunk *sctp_process_strreset_inreq( 58016e1a919SXin Long struct sctp_association *asoc, 58116e1a919SXin Long union sctp_params param, 58216e1a919SXin Long struct sctp_ulpevent **evp) 58316e1a919SXin Long { 58416e1a919SXin Long struct sctp_strreset_inreq *inreq = param.v; 585cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 58616e1a919SXin Long __u32 result = SCTP_STRRESET_DENIED; 58716e1a919SXin Long struct sctp_chunk *chunk = NULL; 58816e1a919SXin Long __u32 request_seq; 5891da4fc97SXin Long __u16 i, nums; 5901da4fc97SXin Long __be16 *str_p; 59116e1a919SXin Long 59216e1a919SXin Long request_seq = ntohl(inreq->request_seq); 593d0f025e6SXin Long if (TSN_lt(asoc->strreset_inseq, request_seq) || 594d0f025e6SXin Long TSN_lt(request_seq, asoc->strreset_inseq - 2)) { 59516e1a919SXin Long result = SCTP_STRRESET_ERR_BAD_SEQNO; 596d0f025e6SXin Long goto err; 597d0f025e6SXin Long } else if (TSN_lt(request_seq, asoc->strreset_inseq)) { 598d0f025e6SXin Long i = asoc->strreset_inseq - request_seq - 1; 599d0f025e6SXin Long result = asoc->strreset_result[i]; 600d0f025e6SXin Long if (result == SCTP_STRRESET_PERFORMED) 601d0f025e6SXin Long return NULL; 602d0f025e6SXin Long goto err; 60316e1a919SXin Long } 604d0f025e6SXin Long asoc->strreset_inseq++; 60516e1a919SXin Long 60616e1a919SXin Long if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ)) 60716e1a919SXin Long goto out; 60816e1a919SXin Long 60916e1a919SXin Long if (asoc->strreset_outstanding) { 61016e1a919SXin Long result = SCTP_STRRESET_ERR_IN_PROGRESS; 61116e1a919SXin Long goto out; 61216e1a919SXin Long } 61316e1a919SXin Long 61416e1a919SXin Long nums = (ntohs(param.p->length) - sizeof(*inreq)) / 2; 61516e1a919SXin Long str_p = inreq->list_of_streams; 61616e1a919SXin Long for (i = 0; i < nums; i++) { 61716e1a919SXin Long if (ntohs(str_p[i]) >= stream->outcnt) { 61816e1a919SXin Long result = SCTP_STRRESET_ERR_WRONG_SSN; 61916e1a919SXin Long goto out; 62016e1a919SXin Long } 62116e1a919SXin Long } 62216e1a919SXin Long 62316e1a919SXin Long chunk = sctp_make_strreset_req(asoc, nums, str_p, 1, 0); 62416e1a919SXin Long if (!chunk) 62516e1a919SXin Long goto out; 62616e1a919SXin Long 62716e1a919SXin Long if (nums) 62816e1a919SXin Long for (i = 0; i < nums; i++) 62916e1a919SXin Long stream->out[ntohs(str_p[i])].state = 63016e1a919SXin Long SCTP_STREAM_CLOSED; 63116e1a919SXin Long else 63216e1a919SXin Long for (i = 0; i < stream->outcnt; i++) 63316e1a919SXin Long stream->out[i].state = SCTP_STREAM_CLOSED; 63416e1a919SXin Long 63516e1a919SXin Long asoc->strreset_chunk = chunk; 63616e1a919SXin Long asoc->strreset_outstanding = 1; 63716e1a919SXin Long sctp_chunk_hold(asoc->strreset_chunk); 63816e1a919SXin Long 639d0f025e6SXin Long result = SCTP_STRRESET_PERFORMED; 640d0f025e6SXin Long 64116e1a919SXin Long *evp = sctp_ulpevent_make_stream_reset_event(asoc, 64216e1a919SXin Long SCTP_STREAM_RESET_INCOMING_SSN, nums, str_p, GFP_ATOMIC); 64316e1a919SXin Long 64416e1a919SXin Long out: 645d0f025e6SXin Long sctp_update_strreset_result(asoc, result); 646d0f025e6SXin Long err: 64716e1a919SXin Long if (!chunk) 64816e1a919SXin Long chunk = sctp_make_strreset_resp(asoc, result, request_seq); 64916e1a919SXin Long 65016e1a919SXin Long return chunk; 65116e1a919SXin Long } 652692787ceSXin Long 653692787ceSXin Long struct sctp_chunk *sctp_process_strreset_tsnreq( 654692787ceSXin Long struct sctp_association *asoc, 655692787ceSXin Long union sctp_params param, 656692787ceSXin Long struct sctp_ulpevent **evp) 657692787ceSXin Long { 658692787ceSXin Long __u32 init_tsn = 0, next_tsn = 0, max_tsn_seen; 659692787ceSXin Long struct sctp_strreset_tsnreq *tsnreq = param.v; 660cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 661692787ceSXin Long __u32 result = SCTP_STRRESET_DENIED; 662692787ceSXin Long __u32 request_seq; 663692787ceSXin Long __u16 i; 664692787ceSXin Long 665692787ceSXin Long request_seq = ntohl(tsnreq->request_seq); 6666c801387SXin Long if (TSN_lt(asoc->strreset_inseq, request_seq) || 6676c801387SXin Long TSN_lt(request_seq, asoc->strreset_inseq - 2)) { 668692787ceSXin Long result = SCTP_STRRESET_ERR_BAD_SEQNO; 6696c801387SXin Long goto err; 6706c801387SXin Long } else if (TSN_lt(request_seq, asoc->strreset_inseq)) { 6716c801387SXin Long i = asoc->strreset_inseq - request_seq - 1; 6726c801387SXin Long result = asoc->strreset_result[i]; 6736c801387SXin Long if (result == SCTP_STRRESET_PERFORMED) { 6746c801387SXin Long next_tsn = asoc->next_tsn; 6756c801387SXin Long init_tsn = 6766c801387SXin Long sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map) + 1; 677692787ceSXin Long } 6786c801387SXin Long goto err; 6796c801387SXin Long } 6806c801387SXin Long asoc->strreset_inseq++; 681692787ceSXin Long 682692787ceSXin Long if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_ASSOC_REQ)) 683692787ceSXin Long goto out; 684692787ceSXin Long 685692787ceSXin Long if (asoc->strreset_outstanding) { 686692787ceSXin Long result = SCTP_STRRESET_ERR_IN_PROGRESS; 687692787ceSXin Long goto out; 688692787ceSXin Long } 689692787ceSXin Long 690692787ceSXin Long /* G3: The same processing as though a SACK chunk with no gap report 691692787ceSXin Long * and a cumulative TSN ACK of the Sender's Next TSN minus 1 were 692692787ceSXin Long * received MUST be performed. 693692787ceSXin Long */ 694692787ceSXin Long max_tsn_seen = sctp_tsnmap_get_max_tsn_seen(&asoc->peer.tsn_map); 695692787ceSXin Long sctp_ulpq_reasm_flushtsn(&asoc->ulpq, max_tsn_seen); 696692787ceSXin Long sctp_ulpq_abort_pd(&asoc->ulpq, GFP_ATOMIC); 697692787ceSXin Long 698692787ceSXin Long /* G1: Compute an appropriate value for the Receiver's Next TSN -- the 699692787ceSXin Long * TSN that the peer should use to send the next DATA chunk. The 700692787ceSXin Long * value SHOULD be the smallest TSN not acknowledged by the 701692787ceSXin Long * receiver of the request plus 2^31. 702692787ceSXin Long */ 703692787ceSXin Long init_tsn = sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map) + (1 << 31); 704692787ceSXin Long sctp_tsnmap_init(&asoc->peer.tsn_map, SCTP_TSN_MAP_INITIAL, 705692787ceSXin Long init_tsn, GFP_ATOMIC); 706692787ceSXin Long 707692787ceSXin Long /* G4: The same processing as though a FWD-TSN chunk (as defined in 708692787ceSXin Long * [RFC3758]) with all streams affected and a new cumulative TSN 709692787ceSXin Long * ACK of the Receiver's Next TSN minus 1 were received MUST be 710692787ceSXin Long * performed. 711692787ceSXin Long */ 712692787ceSXin Long sctp_outq_free(&asoc->outqueue); 713692787ceSXin Long 714692787ceSXin Long /* G2: Compute an appropriate value for the local endpoint's next TSN, 715692787ceSXin Long * i.e., the next TSN assigned by the receiver of the SSN/TSN reset 716692787ceSXin Long * chunk. The value SHOULD be the highest TSN sent by the receiver 717692787ceSXin Long * of the request plus 1. 718692787ceSXin Long */ 719692787ceSXin Long next_tsn = asoc->next_tsn; 720692787ceSXin Long asoc->ctsn_ack_point = next_tsn - 1; 721692787ceSXin Long asoc->adv_peer_ack_point = asoc->ctsn_ack_point; 722692787ceSXin Long 723692787ceSXin Long /* G5: The next expected and outgoing SSNs MUST be reset to 0 for all 724692787ceSXin Long * incoming and outgoing streams. 725692787ceSXin Long */ 726692787ceSXin Long for (i = 0; i < stream->outcnt; i++) 727692787ceSXin Long stream->out[i].ssn = 0; 728692787ceSXin Long for (i = 0; i < stream->incnt; i++) 729692787ceSXin Long stream->in[i].ssn = 0; 730692787ceSXin Long 731692787ceSXin Long result = SCTP_STRRESET_PERFORMED; 732692787ceSXin Long 733692787ceSXin Long *evp = sctp_ulpevent_make_assoc_reset_event(asoc, 0, init_tsn, 734692787ceSXin Long next_tsn, GFP_ATOMIC); 735692787ceSXin Long 736692787ceSXin Long out: 7376c801387SXin Long sctp_update_strreset_result(asoc, result); 7386c801387SXin Long err: 739692787ceSXin Long return sctp_make_strreset_tsnresp(asoc, result, request_seq, 740692787ceSXin Long next_tsn, init_tsn); 741692787ceSXin Long } 74250a41591SXin Long 74350a41591SXin Long struct sctp_chunk *sctp_process_strreset_addstrm_out( 74450a41591SXin Long struct sctp_association *asoc, 74550a41591SXin Long union sctp_params param, 74650a41591SXin Long struct sctp_ulpevent **evp) 74750a41591SXin Long { 74850a41591SXin Long struct sctp_strreset_addstrm *addstrm = param.v; 749cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 75050a41591SXin Long __u32 result = SCTP_STRRESET_DENIED; 75150a41591SXin Long __u32 request_seq, incnt; 752e4dc99c7SXin Long __u16 in, i; 75350a41591SXin Long 75450a41591SXin Long request_seq = ntohl(addstrm->request_seq); 755e4dc99c7SXin Long if (TSN_lt(asoc->strreset_inseq, request_seq) || 756e4dc99c7SXin Long TSN_lt(request_seq, asoc->strreset_inseq - 2)) { 75750a41591SXin Long result = SCTP_STRRESET_ERR_BAD_SEQNO; 758e4dc99c7SXin Long goto err; 759e4dc99c7SXin Long } else if (TSN_lt(request_seq, asoc->strreset_inseq)) { 760e4dc99c7SXin Long i = asoc->strreset_inseq - request_seq - 1; 761e4dc99c7SXin Long result = asoc->strreset_result[i]; 762e4dc99c7SXin Long goto err; 76350a41591SXin Long } 764e4dc99c7SXin Long asoc->strreset_inseq++; 76550a41591SXin Long 76650a41591SXin Long if (!(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ)) 76750a41591SXin Long goto out; 76850a41591SXin Long 76950a41591SXin Long if (asoc->strreset_chunk) { 77050a41591SXin Long if (!sctp_chunk_lookup_strreset_param( 77150a41591SXin Long asoc, 0, SCTP_PARAM_RESET_ADD_IN_STREAMS)) { 77250a41591SXin Long /* same process with outstanding isn't 0 */ 77350a41591SXin Long result = SCTP_STRRESET_ERR_IN_PROGRESS; 77450a41591SXin Long goto out; 77550a41591SXin Long } 77650a41591SXin Long 77750a41591SXin Long asoc->strreset_outstanding--; 77850a41591SXin Long asoc->strreset_outseq++; 77950a41591SXin Long 78050a41591SXin Long if (!asoc->strreset_outstanding) { 78150a41591SXin Long struct sctp_transport *t; 78250a41591SXin Long 78350a41591SXin Long t = asoc->strreset_chunk->transport; 78450a41591SXin Long if (del_timer(&t->reconf_timer)) 78550a41591SXin Long sctp_transport_put(t); 78650a41591SXin Long 78750a41591SXin Long sctp_chunk_put(asoc->strreset_chunk); 78850a41591SXin Long asoc->strreset_chunk = NULL; 78950a41591SXin Long } 79050a41591SXin Long } 79150a41591SXin Long 79250a41591SXin Long in = ntohs(addstrm->number_of_streams); 79350a41591SXin Long incnt = stream->incnt + in; 79450a41591SXin Long if (!in || incnt > SCTP_MAX_STREAM) 79550a41591SXin Long goto out; 79650a41591SXin Long 7971fdb8d8fSMarcelo Ricardo Leitner if (sctp_stream_alloc_in(stream, incnt, GFP_ATOMIC)) 79850a41591SXin Long goto out; 79950a41591SXin Long 80050a41591SXin Long stream->incnt = incnt; 80150a41591SXin Long 80250a41591SXin Long result = SCTP_STRRESET_PERFORMED; 80350a41591SXin Long 80450a41591SXin Long *evp = sctp_ulpevent_make_stream_change_event(asoc, 80550a41591SXin Long 0, ntohs(addstrm->number_of_streams), 0, GFP_ATOMIC); 80650a41591SXin Long 80750a41591SXin Long out: 808e4dc99c7SXin Long sctp_update_strreset_result(asoc, result); 809e4dc99c7SXin Long err: 81050a41591SXin Long return sctp_make_strreset_resp(asoc, result, request_seq); 81150a41591SXin Long } 812c5c4ebb3SXin Long 813c5c4ebb3SXin Long struct sctp_chunk *sctp_process_strreset_addstrm_in( 814c5c4ebb3SXin Long struct sctp_association *asoc, 815c5c4ebb3SXin Long union sctp_params param, 816c5c4ebb3SXin Long struct sctp_ulpevent **evp) 817c5c4ebb3SXin Long { 818c5c4ebb3SXin Long struct sctp_strreset_addstrm *addstrm = param.v; 819cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 820c5c4ebb3SXin Long __u32 result = SCTP_STRRESET_DENIED; 821c5c4ebb3SXin Long struct sctp_chunk *chunk = NULL; 822c5c4ebb3SXin Long __u32 request_seq, outcnt; 823d0f025e6SXin Long __u16 out, i; 824e090abd0SMarcelo Ricardo Leitner int ret; 825c5c4ebb3SXin Long 826c5c4ebb3SXin Long request_seq = ntohl(addstrm->request_seq); 827d0f025e6SXin Long if (TSN_lt(asoc->strreset_inseq, request_seq) || 828d0f025e6SXin Long TSN_lt(request_seq, asoc->strreset_inseq - 2)) { 829c5c4ebb3SXin Long result = SCTP_STRRESET_ERR_BAD_SEQNO; 830d0f025e6SXin Long goto err; 831d0f025e6SXin Long } else if (TSN_lt(request_seq, asoc->strreset_inseq)) { 832d0f025e6SXin Long i = asoc->strreset_inseq - request_seq - 1; 833d0f025e6SXin Long result = asoc->strreset_result[i]; 834d0f025e6SXin Long if (result == SCTP_STRRESET_PERFORMED) 835d0f025e6SXin Long return NULL; 836d0f025e6SXin Long goto err; 837c5c4ebb3SXin Long } 838d0f025e6SXin Long asoc->strreset_inseq++; 839c5c4ebb3SXin Long 840c5c4ebb3SXin Long if (!(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ)) 841c5c4ebb3SXin Long goto out; 842c5c4ebb3SXin Long 843c5c4ebb3SXin Long if (asoc->strreset_outstanding) { 844c5c4ebb3SXin Long result = SCTP_STRRESET_ERR_IN_PROGRESS; 845c5c4ebb3SXin Long goto out; 846c5c4ebb3SXin Long } 847c5c4ebb3SXin Long 848c5c4ebb3SXin Long out = ntohs(addstrm->number_of_streams); 849c5c4ebb3SXin Long outcnt = stream->outcnt + out; 850c5c4ebb3SXin Long if (!out || outcnt > SCTP_MAX_STREAM) 851c5c4ebb3SXin Long goto out; 852c5c4ebb3SXin Long 853e090abd0SMarcelo Ricardo Leitner ret = sctp_stream_alloc_out(stream, outcnt, GFP_ATOMIC); 854e090abd0SMarcelo Ricardo Leitner if (ret) 855c5c4ebb3SXin Long goto out; 856c5c4ebb3SXin Long 857c5c4ebb3SXin Long chunk = sctp_make_strreset_addstrm(asoc, out, 0); 858c5c4ebb3SXin Long if (!chunk) 859c5c4ebb3SXin Long goto out; 860c5c4ebb3SXin Long 861c5c4ebb3SXin Long asoc->strreset_chunk = chunk; 862c5c4ebb3SXin Long asoc->strreset_outstanding = 1; 863c5c4ebb3SXin Long sctp_chunk_hold(asoc->strreset_chunk); 864c5c4ebb3SXin Long 865c5c4ebb3SXin Long stream->outcnt = outcnt; 866c5c4ebb3SXin Long 867d0f025e6SXin Long result = SCTP_STRRESET_PERFORMED; 868d0f025e6SXin Long 869c5c4ebb3SXin Long *evp = sctp_ulpevent_make_stream_change_event(asoc, 870c5c4ebb3SXin Long 0, 0, ntohs(addstrm->number_of_streams), GFP_ATOMIC); 871c5c4ebb3SXin Long 872c5c4ebb3SXin Long out: 873d0f025e6SXin Long sctp_update_strreset_result(asoc, result); 874d0f025e6SXin Long err: 875c5c4ebb3SXin Long if (!chunk) 876c5c4ebb3SXin Long chunk = sctp_make_strreset_resp(asoc, result, request_seq); 877c5c4ebb3SXin Long 878c5c4ebb3SXin Long return chunk; 879c5c4ebb3SXin Long } 88011ae76e6SXin Long 88111ae76e6SXin Long struct sctp_chunk *sctp_process_strreset_resp( 88211ae76e6SXin Long struct sctp_association *asoc, 88311ae76e6SXin Long union sctp_params param, 88411ae76e6SXin Long struct sctp_ulpevent **evp) 88511ae76e6SXin Long { 886cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 88711ae76e6SXin Long struct sctp_strreset_resp *resp = param.v; 88811ae76e6SXin Long struct sctp_transport *t; 88911ae76e6SXin Long __u16 i, nums, flags = 0; 8903c918704SXin Long struct sctp_paramhdr *req; 89111ae76e6SXin Long __u32 result; 89211ae76e6SXin Long 89311ae76e6SXin Long req = sctp_chunk_lookup_strreset_param(asoc, resp->response_seq, 0); 89411ae76e6SXin Long if (!req) 89511ae76e6SXin Long return NULL; 89611ae76e6SXin Long 89711ae76e6SXin Long result = ntohl(resp->result); 89811ae76e6SXin Long if (result != SCTP_STRRESET_PERFORMED) { 89911ae76e6SXin Long /* if in progress, do nothing but retransmit */ 90011ae76e6SXin Long if (result == SCTP_STRRESET_IN_PROGRESS) 90111ae76e6SXin Long return NULL; 90211ae76e6SXin Long else if (result == SCTP_STRRESET_DENIED) 90311ae76e6SXin Long flags = SCTP_STREAM_RESET_DENIED; 90411ae76e6SXin Long else 90511ae76e6SXin Long flags = SCTP_STREAM_RESET_FAILED; 90611ae76e6SXin Long } 90711ae76e6SXin Long 90811ae76e6SXin Long if (req->type == SCTP_PARAM_RESET_OUT_REQUEST) { 90911ae76e6SXin Long struct sctp_strreset_outreq *outreq; 9101da4fc97SXin Long __be16 *str_p; 91111ae76e6SXin Long 91211ae76e6SXin Long outreq = (struct sctp_strreset_outreq *)req; 913edb12f2dSXin Long str_p = outreq->list_of_streams; 91411ae76e6SXin Long nums = (ntohs(outreq->param_hdr.length) - sizeof(*outreq)) / 2; 91511ae76e6SXin Long 91611ae76e6SXin Long if (result == SCTP_STRRESET_PERFORMED) { 91711ae76e6SXin Long if (nums) { 91811ae76e6SXin Long for (i = 0; i < nums; i++) 91911ae76e6SXin Long stream->out[ntohs(str_p[i])].ssn = 0; 92011ae76e6SXin Long } else { 92111ae76e6SXin Long for (i = 0; i < stream->outcnt; i++) 92211ae76e6SXin Long stream->out[i].ssn = 0; 92311ae76e6SXin Long } 92411ae76e6SXin Long 92511ae76e6SXin Long flags = SCTP_STREAM_RESET_OUTGOING_SSN; 92611ae76e6SXin Long } 92711ae76e6SXin Long 92811ae76e6SXin Long for (i = 0; i < stream->outcnt; i++) 92911ae76e6SXin Long stream->out[i].state = SCTP_STREAM_OPEN; 93011ae76e6SXin Long 93111ae76e6SXin Long *evp = sctp_ulpevent_make_stream_reset_event(asoc, flags, 93211ae76e6SXin Long nums, str_p, GFP_ATOMIC); 93311ae76e6SXin Long } else if (req->type == SCTP_PARAM_RESET_IN_REQUEST) { 93411ae76e6SXin Long struct sctp_strreset_inreq *inreq; 9351da4fc97SXin Long __be16 *str_p; 93611ae76e6SXin Long 93711ae76e6SXin Long /* if the result is performed, it's impossible for inreq */ 93811ae76e6SXin Long if (result == SCTP_STRRESET_PERFORMED) 93911ae76e6SXin Long return NULL; 94011ae76e6SXin Long 94111ae76e6SXin Long inreq = (struct sctp_strreset_inreq *)req; 942edb12f2dSXin Long str_p = inreq->list_of_streams; 94311ae76e6SXin Long nums = (ntohs(inreq->param_hdr.length) - sizeof(*inreq)) / 2; 94411ae76e6SXin Long 94511ae76e6SXin Long *evp = sctp_ulpevent_make_stream_reset_event(asoc, flags, 94611ae76e6SXin Long nums, str_p, GFP_ATOMIC); 94711ae76e6SXin Long } else if (req->type == SCTP_PARAM_RESET_TSN_REQUEST) { 94811ae76e6SXin Long struct sctp_strreset_resptsn *resptsn; 94911ae76e6SXin Long __u32 stsn, rtsn; 95011ae76e6SXin Long 95111ae76e6SXin Long /* check for resptsn, as sctp_verify_reconf didn't do it*/ 95211ae76e6SXin Long if (ntohs(param.p->length) != sizeof(*resptsn)) 95311ae76e6SXin Long return NULL; 95411ae76e6SXin Long 95511ae76e6SXin Long resptsn = (struct sctp_strreset_resptsn *)resp; 95611ae76e6SXin Long stsn = ntohl(resptsn->senders_next_tsn); 95711ae76e6SXin Long rtsn = ntohl(resptsn->receivers_next_tsn); 95811ae76e6SXin Long 95911ae76e6SXin Long if (result == SCTP_STRRESET_PERFORMED) { 96011ae76e6SXin Long __u32 mtsn = sctp_tsnmap_get_max_tsn_seen( 96111ae76e6SXin Long &asoc->peer.tsn_map); 96211ae76e6SXin Long 96311ae76e6SXin Long sctp_ulpq_reasm_flushtsn(&asoc->ulpq, mtsn); 96411ae76e6SXin Long sctp_ulpq_abort_pd(&asoc->ulpq, GFP_ATOMIC); 96511ae76e6SXin Long 96611ae76e6SXin Long sctp_tsnmap_init(&asoc->peer.tsn_map, 96711ae76e6SXin Long SCTP_TSN_MAP_INITIAL, 96811ae76e6SXin Long stsn, GFP_ATOMIC); 96911ae76e6SXin Long 97011ae76e6SXin Long sctp_outq_free(&asoc->outqueue); 97111ae76e6SXin Long 97211ae76e6SXin Long asoc->next_tsn = rtsn; 97311ae76e6SXin Long asoc->ctsn_ack_point = asoc->next_tsn - 1; 97411ae76e6SXin Long asoc->adv_peer_ack_point = asoc->ctsn_ack_point; 97511ae76e6SXin Long 97611ae76e6SXin Long for (i = 0; i < stream->outcnt; i++) 97711ae76e6SXin Long stream->out[i].ssn = 0; 97811ae76e6SXin Long for (i = 0; i < stream->incnt; i++) 97911ae76e6SXin Long stream->in[i].ssn = 0; 98011ae76e6SXin Long } 98111ae76e6SXin Long 98211ae76e6SXin Long for (i = 0; i < stream->outcnt; i++) 98311ae76e6SXin Long stream->out[i].state = SCTP_STREAM_OPEN; 98411ae76e6SXin Long 98511ae76e6SXin Long *evp = sctp_ulpevent_make_assoc_reset_event(asoc, flags, 98611ae76e6SXin Long stsn, rtsn, GFP_ATOMIC); 98711ae76e6SXin Long } else if (req->type == SCTP_PARAM_RESET_ADD_OUT_STREAMS) { 98811ae76e6SXin Long struct sctp_strreset_addstrm *addstrm; 98911ae76e6SXin Long __u16 number; 99011ae76e6SXin Long 99111ae76e6SXin Long addstrm = (struct sctp_strreset_addstrm *)req; 99211ae76e6SXin Long nums = ntohs(addstrm->number_of_streams); 99311ae76e6SXin Long number = stream->outcnt - nums; 99411ae76e6SXin Long 99511ae76e6SXin Long if (result == SCTP_STRRESET_PERFORMED) 99611ae76e6SXin Long for (i = number; i < stream->outcnt; i++) 99711ae76e6SXin Long stream->out[i].state = SCTP_STREAM_OPEN; 99811ae76e6SXin Long else 99911ae76e6SXin Long stream->outcnt = number; 100011ae76e6SXin Long 100111ae76e6SXin Long *evp = sctp_ulpevent_make_stream_change_event(asoc, flags, 100211ae76e6SXin Long 0, nums, GFP_ATOMIC); 100311ae76e6SXin Long } else if (req->type == SCTP_PARAM_RESET_ADD_IN_STREAMS) { 100411ae76e6SXin Long struct sctp_strreset_addstrm *addstrm; 100511ae76e6SXin Long 100611ae76e6SXin Long /* if the result is performed, it's impossible for addstrm in 100711ae76e6SXin Long * request. 100811ae76e6SXin Long */ 100911ae76e6SXin Long if (result == SCTP_STRRESET_PERFORMED) 101011ae76e6SXin Long return NULL; 101111ae76e6SXin Long 101211ae76e6SXin Long addstrm = (struct sctp_strreset_addstrm *)req; 101311ae76e6SXin Long nums = ntohs(addstrm->number_of_streams); 101411ae76e6SXin Long 101511ae76e6SXin Long *evp = sctp_ulpevent_make_stream_change_event(asoc, flags, 101611ae76e6SXin Long nums, 0, GFP_ATOMIC); 101711ae76e6SXin Long } 101811ae76e6SXin Long 101911ae76e6SXin Long asoc->strreset_outstanding--; 102011ae76e6SXin Long asoc->strreset_outseq++; 102111ae76e6SXin Long 102211ae76e6SXin Long /* remove everything for this reconf request */ 102311ae76e6SXin Long if (!asoc->strreset_outstanding) { 102411ae76e6SXin Long t = asoc->strreset_chunk->transport; 102511ae76e6SXin Long if (del_timer(&t->reconf_timer)) 102611ae76e6SXin Long sctp_transport_put(t); 102711ae76e6SXin Long 102811ae76e6SXin Long sctp_chunk_put(asoc->strreset_chunk); 102911ae76e6SXin Long asoc->strreset_chunk = NULL; 103011ae76e6SXin Long } 103111ae76e6SXin Long 103211ae76e6SXin Long return NULL; 103311ae76e6SXin Long } 1034