147505b8bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2a8386317SXin Long /* SCTP kernel implementation 3a8386317SXin Long * (C) Copyright IBM Corp. 2001, 2004 4a8386317SXin Long * Copyright (c) 1999-2000 Cisco, Inc. 5a8386317SXin Long * Copyright (c) 1999-2001 Motorola, Inc. 6a8386317SXin Long * Copyright (c) 2001 Intel Corp. 7a8386317SXin Long * 8a8386317SXin Long * This file is part of the SCTP kernel implementation 9a8386317SXin Long * 10fae8b6f4SXin Long * This file contains sctp stream maniuplation primitives and helpers. 11a8386317SXin Long * 12a8386317SXin Long * Please send any bug reports or fixes you make to the 13a8386317SXin Long * email address(es): 14a8386317SXin Long * lksctp developers <linux-sctp@vger.kernel.org> 15a8386317SXin Long * 16a8386317SXin Long * Written or modified by: 17a8386317SXin Long * Xin Long <lucien.xin@gmail.com> 18a8386317SXin Long */ 19a8386317SXin Long 205bbbbe32SMarcelo Ricardo Leitner #include <linux/list.h> 21a8386317SXin Long #include <net/sctp/sctp.h> 227f9d68acSXin Long #include <net/sctp/sm.h> 235bbbbe32SMarcelo Ricardo Leitner #include <net/sctp/stream_sched.h> 245bbbbe32SMarcelo Ricardo Leitner 255bbbbe32SMarcelo Ricardo Leitner /* Migrates chunks from stream queues to new stream queues if needed, 265bbbbe32SMarcelo Ricardo Leitner * but not across associations. Also, removes those chunks to streams 275bbbbe32SMarcelo Ricardo Leitner * higher than the new max. 285bbbbe32SMarcelo Ricardo Leitner */ 295bbbbe32SMarcelo Ricardo Leitner static void sctp_stream_outq_migrate(struct sctp_stream *stream, 305bbbbe32SMarcelo Ricardo Leitner struct sctp_stream *new, __u16 outcnt) 315bbbbe32SMarcelo Ricardo Leitner { 325bbbbe32SMarcelo Ricardo Leitner struct sctp_association *asoc; 335bbbbe32SMarcelo Ricardo Leitner struct sctp_chunk *ch, *temp; 345bbbbe32SMarcelo Ricardo Leitner struct sctp_outq *outq; 355bbbbe32SMarcelo Ricardo Leitner int i; 365bbbbe32SMarcelo Ricardo Leitner 375bbbbe32SMarcelo Ricardo Leitner asoc = container_of(stream, struct sctp_association, stream); 385bbbbe32SMarcelo Ricardo Leitner outq = &asoc->outqueue; 395bbbbe32SMarcelo Ricardo Leitner 405bbbbe32SMarcelo Ricardo Leitner list_for_each_entry_safe(ch, temp, &outq->out_chunk_list, list) { 415bbbbe32SMarcelo Ricardo Leitner __u16 sid = sctp_chunk_stream_no(ch); 425bbbbe32SMarcelo Ricardo Leitner 435bbbbe32SMarcelo Ricardo Leitner if (sid < outcnt) 445bbbbe32SMarcelo Ricardo Leitner continue; 455bbbbe32SMarcelo Ricardo Leitner 465bbbbe32SMarcelo Ricardo Leitner sctp_sched_dequeue_common(outq, ch); 475bbbbe32SMarcelo Ricardo Leitner /* No need to call dequeue_done here because 485bbbbe32SMarcelo Ricardo Leitner * the chunks are not scheduled by now. 495bbbbe32SMarcelo Ricardo Leitner */ 505bbbbe32SMarcelo Ricardo Leitner 515bbbbe32SMarcelo Ricardo Leitner /* Mark as failed send. */ 5208f46070SXin Long sctp_chunk_fail(ch, (__force __u32)SCTP_ERROR_INV_STRM); 535bbbbe32SMarcelo Ricardo Leitner if (asoc->peer.prsctp_capable && 545bbbbe32SMarcelo Ricardo Leitner SCTP_PR_PRIO_ENABLED(ch->sinfo.sinfo_flags)) 555bbbbe32SMarcelo Ricardo Leitner asoc->sent_cnt_removable--; 565bbbbe32SMarcelo Ricardo Leitner 575bbbbe32SMarcelo Ricardo Leitner sctp_chunk_free(ch); 585bbbbe32SMarcelo Ricardo Leitner } 595bbbbe32SMarcelo Ricardo Leitner 605bbbbe32SMarcelo Ricardo Leitner if (new) { 615bbbbe32SMarcelo Ricardo Leitner /* Here we actually move the old ext stuff into the new 625bbbbe32SMarcelo Ricardo Leitner * buffer, because we want to keep it. Then 635bbbbe32SMarcelo Ricardo Leitner * sctp_stream_update will swap ->out pointers. 645bbbbe32SMarcelo Ricardo Leitner */ 655bbbbe32SMarcelo Ricardo Leitner for (i = 0; i < outcnt; i++) { 660d493b4dSKonstantin Khorenko kfree(SCTP_SO(new, i)->ext); 670d493b4dSKonstantin Khorenko SCTP_SO(new, i)->ext = SCTP_SO(stream, i)->ext; 680d493b4dSKonstantin Khorenko SCTP_SO(stream, i)->ext = NULL; 695bbbbe32SMarcelo Ricardo Leitner } 705bbbbe32SMarcelo Ricardo Leitner } 715bbbbe32SMarcelo Ricardo Leitner 72af98c5a7SXin Long for (i = outcnt; i < stream->outcnt; i++) { 730d493b4dSKonstantin Khorenko kfree(SCTP_SO(stream, i)->ext); 74af98c5a7SXin Long SCTP_SO(stream, i)->ext = NULL; 75af98c5a7SXin Long } 765bbbbe32SMarcelo Ricardo Leitner } 77a8386317SXin Long 78e090abd0SMarcelo Ricardo Leitner static int sctp_stream_alloc_out(struct sctp_stream *stream, __u16 outcnt, 79e090abd0SMarcelo Ricardo Leitner gfp_t gfp) 80e090abd0SMarcelo Ricardo Leitner { 812075e50cSKent Overstreet int ret; 82e090abd0SMarcelo Ricardo Leitner 832075e50cSKent Overstreet if (outcnt <= stream->outcnt) 842075e50cSKent Overstreet return 0; 85e090abd0SMarcelo Ricardo Leitner 862075e50cSKent Overstreet ret = genradix_prealloc(&stream->out, outcnt, gfp); 872075e50cSKent Overstreet if (ret) 882075e50cSKent Overstreet return ret; 89cfe4bd7aSXin Long 902075e50cSKent Overstreet stream->outcnt = outcnt; 91e090abd0SMarcelo Ricardo Leitner return 0; 92e090abd0SMarcelo Ricardo Leitner } 93e090abd0SMarcelo Ricardo Leitner 941fdb8d8fSMarcelo Ricardo Leitner static int sctp_stream_alloc_in(struct sctp_stream *stream, __u16 incnt, 951fdb8d8fSMarcelo Ricardo Leitner gfp_t gfp) 961fdb8d8fSMarcelo Ricardo Leitner { 972075e50cSKent Overstreet int ret; 981fdb8d8fSMarcelo Ricardo Leitner 992075e50cSKent Overstreet if (incnt <= stream->incnt) 1002075e50cSKent Overstreet return 0; 1011fdb8d8fSMarcelo Ricardo Leitner 1022075e50cSKent Overstreet ret = genradix_prealloc(&stream->in, incnt, gfp); 1032075e50cSKent Overstreet if (ret) 1042075e50cSKent Overstreet return ret; 1051fdb8d8fSMarcelo Ricardo Leitner 1062075e50cSKent Overstreet stream->incnt = incnt; 1071fdb8d8fSMarcelo Ricardo Leitner return 0; 1081fdb8d8fSMarcelo Ricardo Leitner } 1091fdb8d8fSMarcelo Ricardo Leitner 110ff356414SXin Long int sctp_stream_init(struct sctp_stream *stream, __u16 outcnt, __u16 incnt, 111ff356414SXin Long gfp_t gfp) 112a8386317SXin Long { 1135bbbbe32SMarcelo Ricardo Leitner struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream); 1145bbbbe32SMarcelo Ricardo Leitner int i, ret = 0; 1153dbcc105SXin Long 1161ae2eaaaSMarcelo Ricardo Leitner gfp |= __GFP_NOWARN; 1171ae2eaaaSMarcelo Ricardo Leitner 1183dbcc105SXin Long /* Initial stream->out size may be very big, so free it and alloc 1191ae2eaaaSMarcelo Ricardo Leitner * a new one with new outcnt to save memory if needed. 1203dbcc105SXin Long */ 1211ae2eaaaSMarcelo Ricardo Leitner if (outcnt == stream->outcnt) 1221ae2eaaaSMarcelo Ricardo Leitner goto in; 1231ae2eaaaSMarcelo Ricardo Leitner 1245bbbbe32SMarcelo Ricardo Leitner /* Filter out chunks queued on streams that won't exist anymore */ 1255bbbbe32SMarcelo Ricardo Leitner sched->unsched_all(stream); 1265bbbbe32SMarcelo Ricardo Leitner sctp_stream_outq_migrate(stream, NULL, outcnt); 1275bbbbe32SMarcelo Ricardo Leitner sched->sched_all(stream); 1285bbbbe32SMarcelo Ricardo Leitner 12979d08951SMarcelo Ricardo Leitner ret = sctp_stream_alloc_out(stream, outcnt, gfp); 13079d08951SMarcelo Ricardo Leitner if (ret) 13179d08951SMarcelo Ricardo Leitner goto out; 1323dbcc105SXin Long 1333dbcc105SXin Long for (i = 0; i < stream->outcnt; i++) 13405364ca0SKonstantin Khorenko SCTP_SO(stream, i)->state = SCTP_STREAM_OPEN; 1353dbcc105SXin Long 1361ae2eaaaSMarcelo Ricardo Leitner in: 1370c3f6f65SXin Long sctp_stream_interleave_init(stream); 138ff356414SXin Long if (!incnt) 1395bbbbe32SMarcelo Ricardo Leitner goto out; 140ff356414SXin Long 14179d08951SMarcelo Ricardo Leitner ret = sctp_stream_alloc_in(stream, incnt, gfp); 14279d08951SMarcelo Ricardo Leitner if (ret) { 1435bbbbe32SMarcelo Ricardo Leitner sched->free(stream); 1442075e50cSKent Overstreet genradix_free(&stream->out); 14579d08951SMarcelo Ricardo Leitner stream->outcnt = 0; 14679d08951SMarcelo Ricardo Leitner goto out; 14779d08951SMarcelo Ricardo Leitner } 14879d08951SMarcelo Ricardo Leitner 1495bbbbe32SMarcelo Ricardo Leitner out: 1505bbbbe32SMarcelo Ricardo Leitner return ret; 151a8386317SXin Long } 152a8386317SXin Long 153f952be79SMarcelo Ricardo Leitner int sctp_stream_init_ext(struct sctp_stream *stream, __u16 sid) 154f952be79SMarcelo Ricardo Leitner { 155f952be79SMarcelo Ricardo Leitner struct sctp_stream_out_ext *soute; 1564d141581SMarcelo Ricardo Leitner int ret; 157f952be79SMarcelo Ricardo Leitner 158f952be79SMarcelo Ricardo Leitner soute = kzalloc(sizeof(*soute), GFP_KERNEL); 159f952be79SMarcelo Ricardo Leitner if (!soute) 160f952be79SMarcelo Ricardo Leitner return -ENOMEM; 16105364ca0SKonstantin Khorenko SCTP_SO(stream, sid)->ext = soute; 162f952be79SMarcelo Ricardo Leitner 1634d141581SMarcelo Ricardo Leitner ret = sctp_sched_init_sid(stream, sid, GFP_KERNEL); 1644d141581SMarcelo Ricardo Leitner if (ret) { 1654d141581SMarcelo Ricardo Leitner kfree(SCTP_SO(stream, sid)->ext); 1664d141581SMarcelo Ricardo Leitner SCTP_SO(stream, sid)->ext = NULL; 1674d141581SMarcelo Ricardo Leitner } 1684d141581SMarcelo Ricardo Leitner 1694d141581SMarcelo Ricardo Leitner return ret; 170f952be79SMarcelo Ricardo Leitner } 171f952be79SMarcelo Ricardo Leitner 172a8386317SXin Long void sctp_stream_free(struct sctp_stream *stream) 173a8386317SXin Long { 1745bbbbe32SMarcelo Ricardo Leitner struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream); 175f952be79SMarcelo Ricardo Leitner int i; 176f952be79SMarcelo Ricardo Leitner 1775bbbbe32SMarcelo Ricardo Leitner sched->free(stream); 178f952be79SMarcelo Ricardo Leitner for (i = 0; i < stream->outcnt; i++) 17905364ca0SKonstantin Khorenko kfree(SCTP_SO(stream, i)->ext); 1802075e50cSKent Overstreet genradix_free(&stream->out); 1812075e50cSKent Overstreet genradix_free(&stream->in); 182a8386317SXin Long } 183a8386317SXin Long 184a8386317SXin Long void sctp_stream_clear(struct sctp_stream *stream) 185a8386317SXin Long { 186a8386317SXin Long int i; 187a8386317SXin Long 188107e2425SXin Long for (i = 0; i < stream->outcnt; i++) { 18905364ca0SKonstantin Khorenko SCTP_SO(stream, i)->mid = 0; 19005364ca0SKonstantin Khorenko SCTP_SO(stream, i)->mid_uo = 0; 191107e2425SXin Long } 192a8386317SXin Long 193a8386317SXin Long for (i = 0; i < stream->incnt; i++) 19405364ca0SKonstantin Khorenko SCTP_SI(stream, i)->mid = 0; 195a8386317SXin Long } 1967f9d68acSXin Long 197cee360abSXin Long void sctp_stream_update(struct sctp_stream *stream, struct sctp_stream *new) 198cee360abSXin Long { 1995bbbbe32SMarcelo Ricardo Leitner struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream); 2005bbbbe32SMarcelo Ricardo Leitner 2015bbbbe32SMarcelo Ricardo Leitner sched->unsched_all(stream); 2025bbbbe32SMarcelo Ricardo Leitner sctp_stream_outq_migrate(stream, new, new->outcnt); 203cee360abSXin Long sctp_stream_free(stream); 204cee360abSXin Long 205cee360abSXin Long stream->out = new->out; 206cee360abSXin Long stream->in = new->in; 207cee360abSXin Long stream->outcnt = new->outcnt; 208cee360abSXin Long stream->incnt = new->incnt; 209cee360abSXin Long 2105bbbbe32SMarcelo Ricardo Leitner sched->sched_all(stream); 2115bbbbe32SMarcelo Ricardo Leitner 2122075e50cSKent Overstreet new->out.tree.root = NULL; 2132075e50cSKent Overstreet new->in.tree.root = NULL; 2146a9a27d5SXin Long new->outcnt = 0; 2156a9a27d5SXin Long new->incnt = 0; 216cee360abSXin Long } 217cee360abSXin Long 2187f9d68acSXin Long static int sctp_send_reconf(struct sctp_association *asoc, 2197f9d68acSXin Long struct sctp_chunk *chunk) 2207f9d68acSXin Long { 2217f9d68acSXin Long struct net *net = sock_net(asoc->base.sk); 2227f9d68acSXin Long int retval = 0; 2237f9d68acSXin Long 2247f9d68acSXin Long retval = sctp_primitive_RECONF(net, asoc, chunk); 2257f9d68acSXin Long if (retval) 2267f9d68acSXin Long sctp_chunk_free(chunk); 2277f9d68acSXin Long 2287f9d68acSXin Long return retval; 2297f9d68acSXin Long } 2307f9d68acSXin Long 231d570a59cSXin Long static bool sctp_stream_outq_is_empty(struct sctp_stream *stream, 232d570a59cSXin Long __u16 str_nums, __be16 *str_list) 233d570a59cSXin Long { 234d570a59cSXin Long struct sctp_association *asoc; 235d570a59cSXin Long __u16 i; 236d570a59cSXin Long 237d570a59cSXin Long asoc = container_of(stream, struct sctp_association, stream); 238d570a59cSXin Long if (!asoc->outqueue.out_qlen) 239d570a59cSXin Long return true; 240d570a59cSXin Long 241d570a59cSXin Long if (!str_nums) 242d570a59cSXin Long return false; 243d570a59cSXin Long 244d570a59cSXin Long for (i = 0; i < str_nums; i++) { 245d570a59cSXin Long __u16 sid = ntohs(str_list[i]); 246d570a59cSXin Long 24705364ca0SKonstantin Khorenko if (SCTP_SO(stream, sid)->ext && 24805364ca0SKonstantin Khorenko !list_empty(&SCTP_SO(stream, sid)->ext->outq)) 249d570a59cSXin Long return false; 250d570a59cSXin Long } 251d570a59cSXin Long 252d570a59cSXin Long return true; 253d570a59cSXin Long } 254d570a59cSXin Long 2557f9d68acSXin Long int sctp_send_reset_streams(struct sctp_association *asoc, 2567f9d68acSXin Long struct sctp_reset_streams *params) 2577f9d68acSXin Long { 258cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 2597f9d68acSXin Long __u16 i, str_nums, *str_list; 2607f9d68acSXin Long struct sctp_chunk *chunk; 2617f9d68acSXin Long int retval = -EINVAL; 2621da4fc97SXin Long __be16 *nstr_list; 2637f9d68acSXin Long bool out, in; 2647f9d68acSXin Long 2657f9d68acSXin Long if (!asoc->peer.reconf_capable || 2667f9d68acSXin Long !(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ)) { 2677f9d68acSXin Long retval = -ENOPROTOOPT; 2687f9d68acSXin Long goto out; 2697f9d68acSXin Long } 2707f9d68acSXin Long 2717f9d68acSXin Long if (asoc->strreset_outstanding) { 2727f9d68acSXin Long retval = -EINPROGRESS; 2737f9d68acSXin Long goto out; 2747f9d68acSXin Long } 2757f9d68acSXin Long 2767f9d68acSXin Long out = params->srs_flags & SCTP_STREAM_RESET_OUTGOING; 2777f9d68acSXin Long in = params->srs_flags & SCTP_STREAM_RESET_INCOMING; 2787f9d68acSXin Long if (!out && !in) 2797f9d68acSXin Long goto out; 2807f9d68acSXin Long 2817f9d68acSXin Long str_nums = params->srs_number_streams; 2827f9d68acSXin Long str_list = params->srs_stream_list; 283423852f8SXin Long if (str_nums) { 284423852f8SXin Long int param_len = 0; 285423852f8SXin Long 286423852f8SXin Long if (out) { 2877f9d68acSXin Long for (i = 0; i < str_nums; i++) 2887f9d68acSXin Long if (str_list[i] >= stream->outcnt) 2897f9d68acSXin Long goto out; 2907f9d68acSXin Long 291423852f8SXin Long param_len = str_nums * sizeof(__u16) + 292423852f8SXin Long sizeof(struct sctp_strreset_outreq); 293423852f8SXin Long } 294423852f8SXin Long 295423852f8SXin Long if (in) { 2967f9d68acSXin Long for (i = 0; i < str_nums; i++) 2977f9d68acSXin Long if (str_list[i] >= stream->incnt) 2987f9d68acSXin Long goto out; 2997f9d68acSXin Long 300423852f8SXin Long param_len += str_nums * sizeof(__u16) + 301423852f8SXin Long sizeof(struct sctp_strreset_inreq); 302423852f8SXin Long } 303423852f8SXin Long 304423852f8SXin Long if (param_len > SCTP_MAX_CHUNK_LEN - 305423852f8SXin Long sizeof(struct sctp_reconf_chunk)) 306423852f8SXin Long goto out; 307423852f8SXin Long } 308423852f8SXin Long 3091da4fc97SXin Long nstr_list = kcalloc(str_nums, sizeof(__be16), GFP_KERNEL); 3101da4fc97SXin Long if (!nstr_list) { 3111da4fc97SXin Long retval = -ENOMEM; 3121da4fc97SXin Long goto out; 3131da4fc97SXin Long } 31416e1a919SXin Long 31516e1a919SXin Long for (i = 0; i < str_nums; i++) 3161da4fc97SXin Long nstr_list[i] = htons(str_list[i]); 3171da4fc97SXin Long 318d570a59cSXin Long if (out && !sctp_stream_outq_is_empty(stream, str_nums, nstr_list)) { 319d570a59cSXin Long retval = -EAGAIN; 320d570a59cSXin Long goto out; 321d570a59cSXin Long } 322d570a59cSXin Long 3231da4fc97SXin Long chunk = sctp_make_strreset_req(asoc, str_nums, nstr_list, out, in); 3241da4fc97SXin Long 3251da4fc97SXin Long kfree(nstr_list); 32616e1a919SXin Long 327119aecbaSXin Long if (!chunk) { 328119aecbaSXin Long retval = -ENOMEM; 3297f9d68acSXin Long goto out; 330119aecbaSXin Long } 3317f9d68acSXin Long 3327f9d68acSXin Long if (out) { 3337f9d68acSXin Long if (str_nums) 3347f9d68acSXin Long for (i = 0; i < str_nums; i++) 33505364ca0SKonstantin Khorenko SCTP_SO(stream, str_list[i])->state = 3367f9d68acSXin Long SCTP_STREAM_CLOSED; 3377f9d68acSXin Long else 3387f9d68acSXin Long for (i = 0; i < stream->outcnt; i++) 33905364ca0SKonstantin Khorenko SCTP_SO(stream, i)->state = SCTP_STREAM_CLOSED; 3407f9d68acSXin Long } 3417f9d68acSXin Long 3427f9d68acSXin Long asoc->strreset_chunk = chunk; 3437f9d68acSXin Long sctp_chunk_hold(asoc->strreset_chunk); 3447f9d68acSXin Long 3457f9d68acSXin Long retval = sctp_send_reconf(asoc, chunk); 3467f9d68acSXin Long if (retval) { 3477f9d68acSXin Long sctp_chunk_put(asoc->strreset_chunk); 3487f9d68acSXin Long asoc->strreset_chunk = NULL; 349119aecbaSXin Long if (!out) 350119aecbaSXin Long goto out; 351119aecbaSXin Long 352119aecbaSXin Long if (str_nums) 353119aecbaSXin Long for (i = 0; i < str_nums; i++) 35405364ca0SKonstantin Khorenko SCTP_SO(stream, str_list[i])->state = 355119aecbaSXin Long SCTP_STREAM_OPEN; 356119aecbaSXin Long else 357119aecbaSXin Long for (i = 0; i < stream->outcnt; i++) 35805364ca0SKonstantin Khorenko SCTP_SO(stream, i)->state = SCTP_STREAM_OPEN; 359119aecbaSXin Long 360119aecbaSXin Long goto out; 3617f9d68acSXin Long } 3627f9d68acSXin Long 363119aecbaSXin Long asoc->strreset_outstanding = out + in; 364119aecbaSXin Long 3657f9d68acSXin Long out: 3667f9d68acSXin Long return retval; 3677f9d68acSXin Long } 368a92ce1a4SXin Long 369a92ce1a4SXin Long int sctp_send_reset_assoc(struct sctp_association *asoc) 370a92ce1a4SXin Long { 371cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 372a92ce1a4SXin Long struct sctp_chunk *chunk = NULL; 373a92ce1a4SXin Long int retval; 374a92ce1a4SXin Long __u16 i; 375a92ce1a4SXin Long 376a92ce1a4SXin Long if (!asoc->peer.reconf_capable || 377a92ce1a4SXin Long !(asoc->strreset_enable & SCTP_ENABLE_RESET_ASSOC_REQ)) 378a92ce1a4SXin Long return -ENOPROTOOPT; 379a92ce1a4SXin Long 380a92ce1a4SXin Long if (asoc->strreset_outstanding) 381a92ce1a4SXin Long return -EINPROGRESS; 382a92ce1a4SXin Long 3835c6144a0SXin Long if (!sctp_outq_is_empty(&asoc->outqueue)) 3845c6144a0SXin Long return -EAGAIN; 3855c6144a0SXin Long 386a92ce1a4SXin Long chunk = sctp_make_strreset_tsnreq(asoc); 387a92ce1a4SXin Long if (!chunk) 388a92ce1a4SXin Long return -ENOMEM; 389a92ce1a4SXin Long 390a92ce1a4SXin Long /* Block further xmit of data until this request is completed */ 391cee360abSXin Long for (i = 0; i < stream->outcnt; i++) 39205364ca0SKonstantin Khorenko SCTP_SO(stream, i)->state = SCTP_STREAM_CLOSED; 393a92ce1a4SXin Long 394a92ce1a4SXin Long asoc->strreset_chunk = chunk; 395a92ce1a4SXin Long sctp_chunk_hold(asoc->strreset_chunk); 396a92ce1a4SXin Long 397a92ce1a4SXin Long retval = sctp_send_reconf(asoc, chunk); 398a92ce1a4SXin Long if (retval) { 399a92ce1a4SXin Long sctp_chunk_put(asoc->strreset_chunk); 400a92ce1a4SXin Long asoc->strreset_chunk = NULL; 401a92ce1a4SXin Long 402cee360abSXin Long for (i = 0; i < stream->outcnt; i++) 40305364ca0SKonstantin Khorenko SCTP_SO(stream, i)->state = SCTP_STREAM_OPEN; 404a92ce1a4SXin Long 405a92ce1a4SXin Long return retval; 406a92ce1a4SXin Long } 407a92ce1a4SXin Long 408a92ce1a4SXin Long asoc->strreset_outstanding = 1; 409a92ce1a4SXin Long 410a92ce1a4SXin Long return 0; 411a92ce1a4SXin Long } 412242bd2d5SXin Long 413242bd2d5SXin Long int sctp_send_add_streams(struct sctp_association *asoc, 414242bd2d5SXin Long struct sctp_add_streams *params) 415242bd2d5SXin Long { 416cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 417242bd2d5SXin Long struct sctp_chunk *chunk = NULL; 418dc82673fSWei Yongjun int retval; 419242bd2d5SXin Long __u32 outcnt, incnt; 420242bd2d5SXin Long __u16 out, in; 421242bd2d5SXin Long 422242bd2d5SXin Long if (!asoc->peer.reconf_capable || 423242bd2d5SXin Long !(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ)) { 424242bd2d5SXin Long retval = -ENOPROTOOPT; 425242bd2d5SXin Long goto out; 426242bd2d5SXin Long } 427242bd2d5SXin Long 428242bd2d5SXin Long if (asoc->strreset_outstanding) { 429242bd2d5SXin Long retval = -EINPROGRESS; 430242bd2d5SXin Long goto out; 431242bd2d5SXin Long } 432242bd2d5SXin Long 433242bd2d5SXin Long out = params->sas_outstrms; 434242bd2d5SXin Long in = params->sas_instrms; 435242bd2d5SXin Long outcnt = stream->outcnt + out; 436242bd2d5SXin Long incnt = stream->incnt + in; 437242bd2d5SXin Long if (outcnt > SCTP_MAX_STREAM || incnt > SCTP_MAX_STREAM || 438242bd2d5SXin Long (!out && !in)) { 439242bd2d5SXin Long retval = -EINVAL; 440242bd2d5SXin Long goto out; 441242bd2d5SXin Long } 442242bd2d5SXin Long 443242bd2d5SXin Long if (out) { 444e090abd0SMarcelo Ricardo Leitner retval = sctp_stream_alloc_out(stream, outcnt, GFP_KERNEL); 445e090abd0SMarcelo Ricardo Leitner if (retval) 446242bd2d5SXin Long goto out; 447242bd2d5SXin Long } 448242bd2d5SXin Long 449242bd2d5SXin Long chunk = sctp_make_strreset_addstrm(asoc, out, in); 450dc82673fSWei Yongjun if (!chunk) { 451dc82673fSWei Yongjun retval = -ENOMEM; 452242bd2d5SXin Long goto out; 453dc82673fSWei Yongjun } 454242bd2d5SXin Long 455242bd2d5SXin Long asoc->strreset_chunk = chunk; 456242bd2d5SXin Long sctp_chunk_hold(asoc->strreset_chunk); 457242bd2d5SXin Long 458242bd2d5SXin Long retval = sctp_send_reconf(asoc, chunk); 459242bd2d5SXin Long if (retval) { 460242bd2d5SXin Long sctp_chunk_put(asoc->strreset_chunk); 461242bd2d5SXin Long asoc->strreset_chunk = NULL; 462242bd2d5SXin Long goto out; 463242bd2d5SXin Long } 464242bd2d5SXin Long 465242bd2d5SXin Long asoc->strreset_outstanding = !!out + !!in; 466242bd2d5SXin Long 467242bd2d5SXin Long out: 468242bd2d5SXin Long return retval; 469242bd2d5SXin Long } 47081054476SXin Long 4713c918704SXin Long static struct sctp_paramhdr *sctp_chunk_lookup_strreset_param( 4721da4fc97SXin Long struct sctp_association *asoc, __be32 resp_seq, 47350a41591SXin Long __be16 type) 47481054476SXin Long { 47581054476SXin Long struct sctp_chunk *chunk = asoc->strreset_chunk; 47681054476SXin Long struct sctp_reconf_chunk *hdr; 47781054476SXin Long union sctp_params param; 47881054476SXin Long 47950a41591SXin Long if (!chunk) 48081054476SXin Long return NULL; 48181054476SXin Long 48281054476SXin Long hdr = (struct sctp_reconf_chunk *)chunk->chunk_hdr; 48381054476SXin Long sctp_walk_params(param, hdr, params) { 48481054476SXin Long /* sctp_strreset_tsnreq is actually the basic structure 48581054476SXin Long * of all stream reconf params, so it's safe to use it 48681054476SXin Long * to access request_seq. 48781054476SXin Long */ 48881054476SXin Long struct sctp_strreset_tsnreq *req = param.v; 48981054476SXin Long 49050a41591SXin Long if ((!resp_seq || req->request_seq == resp_seq) && 49150a41591SXin Long (!type || type == req->param_hdr.type)) 49281054476SXin Long return param.v; 49381054476SXin Long } 49481054476SXin Long 49581054476SXin Long return NULL; 49681054476SXin Long } 49781054476SXin Long 498e4dc99c7SXin Long static void sctp_update_strreset_result(struct sctp_association *asoc, 499e4dc99c7SXin Long __u32 result) 500e4dc99c7SXin Long { 501e4dc99c7SXin Long asoc->strreset_result[1] = asoc->strreset_result[0]; 502e4dc99c7SXin Long asoc->strreset_result[0] = result; 503e4dc99c7SXin Long } 504e4dc99c7SXin Long 50581054476SXin Long struct sctp_chunk *sctp_process_strreset_outreq( 50681054476SXin Long struct sctp_association *asoc, 50781054476SXin Long union sctp_params param, 50881054476SXin Long struct sctp_ulpevent **evp) 50981054476SXin Long { 51081054476SXin Long struct sctp_strreset_outreq *outreq = param.v; 511cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 51281054476SXin Long __u32 result = SCTP_STRRESET_DENIED; 5131da4fc97SXin Long __be16 *str_p = NULL; 51481054476SXin Long __u32 request_seq; 5152e6dc4d9SXin Long __u16 i, nums; 51681054476SXin Long 51781054476SXin Long request_seq = ntohl(outreq->request_seq); 51881054476SXin Long 51981054476SXin Long if (ntohl(outreq->send_reset_at_tsn) > 52081054476SXin Long sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map)) { 52181054476SXin Long result = SCTP_STRRESET_IN_PROGRESS; 522e4dc99c7SXin Long goto err; 52381054476SXin Long } 52481054476SXin Long 525e4dc99c7SXin Long if (TSN_lt(asoc->strreset_inseq, request_seq) || 526e4dc99c7SXin Long TSN_lt(request_seq, asoc->strreset_inseq - 2)) { 52781054476SXin Long result = SCTP_STRRESET_ERR_BAD_SEQNO; 528e4dc99c7SXin Long goto err; 529e4dc99c7SXin Long } else if (TSN_lt(request_seq, asoc->strreset_inseq)) { 530e4dc99c7SXin Long i = asoc->strreset_inseq - request_seq - 1; 531e4dc99c7SXin Long result = asoc->strreset_result[i]; 532e4dc99c7SXin Long goto err; 53381054476SXin Long } 534e4dc99c7SXin Long asoc->strreset_inseq++; 53581054476SXin Long 53681054476SXin Long /* Check strreset_enable after inseq inc, as sender cannot tell 53781054476SXin Long * the peer doesn't enable strreset after receiving response with 53881054476SXin Long * result denied, as well as to keep consistent with bsd. 53981054476SXin Long */ 54081054476SXin Long if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ)) 54181054476SXin Long goto out; 54281054476SXin Long 5432e6dc4d9SXin Long nums = (ntohs(param.p->length) - sizeof(*outreq)) / sizeof(__u16); 5442e6dc4d9SXin Long str_p = outreq->list_of_streams; 5452e6dc4d9SXin Long for (i = 0; i < nums; i++) { 5462e6dc4d9SXin Long if (ntohs(str_p[i]) >= stream->incnt) { 5472e6dc4d9SXin Long result = SCTP_STRRESET_ERR_WRONG_SSN; 5482e6dc4d9SXin Long goto out; 5492e6dc4d9SXin Long } 5502e6dc4d9SXin Long } 5512e6dc4d9SXin Long 55281054476SXin Long if (asoc->strreset_chunk) { 55350a41591SXin Long if (!sctp_chunk_lookup_strreset_param( 55450a41591SXin Long asoc, outreq->response_seq, 55550a41591SXin Long SCTP_PARAM_RESET_IN_REQUEST)) { 55681054476SXin Long /* same process with outstanding isn't 0 */ 55781054476SXin Long result = SCTP_STRRESET_ERR_IN_PROGRESS; 55881054476SXin Long goto out; 55981054476SXin Long } 56081054476SXin Long 56181054476SXin Long asoc->strreset_outstanding--; 56281054476SXin Long asoc->strreset_outseq++; 56381054476SXin Long 56481054476SXin Long if (!asoc->strreset_outstanding) { 56550a41591SXin Long struct sctp_transport *t; 56650a41591SXin Long 56781054476SXin Long t = asoc->strreset_chunk->transport; 56881054476SXin Long if (del_timer(&t->reconf_timer)) 56981054476SXin Long sctp_transport_put(t); 57081054476SXin Long 57181054476SXin Long sctp_chunk_put(asoc->strreset_chunk); 57281054476SXin Long asoc->strreset_chunk = NULL; 57381054476SXin Long } 57481054476SXin Long } 57581054476SXin Long 5762e6dc4d9SXin Long if (nums) 57781054476SXin Long for (i = 0; i < nums; i++) 57805364ca0SKonstantin Khorenko SCTP_SI(stream, ntohs(str_p[i]))->mid = 0; 5792e6dc4d9SXin Long else 58081054476SXin Long for (i = 0; i < stream->incnt; i++) 58105364ca0SKonstantin Khorenko SCTP_SI(stream, i)->mid = 0; 58281054476SXin Long 58381054476SXin Long result = SCTP_STRRESET_PERFORMED; 58481054476SXin Long 58581054476SXin Long *evp = sctp_ulpevent_make_stream_reset_event(asoc, 5862e6dc4d9SXin Long SCTP_STREAM_RESET_INCOMING_SSN, nums, str_p, GFP_ATOMIC); 58781054476SXin Long 58881054476SXin Long out: 589e4dc99c7SXin Long sctp_update_strreset_result(asoc, result); 590e4dc99c7SXin Long err: 59181054476SXin Long return sctp_make_strreset_resp(asoc, result, request_seq); 59281054476SXin Long } 59316e1a919SXin Long 59416e1a919SXin Long struct sctp_chunk *sctp_process_strreset_inreq( 59516e1a919SXin Long struct sctp_association *asoc, 59616e1a919SXin Long union sctp_params param, 59716e1a919SXin Long struct sctp_ulpevent **evp) 59816e1a919SXin Long { 59916e1a919SXin Long struct sctp_strreset_inreq *inreq = param.v; 600cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 60116e1a919SXin Long __u32 result = SCTP_STRRESET_DENIED; 60216e1a919SXin Long struct sctp_chunk *chunk = NULL; 60316e1a919SXin Long __u32 request_seq; 6041da4fc97SXin Long __u16 i, nums; 6051da4fc97SXin Long __be16 *str_p; 60616e1a919SXin Long 60716e1a919SXin Long request_seq = ntohl(inreq->request_seq); 608d0f025e6SXin Long if (TSN_lt(asoc->strreset_inseq, request_seq) || 609d0f025e6SXin Long TSN_lt(request_seq, asoc->strreset_inseq - 2)) { 61016e1a919SXin Long result = SCTP_STRRESET_ERR_BAD_SEQNO; 611d0f025e6SXin Long goto err; 612d0f025e6SXin Long } else if (TSN_lt(request_seq, asoc->strreset_inseq)) { 613d0f025e6SXin Long i = asoc->strreset_inseq - request_seq - 1; 614d0f025e6SXin Long result = asoc->strreset_result[i]; 615d0f025e6SXin Long if (result == SCTP_STRRESET_PERFORMED) 616d0f025e6SXin Long return NULL; 617d0f025e6SXin Long goto err; 61816e1a919SXin Long } 619d0f025e6SXin Long asoc->strreset_inseq++; 62016e1a919SXin Long 62116e1a919SXin Long if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ)) 62216e1a919SXin Long goto out; 62316e1a919SXin Long 62416e1a919SXin Long if (asoc->strreset_outstanding) { 62516e1a919SXin Long result = SCTP_STRRESET_ERR_IN_PROGRESS; 62616e1a919SXin Long goto out; 62716e1a919SXin Long } 62816e1a919SXin Long 6293aa623daSXin Long nums = (ntohs(param.p->length) - sizeof(*inreq)) / sizeof(__u16); 63016e1a919SXin Long str_p = inreq->list_of_streams; 63116e1a919SXin Long for (i = 0; i < nums; i++) { 63216e1a919SXin Long if (ntohs(str_p[i]) >= stream->outcnt) { 63316e1a919SXin Long result = SCTP_STRRESET_ERR_WRONG_SSN; 63416e1a919SXin Long goto out; 63516e1a919SXin Long } 63616e1a919SXin Long } 63716e1a919SXin Long 638d570a59cSXin Long if (!sctp_stream_outq_is_empty(stream, nums, str_p)) { 639d570a59cSXin Long result = SCTP_STRRESET_IN_PROGRESS; 640d570a59cSXin Long asoc->strreset_inseq--; 641d570a59cSXin Long goto err; 642d570a59cSXin Long } 643d570a59cSXin Long 64416e1a919SXin Long chunk = sctp_make_strreset_req(asoc, nums, str_p, 1, 0); 64516e1a919SXin Long if (!chunk) 64616e1a919SXin Long goto out; 64716e1a919SXin Long 64816e1a919SXin Long if (nums) 64916e1a919SXin Long for (i = 0; i < nums; i++) 65005364ca0SKonstantin Khorenko SCTP_SO(stream, ntohs(str_p[i]))->state = 65116e1a919SXin Long SCTP_STREAM_CLOSED; 65216e1a919SXin Long else 65316e1a919SXin Long for (i = 0; i < stream->outcnt; i++) 65405364ca0SKonstantin Khorenko SCTP_SO(stream, i)->state = SCTP_STREAM_CLOSED; 65516e1a919SXin Long 65616e1a919SXin Long asoc->strreset_chunk = chunk; 65716e1a919SXin Long asoc->strreset_outstanding = 1; 65816e1a919SXin Long sctp_chunk_hold(asoc->strreset_chunk); 65916e1a919SXin Long 660d0f025e6SXin Long result = SCTP_STRRESET_PERFORMED; 661d0f025e6SXin Long 66216e1a919SXin Long out: 663d0f025e6SXin Long sctp_update_strreset_result(asoc, result); 664d0f025e6SXin Long err: 66516e1a919SXin Long if (!chunk) 66616e1a919SXin Long chunk = sctp_make_strreset_resp(asoc, result, request_seq); 66716e1a919SXin Long 66816e1a919SXin Long return chunk; 66916e1a919SXin Long } 670692787ceSXin Long 671692787ceSXin Long struct sctp_chunk *sctp_process_strreset_tsnreq( 672692787ceSXin Long struct sctp_association *asoc, 673692787ceSXin Long union sctp_params param, 674692787ceSXin Long struct sctp_ulpevent **evp) 675692787ceSXin Long { 676692787ceSXin Long __u32 init_tsn = 0, next_tsn = 0, max_tsn_seen; 677692787ceSXin Long struct sctp_strreset_tsnreq *tsnreq = param.v; 678cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 679692787ceSXin Long __u32 result = SCTP_STRRESET_DENIED; 680692787ceSXin Long __u32 request_seq; 681692787ceSXin Long __u16 i; 682692787ceSXin Long 683692787ceSXin Long request_seq = ntohl(tsnreq->request_seq); 6846c801387SXin Long if (TSN_lt(asoc->strreset_inseq, request_seq) || 6856c801387SXin Long TSN_lt(request_seq, asoc->strreset_inseq - 2)) { 686692787ceSXin Long result = SCTP_STRRESET_ERR_BAD_SEQNO; 6876c801387SXin Long goto err; 6886c801387SXin Long } else if (TSN_lt(request_seq, asoc->strreset_inseq)) { 6896c801387SXin Long i = asoc->strreset_inseq - request_seq - 1; 6906c801387SXin Long result = asoc->strreset_result[i]; 6916c801387SXin Long if (result == SCTP_STRRESET_PERFORMED) { 69252a39589SXin Long next_tsn = asoc->ctsn_ack_point + 1; 6936c801387SXin Long init_tsn = 6946c801387SXin Long sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map) + 1; 695692787ceSXin Long } 6966c801387SXin Long goto err; 6976c801387SXin Long } 6985c6144a0SXin Long 6995c6144a0SXin Long if (!sctp_outq_is_empty(&asoc->outqueue)) { 7005c6144a0SXin Long result = SCTP_STRRESET_IN_PROGRESS; 7015c6144a0SXin Long goto err; 7025c6144a0SXin Long } 7035c6144a0SXin Long 7046c801387SXin Long asoc->strreset_inseq++; 705692787ceSXin Long 706692787ceSXin Long if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_ASSOC_REQ)) 707692787ceSXin Long goto out; 708692787ceSXin Long 709692787ceSXin Long if (asoc->strreset_outstanding) { 710692787ceSXin Long result = SCTP_STRRESET_ERR_IN_PROGRESS; 711692787ceSXin Long goto out; 712692787ceSXin Long } 713692787ceSXin Long 714159f2a74SXin Long /* G4: The same processing as though a FWD-TSN chunk (as defined in 715159f2a74SXin Long * [RFC3758]) with all streams affected and a new cumulative TSN 716159f2a74SXin Long * ACK of the Receiver's Next TSN minus 1 were received MUST be 717159f2a74SXin Long * performed. 718692787ceSXin Long */ 719692787ceSXin Long max_tsn_seen = sctp_tsnmap_get_max_tsn_seen(&asoc->peer.tsn_map); 72047b20a88SXin Long asoc->stream.si->report_ftsn(&asoc->ulpq, max_tsn_seen); 721692787ceSXin Long 722692787ceSXin Long /* G1: Compute an appropriate value for the Receiver's Next TSN -- the 723692787ceSXin Long * TSN that the peer should use to send the next DATA chunk. The 724692787ceSXin Long * value SHOULD be the smallest TSN not acknowledged by the 725692787ceSXin Long * receiver of the request plus 2^31. 726692787ceSXin Long */ 727692787ceSXin Long init_tsn = sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map) + (1 << 31); 728692787ceSXin Long sctp_tsnmap_init(&asoc->peer.tsn_map, SCTP_TSN_MAP_INITIAL, 729692787ceSXin Long init_tsn, GFP_ATOMIC); 730692787ceSXin Long 731159f2a74SXin Long /* G3: The same processing as though a SACK chunk with no gap report 732159f2a74SXin Long * and a cumulative TSN ACK of the Sender's Next TSN minus 1 were 733159f2a74SXin Long * received MUST be performed. 734692787ceSXin Long */ 735692787ceSXin Long sctp_outq_free(&asoc->outqueue); 736692787ceSXin Long 737692787ceSXin Long /* G2: Compute an appropriate value for the local endpoint's next TSN, 738692787ceSXin Long * i.e., the next TSN assigned by the receiver of the SSN/TSN reset 739692787ceSXin Long * chunk. The value SHOULD be the highest TSN sent by the receiver 740692787ceSXin Long * of the request plus 1. 741692787ceSXin Long */ 742692787ceSXin Long next_tsn = asoc->next_tsn; 743692787ceSXin Long asoc->ctsn_ack_point = next_tsn - 1; 744692787ceSXin Long asoc->adv_peer_ack_point = asoc->ctsn_ack_point; 745692787ceSXin Long 746692787ceSXin Long /* G5: The next expected and outgoing SSNs MUST be reset to 0 for all 747692787ceSXin Long * incoming and outgoing streams. 748692787ceSXin Long */ 749107e2425SXin Long for (i = 0; i < stream->outcnt; i++) { 75005364ca0SKonstantin Khorenko SCTP_SO(stream, i)->mid = 0; 75105364ca0SKonstantin Khorenko SCTP_SO(stream, i)->mid_uo = 0; 752107e2425SXin Long } 753692787ceSXin Long for (i = 0; i < stream->incnt; i++) 75405364ca0SKonstantin Khorenko SCTP_SI(stream, i)->mid = 0; 755692787ceSXin Long 756692787ceSXin Long result = SCTP_STRRESET_PERFORMED; 757692787ceSXin Long 758692787ceSXin Long *evp = sctp_ulpevent_make_assoc_reset_event(asoc, 0, init_tsn, 759692787ceSXin Long next_tsn, GFP_ATOMIC); 760692787ceSXin Long 761692787ceSXin Long out: 7626c801387SXin Long sctp_update_strreset_result(asoc, result); 7636c801387SXin Long err: 764692787ceSXin Long return sctp_make_strreset_tsnresp(asoc, result, request_seq, 765692787ceSXin Long next_tsn, init_tsn); 766692787ceSXin Long } 76750a41591SXin Long 76850a41591SXin Long struct sctp_chunk *sctp_process_strreset_addstrm_out( 76950a41591SXin Long struct sctp_association *asoc, 77050a41591SXin Long union sctp_params param, 77150a41591SXin Long struct sctp_ulpevent **evp) 77250a41591SXin Long { 77350a41591SXin Long struct sctp_strreset_addstrm *addstrm = param.v; 774cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 77550a41591SXin Long __u32 result = SCTP_STRRESET_DENIED; 77650a41591SXin Long __u32 request_seq, incnt; 777e4dc99c7SXin Long __u16 in, i; 77850a41591SXin Long 77950a41591SXin Long request_seq = ntohl(addstrm->request_seq); 780e4dc99c7SXin Long if (TSN_lt(asoc->strreset_inseq, request_seq) || 781e4dc99c7SXin Long TSN_lt(request_seq, asoc->strreset_inseq - 2)) { 78250a41591SXin Long result = SCTP_STRRESET_ERR_BAD_SEQNO; 783e4dc99c7SXin Long goto err; 784e4dc99c7SXin Long } else if (TSN_lt(request_seq, asoc->strreset_inseq)) { 785e4dc99c7SXin Long i = asoc->strreset_inseq - request_seq - 1; 786e4dc99c7SXin Long result = asoc->strreset_result[i]; 787e4dc99c7SXin Long goto err; 78850a41591SXin Long } 789e4dc99c7SXin Long asoc->strreset_inseq++; 79050a41591SXin Long 79150a41591SXin Long if (!(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ)) 79250a41591SXin Long goto out; 79350a41591SXin Long 7948220c870SXin Long in = ntohs(addstrm->number_of_streams); 7958220c870SXin Long incnt = stream->incnt + in; 7968220c870SXin Long if (!in || incnt > SCTP_MAX_STREAM) 7978220c870SXin Long goto out; 7988220c870SXin Long 7998220c870SXin Long if (sctp_stream_alloc_in(stream, incnt, GFP_ATOMIC)) 8008220c870SXin Long goto out; 8018220c870SXin Long 80250a41591SXin Long if (asoc->strreset_chunk) { 80350a41591SXin Long if (!sctp_chunk_lookup_strreset_param( 80450a41591SXin Long asoc, 0, SCTP_PARAM_RESET_ADD_IN_STREAMS)) { 80550a41591SXin Long /* same process with outstanding isn't 0 */ 80650a41591SXin Long result = SCTP_STRRESET_ERR_IN_PROGRESS; 80750a41591SXin Long goto out; 80850a41591SXin Long } 80950a41591SXin Long 81050a41591SXin Long asoc->strreset_outstanding--; 81150a41591SXin Long asoc->strreset_outseq++; 81250a41591SXin Long 81350a41591SXin Long if (!asoc->strreset_outstanding) { 81450a41591SXin Long struct sctp_transport *t; 81550a41591SXin Long 81650a41591SXin Long t = asoc->strreset_chunk->transport; 81750a41591SXin Long if (del_timer(&t->reconf_timer)) 81850a41591SXin Long sctp_transport_put(t); 81950a41591SXin Long 82050a41591SXin Long sctp_chunk_put(asoc->strreset_chunk); 82150a41591SXin Long asoc->strreset_chunk = NULL; 82250a41591SXin Long } 82350a41591SXin Long } 82450a41591SXin Long 82550a41591SXin Long stream->incnt = incnt; 82650a41591SXin Long 82750a41591SXin Long result = SCTP_STRRESET_PERFORMED; 82850a41591SXin Long 82950a41591SXin Long *evp = sctp_ulpevent_make_stream_change_event(asoc, 83050a41591SXin Long 0, ntohs(addstrm->number_of_streams), 0, GFP_ATOMIC); 83150a41591SXin Long 83250a41591SXin Long out: 833e4dc99c7SXin Long sctp_update_strreset_result(asoc, result); 834e4dc99c7SXin Long err: 83550a41591SXin Long return sctp_make_strreset_resp(asoc, result, request_seq); 83650a41591SXin Long } 837c5c4ebb3SXin Long 838c5c4ebb3SXin Long struct sctp_chunk *sctp_process_strreset_addstrm_in( 839c5c4ebb3SXin Long struct sctp_association *asoc, 840c5c4ebb3SXin Long union sctp_params param, 841c5c4ebb3SXin Long struct sctp_ulpevent **evp) 842c5c4ebb3SXin Long { 843c5c4ebb3SXin Long struct sctp_strreset_addstrm *addstrm = param.v; 844cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 845c5c4ebb3SXin Long __u32 result = SCTP_STRRESET_DENIED; 846c5c4ebb3SXin Long struct sctp_chunk *chunk = NULL; 847c5c4ebb3SXin Long __u32 request_seq, outcnt; 848d0f025e6SXin Long __u16 out, i; 849e090abd0SMarcelo Ricardo Leitner int ret; 850c5c4ebb3SXin Long 851c5c4ebb3SXin Long request_seq = ntohl(addstrm->request_seq); 852d0f025e6SXin Long if (TSN_lt(asoc->strreset_inseq, request_seq) || 853d0f025e6SXin Long TSN_lt(request_seq, asoc->strreset_inseq - 2)) { 854c5c4ebb3SXin Long result = SCTP_STRRESET_ERR_BAD_SEQNO; 855d0f025e6SXin Long goto err; 856d0f025e6SXin Long } else if (TSN_lt(request_seq, asoc->strreset_inseq)) { 857d0f025e6SXin Long i = asoc->strreset_inseq - request_seq - 1; 858d0f025e6SXin Long result = asoc->strreset_result[i]; 859d0f025e6SXin Long if (result == SCTP_STRRESET_PERFORMED) 860d0f025e6SXin Long return NULL; 861d0f025e6SXin Long goto err; 862c5c4ebb3SXin Long } 863d0f025e6SXin Long asoc->strreset_inseq++; 864c5c4ebb3SXin Long 865c5c4ebb3SXin Long if (!(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ)) 866c5c4ebb3SXin Long goto out; 867c5c4ebb3SXin Long 868c5c4ebb3SXin Long if (asoc->strreset_outstanding) { 869c5c4ebb3SXin Long result = SCTP_STRRESET_ERR_IN_PROGRESS; 870c5c4ebb3SXin Long goto out; 871c5c4ebb3SXin Long } 872c5c4ebb3SXin Long 873c5c4ebb3SXin Long out = ntohs(addstrm->number_of_streams); 874c5c4ebb3SXin Long outcnt = stream->outcnt + out; 875c5c4ebb3SXin Long if (!out || outcnt > SCTP_MAX_STREAM) 876c5c4ebb3SXin Long goto out; 877c5c4ebb3SXin Long 878e090abd0SMarcelo Ricardo Leitner ret = sctp_stream_alloc_out(stream, outcnt, GFP_ATOMIC); 879e090abd0SMarcelo Ricardo Leitner if (ret) 880c5c4ebb3SXin Long goto out; 881c5c4ebb3SXin Long 882c5c4ebb3SXin Long chunk = sctp_make_strreset_addstrm(asoc, out, 0); 883c5c4ebb3SXin Long if (!chunk) 884c5c4ebb3SXin Long goto out; 885c5c4ebb3SXin Long 886c5c4ebb3SXin Long asoc->strreset_chunk = chunk; 887c5c4ebb3SXin Long asoc->strreset_outstanding = 1; 888c5c4ebb3SXin Long sctp_chunk_hold(asoc->strreset_chunk); 889c5c4ebb3SXin Long 890c5c4ebb3SXin Long stream->outcnt = outcnt; 891c5c4ebb3SXin Long 892d0f025e6SXin Long result = SCTP_STRRESET_PERFORMED; 893d0f025e6SXin Long 894c5c4ebb3SXin Long out: 895d0f025e6SXin Long sctp_update_strreset_result(asoc, result); 896d0f025e6SXin Long err: 897c5c4ebb3SXin Long if (!chunk) 898c5c4ebb3SXin Long chunk = sctp_make_strreset_resp(asoc, result, request_seq); 899c5c4ebb3SXin Long 900c5c4ebb3SXin Long return chunk; 901c5c4ebb3SXin Long } 90211ae76e6SXin Long 90311ae76e6SXin Long struct sctp_chunk *sctp_process_strreset_resp( 90411ae76e6SXin Long struct sctp_association *asoc, 90511ae76e6SXin Long union sctp_params param, 90611ae76e6SXin Long struct sctp_ulpevent **evp) 90711ae76e6SXin Long { 908cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 90911ae76e6SXin Long struct sctp_strreset_resp *resp = param.v; 91011ae76e6SXin Long struct sctp_transport *t; 91111ae76e6SXin Long __u16 i, nums, flags = 0; 9123c918704SXin Long struct sctp_paramhdr *req; 91311ae76e6SXin Long __u32 result; 91411ae76e6SXin Long 91511ae76e6SXin Long req = sctp_chunk_lookup_strreset_param(asoc, resp->response_seq, 0); 91611ae76e6SXin Long if (!req) 91711ae76e6SXin Long return NULL; 91811ae76e6SXin Long 91911ae76e6SXin Long result = ntohl(resp->result); 92011ae76e6SXin Long if (result != SCTP_STRRESET_PERFORMED) { 92111ae76e6SXin Long /* if in progress, do nothing but retransmit */ 92211ae76e6SXin Long if (result == SCTP_STRRESET_IN_PROGRESS) 92311ae76e6SXin Long return NULL; 92411ae76e6SXin Long else if (result == SCTP_STRRESET_DENIED) 92511ae76e6SXin Long flags = SCTP_STREAM_RESET_DENIED; 92611ae76e6SXin Long else 92711ae76e6SXin Long flags = SCTP_STREAM_RESET_FAILED; 92811ae76e6SXin Long } 92911ae76e6SXin Long 93011ae76e6SXin Long if (req->type == SCTP_PARAM_RESET_OUT_REQUEST) { 93111ae76e6SXin Long struct sctp_strreset_outreq *outreq; 9321da4fc97SXin Long __be16 *str_p; 93311ae76e6SXin Long 93411ae76e6SXin Long outreq = (struct sctp_strreset_outreq *)req; 935edb12f2dSXin Long str_p = outreq->list_of_streams; 9363aa623daSXin Long nums = (ntohs(outreq->param_hdr.length) - sizeof(*outreq)) / 9373aa623daSXin Long sizeof(__u16); 93811ae76e6SXin Long 93911ae76e6SXin Long if (result == SCTP_STRRESET_PERFORMED) { 94005364ca0SKonstantin Khorenko struct sctp_stream_out *sout; 94111ae76e6SXin Long if (nums) { 942107e2425SXin Long for (i = 0; i < nums; i++) { 94305364ca0SKonstantin Khorenko sout = SCTP_SO(stream, ntohs(str_p[i])); 94405364ca0SKonstantin Khorenko sout->mid = 0; 94505364ca0SKonstantin Khorenko sout->mid_uo = 0; 946107e2425SXin Long } 94711ae76e6SXin Long } else { 948107e2425SXin Long for (i = 0; i < stream->outcnt; i++) { 94905364ca0SKonstantin Khorenko sout = SCTP_SO(stream, i); 95005364ca0SKonstantin Khorenko sout->mid = 0; 95105364ca0SKonstantin Khorenko sout->mid_uo = 0; 952107e2425SXin Long } 95311ae76e6SXin Long } 95411ae76e6SXin Long } 95511ae76e6SXin Long 9562e6dc4d9SXin Long flags |= SCTP_STREAM_RESET_OUTGOING_SSN; 9572e6dc4d9SXin Long 95811ae76e6SXin Long for (i = 0; i < stream->outcnt; i++) 95905364ca0SKonstantin Khorenko SCTP_SO(stream, i)->state = SCTP_STREAM_OPEN; 96011ae76e6SXin Long 96111ae76e6SXin Long *evp = sctp_ulpevent_make_stream_reset_event(asoc, flags, 96211ae76e6SXin Long nums, str_p, GFP_ATOMIC); 96311ae76e6SXin Long } else if (req->type == SCTP_PARAM_RESET_IN_REQUEST) { 96411ae76e6SXin Long struct sctp_strreset_inreq *inreq; 9651da4fc97SXin Long __be16 *str_p; 96611ae76e6SXin Long 96711ae76e6SXin Long /* if the result is performed, it's impossible for inreq */ 96811ae76e6SXin Long if (result == SCTP_STRRESET_PERFORMED) 96911ae76e6SXin Long return NULL; 97011ae76e6SXin Long 97111ae76e6SXin Long inreq = (struct sctp_strreset_inreq *)req; 972edb12f2dSXin Long str_p = inreq->list_of_streams; 9733aa623daSXin Long nums = (ntohs(inreq->param_hdr.length) - sizeof(*inreq)) / 9743aa623daSXin Long sizeof(__u16); 97511ae76e6SXin Long 9762e6dc4d9SXin Long flags |= SCTP_STREAM_RESET_INCOMING_SSN; 9772e6dc4d9SXin Long 97811ae76e6SXin Long *evp = sctp_ulpevent_make_stream_reset_event(asoc, flags, 97911ae76e6SXin Long nums, str_p, GFP_ATOMIC); 98011ae76e6SXin Long } else if (req->type == SCTP_PARAM_RESET_TSN_REQUEST) { 98111ae76e6SXin Long struct sctp_strreset_resptsn *resptsn; 98211ae76e6SXin Long __u32 stsn, rtsn; 98311ae76e6SXin Long 98411ae76e6SXin Long /* check for resptsn, as sctp_verify_reconf didn't do it*/ 98511ae76e6SXin Long if (ntohs(param.p->length) != sizeof(*resptsn)) 98611ae76e6SXin Long return NULL; 98711ae76e6SXin Long 98811ae76e6SXin Long resptsn = (struct sctp_strreset_resptsn *)resp; 98911ae76e6SXin Long stsn = ntohl(resptsn->senders_next_tsn); 99011ae76e6SXin Long rtsn = ntohl(resptsn->receivers_next_tsn); 99111ae76e6SXin Long 99211ae76e6SXin Long if (result == SCTP_STRRESET_PERFORMED) { 99311ae76e6SXin Long __u32 mtsn = sctp_tsnmap_get_max_tsn_seen( 99411ae76e6SXin Long &asoc->peer.tsn_map); 995159f2a74SXin Long LIST_HEAD(temp); 99611ae76e6SXin Long 99747b20a88SXin Long asoc->stream.si->report_ftsn(&asoc->ulpq, mtsn); 99811ae76e6SXin Long 99911ae76e6SXin Long sctp_tsnmap_init(&asoc->peer.tsn_map, 100011ae76e6SXin Long SCTP_TSN_MAP_INITIAL, 100111ae76e6SXin Long stsn, GFP_ATOMIC); 100211ae76e6SXin Long 1003159f2a74SXin Long /* Clean up sacked and abandoned queues only. As the 1004159f2a74SXin Long * out_chunk_list may not be empty, splice it to temp, 1005159f2a74SXin Long * then get it back after sctp_outq_free is done. 1006159f2a74SXin Long */ 1007159f2a74SXin Long list_splice_init(&asoc->outqueue.out_chunk_list, &temp); 100811ae76e6SXin Long sctp_outq_free(&asoc->outqueue); 1009159f2a74SXin Long list_splice_init(&temp, &asoc->outqueue.out_chunk_list); 101011ae76e6SXin Long 101111ae76e6SXin Long asoc->next_tsn = rtsn; 101211ae76e6SXin Long asoc->ctsn_ack_point = asoc->next_tsn - 1; 101311ae76e6SXin Long asoc->adv_peer_ack_point = asoc->ctsn_ack_point; 101411ae76e6SXin Long 1015107e2425SXin Long for (i = 0; i < stream->outcnt; i++) { 101605364ca0SKonstantin Khorenko SCTP_SO(stream, i)->mid = 0; 101705364ca0SKonstantin Khorenko SCTP_SO(stream, i)->mid_uo = 0; 1018107e2425SXin Long } 101911ae76e6SXin Long for (i = 0; i < stream->incnt; i++) 102005364ca0SKonstantin Khorenko SCTP_SI(stream, i)->mid = 0; 102111ae76e6SXin Long } 102211ae76e6SXin Long 102311ae76e6SXin Long for (i = 0; i < stream->outcnt; i++) 102405364ca0SKonstantin Khorenko SCTP_SO(stream, i)->state = SCTP_STREAM_OPEN; 102511ae76e6SXin Long 102611ae76e6SXin Long *evp = sctp_ulpevent_make_assoc_reset_event(asoc, flags, 102711ae76e6SXin Long stsn, rtsn, GFP_ATOMIC); 102811ae76e6SXin Long } else if (req->type == SCTP_PARAM_RESET_ADD_OUT_STREAMS) { 102911ae76e6SXin Long struct sctp_strreset_addstrm *addstrm; 103011ae76e6SXin Long __u16 number; 103111ae76e6SXin Long 103211ae76e6SXin Long addstrm = (struct sctp_strreset_addstrm *)req; 103311ae76e6SXin Long nums = ntohs(addstrm->number_of_streams); 103411ae76e6SXin Long number = stream->outcnt - nums; 103511ae76e6SXin Long 103611ae76e6SXin Long if (result == SCTP_STRRESET_PERFORMED) 103711ae76e6SXin Long for (i = number; i < stream->outcnt; i++) 103805364ca0SKonstantin Khorenko SCTP_SO(stream, i)->state = SCTP_STREAM_OPEN; 103911ae76e6SXin Long else 104011ae76e6SXin Long stream->outcnt = number; 104111ae76e6SXin Long 104211ae76e6SXin Long *evp = sctp_ulpevent_make_stream_change_event(asoc, flags, 104311ae76e6SXin Long 0, nums, GFP_ATOMIC); 104411ae76e6SXin Long } else if (req->type == SCTP_PARAM_RESET_ADD_IN_STREAMS) { 104511ae76e6SXin Long struct sctp_strreset_addstrm *addstrm; 104611ae76e6SXin Long 104711ae76e6SXin Long /* if the result is performed, it's impossible for addstrm in 104811ae76e6SXin Long * request. 104911ae76e6SXin Long */ 105011ae76e6SXin Long if (result == SCTP_STRRESET_PERFORMED) 105111ae76e6SXin Long return NULL; 105211ae76e6SXin Long 105311ae76e6SXin Long addstrm = (struct sctp_strreset_addstrm *)req; 105411ae76e6SXin Long nums = ntohs(addstrm->number_of_streams); 105511ae76e6SXin Long 105611ae76e6SXin Long *evp = sctp_ulpevent_make_stream_change_event(asoc, flags, 105711ae76e6SXin Long nums, 0, GFP_ATOMIC); 105811ae76e6SXin Long } 105911ae76e6SXin Long 106011ae76e6SXin Long asoc->strreset_outstanding--; 106111ae76e6SXin Long asoc->strreset_outseq++; 106211ae76e6SXin Long 106311ae76e6SXin Long /* remove everything for this reconf request */ 106411ae76e6SXin Long if (!asoc->strreset_outstanding) { 106511ae76e6SXin Long t = asoc->strreset_chunk->transport; 106611ae76e6SXin Long if (del_timer(&t->reconf_timer)) 106711ae76e6SXin Long sctp_transport_put(t); 106811ae76e6SXin Long 106911ae76e6SXin Long sctp_chunk_put(asoc->strreset_chunk); 107011ae76e6SXin Long asoc->strreset_chunk = NULL; 107111ae76e6SXin Long } 107211ae76e6SXin Long 107311ae76e6SXin Long return NULL; 107411ae76e6SXin Long } 1075