1a8386317SXin Long /* SCTP kernel implementation 2a8386317SXin Long * (C) Copyright IBM Corp. 2001, 2004 3a8386317SXin Long * Copyright (c) 1999-2000 Cisco, Inc. 4a8386317SXin Long * Copyright (c) 1999-2001 Motorola, Inc. 5a8386317SXin Long * Copyright (c) 2001 Intel Corp. 6a8386317SXin Long * 7a8386317SXin Long * This file is part of the SCTP kernel implementation 8a8386317SXin Long * 9fae8b6f4SXin Long * This file contains sctp stream maniuplation primitives and helpers. 10a8386317SXin Long * 11a8386317SXin Long * This SCTP implementation is free software; 12a8386317SXin Long * you can redistribute it and/or modify it under the terms of 13a8386317SXin Long * the GNU General Public License as published by 14a8386317SXin Long * the Free Software Foundation; either version 2, or (at your option) 15a8386317SXin Long * any later version. 16a8386317SXin Long * 17a8386317SXin Long * This SCTP implementation is distributed in the hope that it 18a8386317SXin Long * will be useful, but WITHOUT ANY WARRANTY; without even the implied 19a8386317SXin Long * ************************ 20a8386317SXin Long * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 21a8386317SXin Long * See the GNU General Public License for more details. 22a8386317SXin Long * 23a8386317SXin Long * You should have received a copy of the GNU General Public License 24a8386317SXin Long * along with GNU CC; see the file COPYING. If not, see 25a8386317SXin Long * <http://www.gnu.org/licenses/>. 26a8386317SXin Long * 27a8386317SXin Long * Please send any bug reports or fixes you make to the 28a8386317SXin Long * email address(es): 29a8386317SXin Long * lksctp developers <linux-sctp@vger.kernel.org> 30a8386317SXin Long * 31a8386317SXin Long * Written or modified by: 32a8386317SXin Long * Xin Long <lucien.xin@gmail.com> 33a8386317SXin Long */ 34a8386317SXin Long 355bbbbe32SMarcelo Ricardo Leitner #include <linux/list.h> 36a8386317SXin Long #include <net/sctp/sctp.h> 377f9d68acSXin Long #include <net/sctp/sm.h> 385bbbbe32SMarcelo Ricardo Leitner #include <net/sctp/stream_sched.h> 395bbbbe32SMarcelo Ricardo Leitner 405bbbbe32SMarcelo Ricardo Leitner /* Migrates chunks from stream queues to new stream queues if needed, 415bbbbe32SMarcelo Ricardo Leitner * but not across associations. Also, removes those chunks to streams 425bbbbe32SMarcelo Ricardo Leitner * higher than the new max. 435bbbbe32SMarcelo Ricardo Leitner */ 445bbbbe32SMarcelo Ricardo Leitner static void sctp_stream_outq_migrate(struct sctp_stream *stream, 455bbbbe32SMarcelo Ricardo Leitner struct sctp_stream *new, __u16 outcnt) 465bbbbe32SMarcelo Ricardo Leitner { 475bbbbe32SMarcelo Ricardo Leitner struct sctp_association *asoc; 485bbbbe32SMarcelo Ricardo Leitner struct sctp_chunk *ch, *temp; 495bbbbe32SMarcelo Ricardo Leitner struct sctp_outq *outq; 505bbbbe32SMarcelo Ricardo Leitner int i; 515bbbbe32SMarcelo Ricardo Leitner 525bbbbe32SMarcelo Ricardo Leitner asoc = container_of(stream, struct sctp_association, stream); 535bbbbe32SMarcelo Ricardo Leitner outq = &asoc->outqueue; 545bbbbe32SMarcelo Ricardo Leitner 555bbbbe32SMarcelo Ricardo Leitner list_for_each_entry_safe(ch, temp, &outq->out_chunk_list, list) { 565bbbbe32SMarcelo Ricardo Leitner __u16 sid = sctp_chunk_stream_no(ch); 575bbbbe32SMarcelo Ricardo Leitner 585bbbbe32SMarcelo Ricardo Leitner if (sid < outcnt) 595bbbbe32SMarcelo Ricardo Leitner continue; 605bbbbe32SMarcelo Ricardo Leitner 615bbbbe32SMarcelo Ricardo Leitner sctp_sched_dequeue_common(outq, ch); 625bbbbe32SMarcelo Ricardo Leitner /* No need to call dequeue_done here because 635bbbbe32SMarcelo Ricardo Leitner * the chunks are not scheduled by now. 645bbbbe32SMarcelo Ricardo Leitner */ 655bbbbe32SMarcelo Ricardo Leitner 665bbbbe32SMarcelo Ricardo Leitner /* Mark as failed send. */ 6708f46070SXin Long sctp_chunk_fail(ch, (__force __u32)SCTP_ERROR_INV_STRM); 685bbbbe32SMarcelo Ricardo Leitner if (asoc->peer.prsctp_capable && 695bbbbe32SMarcelo Ricardo Leitner SCTP_PR_PRIO_ENABLED(ch->sinfo.sinfo_flags)) 705bbbbe32SMarcelo Ricardo Leitner asoc->sent_cnt_removable--; 715bbbbe32SMarcelo Ricardo Leitner 725bbbbe32SMarcelo Ricardo Leitner sctp_chunk_free(ch); 735bbbbe32SMarcelo Ricardo Leitner } 745bbbbe32SMarcelo Ricardo Leitner 755bbbbe32SMarcelo Ricardo Leitner if (new) { 765bbbbe32SMarcelo Ricardo Leitner /* Here we actually move the old ext stuff into the new 775bbbbe32SMarcelo Ricardo Leitner * buffer, because we want to keep it. Then 785bbbbe32SMarcelo Ricardo Leitner * sctp_stream_update will swap ->out pointers. 795bbbbe32SMarcelo Ricardo Leitner */ 805bbbbe32SMarcelo Ricardo Leitner for (i = 0; i < outcnt; i++) { 810d493b4dSKonstantin Khorenko kfree(SCTP_SO(new, i)->ext); 820d493b4dSKonstantin Khorenko SCTP_SO(new, i)->ext = SCTP_SO(stream, i)->ext; 830d493b4dSKonstantin Khorenko SCTP_SO(stream, i)->ext = NULL; 845bbbbe32SMarcelo Ricardo Leitner } 855bbbbe32SMarcelo Ricardo Leitner } 865bbbbe32SMarcelo Ricardo Leitner 87af98c5a7SXin Long for (i = outcnt; i < stream->outcnt; i++) { 880d493b4dSKonstantin Khorenko kfree(SCTP_SO(stream, i)->ext); 89af98c5a7SXin Long SCTP_SO(stream, i)->ext = NULL; 90af98c5a7SXin Long } 915bbbbe32SMarcelo Ricardo Leitner } 92a8386317SXin Long 93e090abd0SMarcelo Ricardo Leitner static int sctp_stream_alloc_out(struct sctp_stream *stream, __u16 outcnt, 94e090abd0SMarcelo Ricardo Leitner gfp_t gfp) 95e090abd0SMarcelo Ricardo Leitner { 962075e50cSKent Overstreet int ret; 97e090abd0SMarcelo Ricardo Leitner 982075e50cSKent Overstreet if (outcnt <= stream->outcnt) 992075e50cSKent Overstreet return 0; 100e090abd0SMarcelo Ricardo Leitner 1012075e50cSKent Overstreet ret = genradix_prealloc(&stream->out, outcnt, gfp); 1022075e50cSKent Overstreet if (ret) 1032075e50cSKent Overstreet return ret; 104cfe4bd7aSXin Long 1052075e50cSKent Overstreet stream->outcnt = outcnt; 106e090abd0SMarcelo Ricardo Leitner return 0; 107e090abd0SMarcelo Ricardo Leitner } 108e090abd0SMarcelo Ricardo Leitner 1091fdb8d8fSMarcelo Ricardo Leitner static int sctp_stream_alloc_in(struct sctp_stream *stream, __u16 incnt, 1101fdb8d8fSMarcelo Ricardo Leitner gfp_t gfp) 1111fdb8d8fSMarcelo Ricardo Leitner { 1122075e50cSKent Overstreet int ret; 1131fdb8d8fSMarcelo Ricardo Leitner 1142075e50cSKent Overstreet if (incnt <= stream->incnt) 1152075e50cSKent Overstreet return 0; 1161fdb8d8fSMarcelo Ricardo Leitner 1172075e50cSKent Overstreet ret = genradix_prealloc(&stream->in, incnt, gfp); 1182075e50cSKent Overstreet if (ret) 1192075e50cSKent Overstreet return ret; 1201fdb8d8fSMarcelo Ricardo Leitner 1212075e50cSKent Overstreet stream->incnt = incnt; 1221fdb8d8fSMarcelo Ricardo Leitner return 0; 1231fdb8d8fSMarcelo Ricardo Leitner } 1241fdb8d8fSMarcelo Ricardo Leitner 125ff356414SXin Long int sctp_stream_init(struct sctp_stream *stream, __u16 outcnt, __u16 incnt, 126ff356414SXin Long gfp_t gfp) 127a8386317SXin Long { 1285bbbbe32SMarcelo Ricardo Leitner struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream); 1295bbbbe32SMarcelo Ricardo Leitner int i, ret = 0; 1303dbcc105SXin Long 1311ae2eaaaSMarcelo Ricardo Leitner gfp |= __GFP_NOWARN; 1321ae2eaaaSMarcelo Ricardo Leitner 1333dbcc105SXin Long /* Initial stream->out size may be very big, so free it and alloc 1341ae2eaaaSMarcelo Ricardo Leitner * a new one with new outcnt to save memory if needed. 1353dbcc105SXin Long */ 1361ae2eaaaSMarcelo Ricardo Leitner if (outcnt == stream->outcnt) 1371ae2eaaaSMarcelo Ricardo Leitner goto in; 1381ae2eaaaSMarcelo Ricardo Leitner 1395bbbbe32SMarcelo Ricardo Leitner /* Filter out chunks queued on streams that won't exist anymore */ 1405bbbbe32SMarcelo Ricardo Leitner sched->unsched_all(stream); 1415bbbbe32SMarcelo Ricardo Leitner sctp_stream_outq_migrate(stream, NULL, outcnt); 1425bbbbe32SMarcelo Ricardo Leitner sched->sched_all(stream); 1435bbbbe32SMarcelo Ricardo Leitner 14479d08951SMarcelo Ricardo Leitner ret = sctp_stream_alloc_out(stream, outcnt, gfp); 14579d08951SMarcelo Ricardo Leitner if (ret) 14679d08951SMarcelo Ricardo Leitner goto out; 1473dbcc105SXin Long 1483dbcc105SXin Long for (i = 0; i < stream->outcnt; i++) 14905364ca0SKonstantin Khorenko SCTP_SO(stream, i)->state = SCTP_STREAM_OPEN; 1503dbcc105SXin Long 1511ae2eaaaSMarcelo Ricardo Leitner in: 1520c3f6f65SXin Long sctp_stream_interleave_init(stream); 153ff356414SXin Long if (!incnt) 1545bbbbe32SMarcelo Ricardo Leitner goto out; 155ff356414SXin Long 15679d08951SMarcelo Ricardo Leitner ret = sctp_stream_alloc_in(stream, incnt, gfp); 15779d08951SMarcelo Ricardo Leitner if (ret) { 1585bbbbe32SMarcelo Ricardo Leitner sched->free(stream); 1592075e50cSKent Overstreet genradix_free(&stream->out); 16079d08951SMarcelo Ricardo Leitner stream->outcnt = 0; 16179d08951SMarcelo Ricardo Leitner goto out; 16279d08951SMarcelo Ricardo Leitner } 16379d08951SMarcelo Ricardo Leitner 1645bbbbe32SMarcelo Ricardo Leitner out: 1655bbbbe32SMarcelo Ricardo Leitner return ret; 166a8386317SXin Long } 167a8386317SXin Long 168f952be79SMarcelo Ricardo Leitner int sctp_stream_init_ext(struct sctp_stream *stream, __u16 sid) 169f952be79SMarcelo Ricardo Leitner { 170f952be79SMarcelo Ricardo Leitner struct sctp_stream_out_ext *soute; 171f952be79SMarcelo Ricardo Leitner 172f952be79SMarcelo Ricardo Leitner soute = kzalloc(sizeof(*soute), GFP_KERNEL); 173f952be79SMarcelo Ricardo Leitner if (!soute) 174f952be79SMarcelo Ricardo Leitner return -ENOMEM; 17505364ca0SKonstantin Khorenko SCTP_SO(stream, sid)->ext = soute; 176f952be79SMarcelo Ricardo Leitner 1775bbbbe32SMarcelo Ricardo Leitner return sctp_sched_init_sid(stream, sid, GFP_KERNEL); 178f952be79SMarcelo Ricardo Leitner } 179f952be79SMarcelo Ricardo Leitner 180a8386317SXin Long void sctp_stream_free(struct sctp_stream *stream) 181a8386317SXin Long { 1825bbbbe32SMarcelo Ricardo Leitner struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream); 183f952be79SMarcelo Ricardo Leitner int i; 184f952be79SMarcelo Ricardo Leitner 1855bbbbe32SMarcelo Ricardo Leitner sched->free(stream); 186f952be79SMarcelo Ricardo Leitner for (i = 0; i < stream->outcnt; i++) 18705364ca0SKonstantin Khorenko kfree(SCTP_SO(stream, i)->ext); 1882075e50cSKent Overstreet genradix_free(&stream->out); 1892075e50cSKent Overstreet genradix_free(&stream->in); 190a8386317SXin Long } 191a8386317SXin Long 192a8386317SXin Long void sctp_stream_clear(struct sctp_stream *stream) 193a8386317SXin Long { 194a8386317SXin Long int i; 195a8386317SXin Long 196107e2425SXin Long for (i = 0; i < stream->outcnt; i++) { 19705364ca0SKonstantin Khorenko SCTP_SO(stream, i)->mid = 0; 19805364ca0SKonstantin Khorenko SCTP_SO(stream, i)->mid_uo = 0; 199107e2425SXin Long } 200a8386317SXin Long 201a8386317SXin Long for (i = 0; i < stream->incnt; i++) 20205364ca0SKonstantin Khorenko SCTP_SI(stream, i)->mid = 0; 203a8386317SXin Long } 2047f9d68acSXin Long 205cee360abSXin Long void sctp_stream_update(struct sctp_stream *stream, struct sctp_stream *new) 206cee360abSXin Long { 2075bbbbe32SMarcelo Ricardo Leitner struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream); 2085bbbbe32SMarcelo Ricardo Leitner 2095bbbbe32SMarcelo Ricardo Leitner sched->unsched_all(stream); 2105bbbbe32SMarcelo Ricardo Leitner sctp_stream_outq_migrate(stream, new, new->outcnt); 211cee360abSXin Long sctp_stream_free(stream); 212cee360abSXin Long 213cee360abSXin Long stream->out = new->out; 214cee360abSXin Long stream->in = new->in; 215cee360abSXin Long stream->outcnt = new->outcnt; 216cee360abSXin Long stream->incnt = new->incnt; 217cee360abSXin Long 2185bbbbe32SMarcelo Ricardo Leitner sched->sched_all(stream); 2195bbbbe32SMarcelo Ricardo Leitner 2202075e50cSKent Overstreet new->out.tree.root = NULL; 2212075e50cSKent Overstreet new->in.tree.root = NULL; 2226a9a27d5SXin Long new->outcnt = 0; 2236a9a27d5SXin Long new->incnt = 0; 224cee360abSXin Long } 225cee360abSXin Long 2267f9d68acSXin Long static int sctp_send_reconf(struct sctp_association *asoc, 2277f9d68acSXin Long struct sctp_chunk *chunk) 2287f9d68acSXin Long { 2297f9d68acSXin Long struct net *net = sock_net(asoc->base.sk); 2307f9d68acSXin Long int retval = 0; 2317f9d68acSXin Long 2327f9d68acSXin Long retval = sctp_primitive_RECONF(net, asoc, chunk); 2337f9d68acSXin Long if (retval) 2347f9d68acSXin Long sctp_chunk_free(chunk); 2357f9d68acSXin Long 2367f9d68acSXin Long return retval; 2377f9d68acSXin Long } 2387f9d68acSXin Long 239d570a59cSXin Long static bool sctp_stream_outq_is_empty(struct sctp_stream *stream, 240d570a59cSXin Long __u16 str_nums, __be16 *str_list) 241d570a59cSXin Long { 242d570a59cSXin Long struct sctp_association *asoc; 243d570a59cSXin Long __u16 i; 244d570a59cSXin Long 245d570a59cSXin Long asoc = container_of(stream, struct sctp_association, stream); 246d570a59cSXin Long if (!asoc->outqueue.out_qlen) 247d570a59cSXin Long return true; 248d570a59cSXin Long 249d570a59cSXin Long if (!str_nums) 250d570a59cSXin Long return false; 251d570a59cSXin Long 252d570a59cSXin Long for (i = 0; i < str_nums; i++) { 253d570a59cSXin Long __u16 sid = ntohs(str_list[i]); 254d570a59cSXin Long 25505364ca0SKonstantin Khorenko if (SCTP_SO(stream, sid)->ext && 25605364ca0SKonstantin Khorenko !list_empty(&SCTP_SO(stream, sid)->ext->outq)) 257d570a59cSXin Long return false; 258d570a59cSXin Long } 259d570a59cSXin Long 260d570a59cSXin Long return true; 261d570a59cSXin Long } 262d570a59cSXin Long 2637f9d68acSXin Long int sctp_send_reset_streams(struct sctp_association *asoc, 2647f9d68acSXin Long struct sctp_reset_streams *params) 2657f9d68acSXin Long { 266cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 2677f9d68acSXin Long __u16 i, str_nums, *str_list; 2687f9d68acSXin Long struct sctp_chunk *chunk; 2697f9d68acSXin Long int retval = -EINVAL; 2701da4fc97SXin Long __be16 *nstr_list; 2717f9d68acSXin Long bool out, in; 2727f9d68acSXin Long 2737f9d68acSXin Long if (!asoc->peer.reconf_capable || 2747f9d68acSXin Long !(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ)) { 2757f9d68acSXin Long retval = -ENOPROTOOPT; 2767f9d68acSXin Long goto out; 2777f9d68acSXin Long } 2787f9d68acSXin Long 2797f9d68acSXin Long if (asoc->strreset_outstanding) { 2807f9d68acSXin Long retval = -EINPROGRESS; 2817f9d68acSXin Long goto out; 2827f9d68acSXin Long } 2837f9d68acSXin Long 2847f9d68acSXin Long out = params->srs_flags & SCTP_STREAM_RESET_OUTGOING; 2857f9d68acSXin Long in = params->srs_flags & SCTP_STREAM_RESET_INCOMING; 2867f9d68acSXin Long if (!out && !in) 2877f9d68acSXin Long goto out; 2887f9d68acSXin Long 2897f9d68acSXin Long str_nums = params->srs_number_streams; 2907f9d68acSXin Long str_list = params->srs_stream_list; 291423852f8SXin Long if (str_nums) { 292423852f8SXin Long int param_len = 0; 293423852f8SXin Long 294423852f8SXin Long if (out) { 2957f9d68acSXin Long for (i = 0; i < str_nums; i++) 2967f9d68acSXin Long if (str_list[i] >= stream->outcnt) 2977f9d68acSXin Long goto out; 2987f9d68acSXin Long 299423852f8SXin Long param_len = str_nums * sizeof(__u16) + 300423852f8SXin Long sizeof(struct sctp_strreset_outreq); 301423852f8SXin Long } 302423852f8SXin Long 303423852f8SXin Long if (in) { 3047f9d68acSXin Long for (i = 0; i < str_nums; i++) 3057f9d68acSXin Long if (str_list[i] >= stream->incnt) 3067f9d68acSXin Long goto out; 3077f9d68acSXin Long 308423852f8SXin Long param_len += str_nums * sizeof(__u16) + 309423852f8SXin Long sizeof(struct sctp_strreset_inreq); 310423852f8SXin Long } 311423852f8SXin Long 312423852f8SXin Long if (param_len > SCTP_MAX_CHUNK_LEN - 313423852f8SXin Long sizeof(struct sctp_reconf_chunk)) 314423852f8SXin Long goto out; 315423852f8SXin Long } 316423852f8SXin Long 3171da4fc97SXin Long nstr_list = kcalloc(str_nums, sizeof(__be16), GFP_KERNEL); 3181da4fc97SXin Long if (!nstr_list) { 3191da4fc97SXin Long retval = -ENOMEM; 3201da4fc97SXin Long goto out; 3211da4fc97SXin Long } 32216e1a919SXin Long 32316e1a919SXin Long for (i = 0; i < str_nums; i++) 3241da4fc97SXin Long nstr_list[i] = htons(str_list[i]); 3251da4fc97SXin Long 326d570a59cSXin Long if (out && !sctp_stream_outq_is_empty(stream, str_nums, nstr_list)) { 327d570a59cSXin Long retval = -EAGAIN; 328d570a59cSXin Long goto out; 329d570a59cSXin Long } 330d570a59cSXin Long 3311da4fc97SXin Long chunk = sctp_make_strreset_req(asoc, str_nums, nstr_list, out, in); 3321da4fc97SXin Long 3331da4fc97SXin Long kfree(nstr_list); 33416e1a919SXin Long 335119aecbaSXin Long if (!chunk) { 336119aecbaSXin Long retval = -ENOMEM; 3377f9d68acSXin Long goto out; 338119aecbaSXin Long } 3397f9d68acSXin Long 3407f9d68acSXin Long if (out) { 3417f9d68acSXin Long if (str_nums) 3427f9d68acSXin Long for (i = 0; i < str_nums; i++) 34305364ca0SKonstantin Khorenko SCTP_SO(stream, str_list[i])->state = 3447f9d68acSXin Long SCTP_STREAM_CLOSED; 3457f9d68acSXin Long else 3467f9d68acSXin Long for (i = 0; i < stream->outcnt; i++) 34705364ca0SKonstantin Khorenko SCTP_SO(stream, i)->state = SCTP_STREAM_CLOSED; 3487f9d68acSXin Long } 3497f9d68acSXin Long 3507f9d68acSXin Long asoc->strreset_chunk = chunk; 3517f9d68acSXin Long sctp_chunk_hold(asoc->strreset_chunk); 3527f9d68acSXin Long 3537f9d68acSXin Long retval = sctp_send_reconf(asoc, chunk); 3547f9d68acSXin Long if (retval) { 3557f9d68acSXin Long sctp_chunk_put(asoc->strreset_chunk); 3567f9d68acSXin Long asoc->strreset_chunk = NULL; 357119aecbaSXin Long if (!out) 358119aecbaSXin Long goto out; 359119aecbaSXin Long 360119aecbaSXin Long if (str_nums) 361119aecbaSXin Long for (i = 0; i < str_nums; i++) 36205364ca0SKonstantin Khorenko SCTP_SO(stream, str_list[i])->state = 363119aecbaSXin Long SCTP_STREAM_OPEN; 364119aecbaSXin Long else 365119aecbaSXin Long for (i = 0; i < stream->outcnt; i++) 36605364ca0SKonstantin Khorenko SCTP_SO(stream, i)->state = SCTP_STREAM_OPEN; 367119aecbaSXin Long 368119aecbaSXin Long goto out; 3697f9d68acSXin Long } 3707f9d68acSXin Long 371119aecbaSXin Long asoc->strreset_outstanding = out + in; 372119aecbaSXin Long 3737f9d68acSXin Long out: 3747f9d68acSXin Long return retval; 3757f9d68acSXin Long } 376a92ce1a4SXin Long 377a92ce1a4SXin Long int sctp_send_reset_assoc(struct sctp_association *asoc) 378a92ce1a4SXin Long { 379cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 380a92ce1a4SXin Long struct sctp_chunk *chunk = NULL; 381a92ce1a4SXin Long int retval; 382a92ce1a4SXin Long __u16 i; 383a92ce1a4SXin Long 384a92ce1a4SXin Long if (!asoc->peer.reconf_capable || 385a92ce1a4SXin Long !(asoc->strreset_enable & SCTP_ENABLE_RESET_ASSOC_REQ)) 386a92ce1a4SXin Long return -ENOPROTOOPT; 387a92ce1a4SXin Long 388a92ce1a4SXin Long if (asoc->strreset_outstanding) 389a92ce1a4SXin Long return -EINPROGRESS; 390a92ce1a4SXin Long 3915c6144a0SXin Long if (!sctp_outq_is_empty(&asoc->outqueue)) 3925c6144a0SXin Long return -EAGAIN; 3935c6144a0SXin Long 394a92ce1a4SXin Long chunk = sctp_make_strreset_tsnreq(asoc); 395a92ce1a4SXin Long if (!chunk) 396a92ce1a4SXin Long return -ENOMEM; 397a92ce1a4SXin Long 398a92ce1a4SXin Long /* Block further xmit of data until this request is completed */ 399cee360abSXin Long for (i = 0; i < stream->outcnt; i++) 40005364ca0SKonstantin Khorenko SCTP_SO(stream, i)->state = SCTP_STREAM_CLOSED; 401a92ce1a4SXin Long 402a92ce1a4SXin Long asoc->strreset_chunk = chunk; 403a92ce1a4SXin Long sctp_chunk_hold(asoc->strreset_chunk); 404a92ce1a4SXin Long 405a92ce1a4SXin Long retval = sctp_send_reconf(asoc, chunk); 406a92ce1a4SXin Long if (retval) { 407a92ce1a4SXin Long sctp_chunk_put(asoc->strreset_chunk); 408a92ce1a4SXin Long asoc->strreset_chunk = NULL; 409a92ce1a4SXin Long 410cee360abSXin Long for (i = 0; i < stream->outcnt; i++) 41105364ca0SKonstantin Khorenko SCTP_SO(stream, i)->state = SCTP_STREAM_OPEN; 412a92ce1a4SXin Long 413a92ce1a4SXin Long return retval; 414a92ce1a4SXin Long } 415a92ce1a4SXin Long 416a92ce1a4SXin Long asoc->strreset_outstanding = 1; 417a92ce1a4SXin Long 418a92ce1a4SXin Long return 0; 419a92ce1a4SXin Long } 420242bd2d5SXin Long 421242bd2d5SXin Long int sctp_send_add_streams(struct sctp_association *asoc, 422242bd2d5SXin Long struct sctp_add_streams *params) 423242bd2d5SXin Long { 424cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 425242bd2d5SXin Long struct sctp_chunk *chunk = NULL; 426dc82673fSWei Yongjun int retval; 427242bd2d5SXin Long __u32 outcnt, incnt; 428242bd2d5SXin Long __u16 out, in; 429242bd2d5SXin Long 430242bd2d5SXin Long if (!asoc->peer.reconf_capable || 431242bd2d5SXin Long !(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ)) { 432242bd2d5SXin Long retval = -ENOPROTOOPT; 433242bd2d5SXin Long goto out; 434242bd2d5SXin Long } 435242bd2d5SXin Long 436242bd2d5SXin Long if (asoc->strreset_outstanding) { 437242bd2d5SXin Long retval = -EINPROGRESS; 438242bd2d5SXin Long goto out; 439242bd2d5SXin Long } 440242bd2d5SXin Long 441242bd2d5SXin Long out = params->sas_outstrms; 442242bd2d5SXin Long in = params->sas_instrms; 443242bd2d5SXin Long outcnt = stream->outcnt + out; 444242bd2d5SXin Long incnt = stream->incnt + in; 445242bd2d5SXin Long if (outcnt > SCTP_MAX_STREAM || incnt > SCTP_MAX_STREAM || 446242bd2d5SXin Long (!out && !in)) { 447242bd2d5SXin Long retval = -EINVAL; 448242bd2d5SXin Long goto out; 449242bd2d5SXin Long } 450242bd2d5SXin Long 451242bd2d5SXin Long if (out) { 452e090abd0SMarcelo Ricardo Leitner retval = sctp_stream_alloc_out(stream, outcnt, GFP_KERNEL); 453e090abd0SMarcelo Ricardo Leitner if (retval) 454242bd2d5SXin Long goto out; 455242bd2d5SXin Long } 456242bd2d5SXin Long 457242bd2d5SXin Long chunk = sctp_make_strreset_addstrm(asoc, out, in); 458dc82673fSWei Yongjun if (!chunk) { 459dc82673fSWei Yongjun retval = -ENOMEM; 460242bd2d5SXin Long goto out; 461dc82673fSWei Yongjun } 462242bd2d5SXin Long 463242bd2d5SXin Long asoc->strreset_chunk = chunk; 464242bd2d5SXin Long sctp_chunk_hold(asoc->strreset_chunk); 465242bd2d5SXin Long 466242bd2d5SXin Long retval = sctp_send_reconf(asoc, chunk); 467242bd2d5SXin Long if (retval) { 468242bd2d5SXin Long sctp_chunk_put(asoc->strreset_chunk); 469242bd2d5SXin Long asoc->strreset_chunk = NULL; 470242bd2d5SXin Long goto out; 471242bd2d5SXin Long } 472242bd2d5SXin Long 473242bd2d5SXin Long asoc->strreset_outstanding = !!out + !!in; 474242bd2d5SXin Long 475242bd2d5SXin Long out: 476242bd2d5SXin Long return retval; 477242bd2d5SXin Long } 47881054476SXin Long 4793c918704SXin Long static struct sctp_paramhdr *sctp_chunk_lookup_strreset_param( 4801da4fc97SXin Long struct sctp_association *asoc, __be32 resp_seq, 48150a41591SXin Long __be16 type) 48281054476SXin Long { 48381054476SXin Long struct sctp_chunk *chunk = asoc->strreset_chunk; 48481054476SXin Long struct sctp_reconf_chunk *hdr; 48581054476SXin Long union sctp_params param; 48681054476SXin Long 48750a41591SXin Long if (!chunk) 48881054476SXin Long return NULL; 48981054476SXin Long 49081054476SXin Long hdr = (struct sctp_reconf_chunk *)chunk->chunk_hdr; 49181054476SXin Long sctp_walk_params(param, hdr, params) { 49281054476SXin Long /* sctp_strreset_tsnreq is actually the basic structure 49381054476SXin Long * of all stream reconf params, so it's safe to use it 49481054476SXin Long * to access request_seq. 49581054476SXin Long */ 49681054476SXin Long struct sctp_strreset_tsnreq *req = param.v; 49781054476SXin Long 49850a41591SXin Long if ((!resp_seq || req->request_seq == resp_seq) && 49950a41591SXin Long (!type || type == req->param_hdr.type)) 50081054476SXin Long return param.v; 50181054476SXin Long } 50281054476SXin Long 50381054476SXin Long return NULL; 50481054476SXin Long } 50581054476SXin Long 506e4dc99c7SXin Long static void sctp_update_strreset_result(struct sctp_association *asoc, 507e4dc99c7SXin Long __u32 result) 508e4dc99c7SXin Long { 509e4dc99c7SXin Long asoc->strreset_result[1] = asoc->strreset_result[0]; 510e4dc99c7SXin Long asoc->strreset_result[0] = result; 511e4dc99c7SXin Long } 512e4dc99c7SXin Long 51381054476SXin Long struct sctp_chunk *sctp_process_strreset_outreq( 51481054476SXin Long struct sctp_association *asoc, 51581054476SXin Long union sctp_params param, 51681054476SXin Long struct sctp_ulpevent **evp) 51781054476SXin Long { 51881054476SXin Long struct sctp_strreset_outreq *outreq = param.v; 519cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 52081054476SXin Long __u32 result = SCTP_STRRESET_DENIED; 5211da4fc97SXin Long __be16 *str_p = NULL; 52281054476SXin Long __u32 request_seq; 5232e6dc4d9SXin Long __u16 i, nums; 52481054476SXin Long 52581054476SXin Long request_seq = ntohl(outreq->request_seq); 52681054476SXin Long 52781054476SXin Long if (ntohl(outreq->send_reset_at_tsn) > 52881054476SXin Long sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map)) { 52981054476SXin Long result = SCTP_STRRESET_IN_PROGRESS; 530e4dc99c7SXin Long goto err; 53181054476SXin Long } 53281054476SXin Long 533e4dc99c7SXin Long if (TSN_lt(asoc->strreset_inseq, request_seq) || 534e4dc99c7SXin Long TSN_lt(request_seq, asoc->strreset_inseq - 2)) { 53581054476SXin Long result = SCTP_STRRESET_ERR_BAD_SEQNO; 536e4dc99c7SXin Long goto err; 537e4dc99c7SXin Long } else if (TSN_lt(request_seq, asoc->strreset_inseq)) { 538e4dc99c7SXin Long i = asoc->strreset_inseq - request_seq - 1; 539e4dc99c7SXin Long result = asoc->strreset_result[i]; 540e4dc99c7SXin Long goto err; 54181054476SXin Long } 542e4dc99c7SXin Long asoc->strreset_inseq++; 54381054476SXin Long 54481054476SXin Long /* Check strreset_enable after inseq inc, as sender cannot tell 54581054476SXin Long * the peer doesn't enable strreset after receiving response with 54681054476SXin Long * result denied, as well as to keep consistent with bsd. 54781054476SXin Long */ 54881054476SXin Long if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ)) 54981054476SXin Long goto out; 55081054476SXin Long 5512e6dc4d9SXin Long nums = (ntohs(param.p->length) - sizeof(*outreq)) / sizeof(__u16); 5522e6dc4d9SXin Long str_p = outreq->list_of_streams; 5532e6dc4d9SXin Long for (i = 0; i < nums; i++) { 5542e6dc4d9SXin Long if (ntohs(str_p[i]) >= stream->incnt) { 5552e6dc4d9SXin Long result = SCTP_STRRESET_ERR_WRONG_SSN; 5562e6dc4d9SXin Long goto out; 5572e6dc4d9SXin Long } 5582e6dc4d9SXin Long } 5592e6dc4d9SXin Long 56081054476SXin Long if (asoc->strreset_chunk) { 56150a41591SXin Long if (!sctp_chunk_lookup_strreset_param( 56250a41591SXin Long asoc, outreq->response_seq, 56350a41591SXin Long SCTP_PARAM_RESET_IN_REQUEST)) { 56481054476SXin Long /* same process with outstanding isn't 0 */ 56581054476SXin Long result = SCTP_STRRESET_ERR_IN_PROGRESS; 56681054476SXin Long goto out; 56781054476SXin Long } 56881054476SXin Long 56981054476SXin Long asoc->strreset_outstanding--; 57081054476SXin Long asoc->strreset_outseq++; 57181054476SXin Long 57281054476SXin Long if (!asoc->strreset_outstanding) { 57350a41591SXin Long struct sctp_transport *t; 57450a41591SXin Long 57581054476SXin Long t = asoc->strreset_chunk->transport; 57681054476SXin Long if (del_timer(&t->reconf_timer)) 57781054476SXin Long sctp_transport_put(t); 57881054476SXin Long 57981054476SXin Long sctp_chunk_put(asoc->strreset_chunk); 58081054476SXin Long asoc->strreset_chunk = NULL; 58181054476SXin Long } 58281054476SXin Long } 58381054476SXin Long 5842e6dc4d9SXin Long if (nums) 58581054476SXin Long for (i = 0; i < nums; i++) 58605364ca0SKonstantin Khorenko SCTP_SI(stream, ntohs(str_p[i]))->mid = 0; 5872e6dc4d9SXin Long else 58881054476SXin Long for (i = 0; i < stream->incnt; i++) 58905364ca0SKonstantin Khorenko SCTP_SI(stream, i)->mid = 0; 59081054476SXin Long 59181054476SXin Long result = SCTP_STRRESET_PERFORMED; 59281054476SXin Long 59381054476SXin Long *evp = sctp_ulpevent_make_stream_reset_event(asoc, 5942e6dc4d9SXin Long SCTP_STREAM_RESET_INCOMING_SSN, nums, str_p, GFP_ATOMIC); 59581054476SXin Long 59681054476SXin Long out: 597e4dc99c7SXin Long sctp_update_strreset_result(asoc, result); 598e4dc99c7SXin Long err: 59981054476SXin Long return sctp_make_strreset_resp(asoc, result, request_seq); 60081054476SXin Long } 60116e1a919SXin Long 60216e1a919SXin Long struct sctp_chunk *sctp_process_strreset_inreq( 60316e1a919SXin Long struct sctp_association *asoc, 60416e1a919SXin Long union sctp_params param, 60516e1a919SXin Long struct sctp_ulpevent **evp) 60616e1a919SXin Long { 60716e1a919SXin Long struct sctp_strreset_inreq *inreq = param.v; 608cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 60916e1a919SXin Long __u32 result = SCTP_STRRESET_DENIED; 61016e1a919SXin Long struct sctp_chunk *chunk = NULL; 61116e1a919SXin Long __u32 request_seq; 6121da4fc97SXin Long __u16 i, nums; 6131da4fc97SXin Long __be16 *str_p; 61416e1a919SXin Long 61516e1a919SXin Long request_seq = ntohl(inreq->request_seq); 616d0f025e6SXin Long if (TSN_lt(asoc->strreset_inseq, request_seq) || 617d0f025e6SXin Long TSN_lt(request_seq, asoc->strreset_inseq - 2)) { 61816e1a919SXin Long result = SCTP_STRRESET_ERR_BAD_SEQNO; 619d0f025e6SXin Long goto err; 620d0f025e6SXin Long } else if (TSN_lt(request_seq, asoc->strreset_inseq)) { 621d0f025e6SXin Long i = asoc->strreset_inseq - request_seq - 1; 622d0f025e6SXin Long result = asoc->strreset_result[i]; 623d0f025e6SXin Long if (result == SCTP_STRRESET_PERFORMED) 624d0f025e6SXin Long return NULL; 625d0f025e6SXin Long goto err; 62616e1a919SXin Long } 627d0f025e6SXin Long asoc->strreset_inseq++; 62816e1a919SXin Long 62916e1a919SXin Long if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ)) 63016e1a919SXin Long goto out; 63116e1a919SXin Long 63216e1a919SXin Long if (asoc->strreset_outstanding) { 63316e1a919SXin Long result = SCTP_STRRESET_ERR_IN_PROGRESS; 63416e1a919SXin Long goto out; 63516e1a919SXin Long } 63616e1a919SXin Long 6373aa623daSXin Long nums = (ntohs(param.p->length) - sizeof(*inreq)) / sizeof(__u16); 63816e1a919SXin Long str_p = inreq->list_of_streams; 63916e1a919SXin Long for (i = 0; i < nums; i++) { 64016e1a919SXin Long if (ntohs(str_p[i]) >= stream->outcnt) { 64116e1a919SXin Long result = SCTP_STRRESET_ERR_WRONG_SSN; 64216e1a919SXin Long goto out; 64316e1a919SXin Long } 64416e1a919SXin Long } 64516e1a919SXin Long 646d570a59cSXin Long if (!sctp_stream_outq_is_empty(stream, nums, str_p)) { 647d570a59cSXin Long result = SCTP_STRRESET_IN_PROGRESS; 648d570a59cSXin Long asoc->strreset_inseq--; 649d570a59cSXin Long goto err; 650d570a59cSXin Long } 651d570a59cSXin Long 65216e1a919SXin Long chunk = sctp_make_strreset_req(asoc, nums, str_p, 1, 0); 65316e1a919SXin Long if (!chunk) 65416e1a919SXin Long goto out; 65516e1a919SXin Long 65616e1a919SXin Long if (nums) 65716e1a919SXin Long for (i = 0; i < nums; i++) 65805364ca0SKonstantin Khorenko SCTP_SO(stream, ntohs(str_p[i]))->state = 65916e1a919SXin Long SCTP_STREAM_CLOSED; 66016e1a919SXin Long else 66116e1a919SXin Long for (i = 0; i < stream->outcnt; i++) 66205364ca0SKonstantin Khorenko SCTP_SO(stream, i)->state = SCTP_STREAM_CLOSED; 66316e1a919SXin Long 66416e1a919SXin Long asoc->strreset_chunk = chunk; 66516e1a919SXin Long asoc->strreset_outstanding = 1; 66616e1a919SXin Long sctp_chunk_hold(asoc->strreset_chunk); 66716e1a919SXin Long 668d0f025e6SXin Long result = SCTP_STRRESET_PERFORMED; 669d0f025e6SXin Long 67016e1a919SXin Long out: 671d0f025e6SXin Long sctp_update_strreset_result(asoc, result); 672d0f025e6SXin Long err: 67316e1a919SXin Long if (!chunk) 67416e1a919SXin Long chunk = sctp_make_strreset_resp(asoc, result, request_seq); 67516e1a919SXin Long 67616e1a919SXin Long return chunk; 67716e1a919SXin Long } 678692787ceSXin Long 679692787ceSXin Long struct sctp_chunk *sctp_process_strreset_tsnreq( 680692787ceSXin Long struct sctp_association *asoc, 681692787ceSXin Long union sctp_params param, 682692787ceSXin Long struct sctp_ulpevent **evp) 683692787ceSXin Long { 684692787ceSXin Long __u32 init_tsn = 0, next_tsn = 0, max_tsn_seen; 685692787ceSXin Long struct sctp_strreset_tsnreq *tsnreq = param.v; 686cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 687692787ceSXin Long __u32 result = SCTP_STRRESET_DENIED; 688692787ceSXin Long __u32 request_seq; 689692787ceSXin Long __u16 i; 690692787ceSXin Long 691692787ceSXin Long request_seq = ntohl(tsnreq->request_seq); 6926c801387SXin Long if (TSN_lt(asoc->strreset_inseq, request_seq) || 6936c801387SXin Long TSN_lt(request_seq, asoc->strreset_inseq - 2)) { 694692787ceSXin Long result = SCTP_STRRESET_ERR_BAD_SEQNO; 6956c801387SXin Long goto err; 6966c801387SXin Long } else if (TSN_lt(request_seq, asoc->strreset_inseq)) { 6976c801387SXin Long i = asoc->strreset_inseq - request_seq - 1; 6986c801387SXin Long result = asoc->strreset_result[i]; 6996c801387SXin Long if (result == SCTP_STRRESET_PERFORMED) { 70052a39589SXin Long next_tsn = asoc->ctsn_ack_point + 1; 7016c801387SXin Long init_tsn = 7026c801387SXin Long sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map) + 1; 703692787ceSXin Long } 7046c801387SXin Long goto err; 7056c801387SXin Long } 7065c6144a0SXin Long 7075c6144a0SXin Long if (!sctp_outq_is_empty(&asoc->outqueue)) { 7085c6144a0SXin Long result = SCTP_STRRESET_IN_PROGRESS; 7095c6144a0SXin Long goto err; 7105c6144a0SXin Long } 7115c6144a0SXin Long 7126c801387SXin Long asoc->strreset_inseq++; 713692787ceSXin Long 714692787ceSXin Long if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_ASSOC_REQ)) 715692787ceSXin Long goto out; 716692787ceSXin Long 717692787ceSXin Long if (asoc->strreset_outstanding) { 718692787ceSXin Long result = SCTP_STRRESET_ERR_IN_PROGRESS; 719692787ceSXin Long goto out; 720692787ceSXin Long } 721692787ceSXin Long 722159f2a74SXin Long /* G4: The same processing as though a FWD-TSN chunk (as defined in 723159f2a74SXin Long * [RFC3758]) with all streams affected and a new cumulative TSN 724159f2a74SXin Long * ACK of the Receiver's Next TSN minus 1 were received MUST be 725159f2a74SXin Long * performed. 726692787ceSXin Long */ 727692787ceSXin Long max_tsn_seen = sctp_tsnmap_get_max_tsn_seen(&asoc->peer.tsn_map); 72847b20a88SXin Long asoc->stream.si->report_ftsn(&asoc->ulpq, max_tsn_seen); 729692787ceSXin Long 730692787ceSXin Long /* G1: Compute an appropriate value for the Receiver's Next TSN -- the 731692787ceSXin Long * TSN that the peer should use to send the next DATA chunk. The 732692787ceSXin Long * value SHOULD be the smallest TSN not acknowledged by the 733692787ceSXin Long * receiver of the request plus 2^31. 734692787ceSXin Long */ 735692787ceSXin Long init_tsn = sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map) + (1 << 31); 736692787ceSXin Long sctp_tsnmap_init(&asoc->peer.tsn_map, SCTP_TSN_MAP_INITIAL, 737692787ceSXin Long init_tsn, GFP_ATOMIC); 738692787ceSXin Long 739159f2a74SXin Long /* G3: The same processing as though a SACK chunk with no gap report 740159f2a74SXin Long * and a cumulative TSN ACK of the Sender's Next TSN minus 1 were 741159f2a74SXin Long * received MUST be performed. 742692787ceSXin Long */ 743692787ceSXin Long sctp_outq_free(&asoc->outqueue); 744692787ceSXin Long 745692787ceSXin Long /* G2: Compute an appropriate value for the local endpoint's next TSN, 746692787ceSXin Long * i.e., the next TSN assigned by the receiver of the SSN/TSN reset 747692787ceSXin Long * chunk. The value SHOULD be the highest TSN sent by the receiver 748692787ceSXin Long * of the request plus 1. 749692787ceSXin Long */ 750692787ceSXin Long next_tsn = asoc->next_tsn; 751692787ceSXin Long asoc->ctsn_ack_point = next_tsn - 1; 752692787ceSXin Long asoc->adv_peer_ack_point = asoc->ctsn_ack_point; 753692787ceSXin Long 754692787ceSXin Long /* G5: The next expected and outgoing SSNs MUST be reset to 0 for all 755692787ceSXin Long * incoming and outgoing streams. 756692787ceSXin Long */ 757107e2425SXin Long for (i = 0; i < stream->outcnt; i++) { 75805364ca0SKonstantin Khorenko SCTP_SO(stream, i)->mid = 0; 75905364ca0SKonstantin Khorenko SCTP_SO(stream, i)->mid_uo = 0; 760107e2425SXin Long } 761692787ceSXin Long for (i = 0; i < stream->incnt; i++) 76205364ca0SKonstantin Khorenko SCTP_SI(stream, i)->mid = 0; 763692787ceSXin Long 764692787ceSXin Long result = SCTP_STRRESET_PERFORMED; 765692787ceSXin Long 766692787ceSXin Long *evp = sctp_ulpevent_make_assoc_reset_event(asoc, 0, init_tsn, 767692787ceSXin Long next_tsn, GFP_ATOMIC); 768692787ceSXin Long 769692787ceSXin Long out: 7706c801387SXin Long sctp_update_strreset_result(asoc, result); 7716c801387SXin Long err: 772692787ceSXin Long return sctp_make_strreset_tsnresp(asoc, result, request_seq, 773692787ceSXin Long next_tsn, init_tsn); 774692787ceSXin Long } 77550a41591SXin Long 77650a41591SXin Long struct sctp_chunk *sctp_process_strreset_addstrm_out( 77750a41591SXin Long struct sctp_association *asoc, 77850a41591SXin Long union sctp_params param, 77950a41591SXin Long struct sctp_ulpevent **evp) 78050a41591SXin Long { 78150a41591SXin Long struct sctp_strreset_addstrm *addstrm = param.v; 782cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 78350a41591SXin Long __u32 result = SCTP_STRRESET_DENIED; 78450a41591SXin Long __u32 request_seq, incnt; 785e4dc99c7SXin Long __u16 in, i; 78650a41591SXin Long 78750a41591SXin Long request_seq = ntohl(addstrm->request_seq); 788e4dc99c7SXin Long if (TSN_lt(asoc->strreset_inseq, request_seq) || 789e4dc99c7SXin Long TSN_lt(request_seq, asoc->strreset_inseq - 2)) { 79050a41591SXin Long result = SCTP_STRRESET_ERR_BAD_SEQNO; 791e4dc99c7SXin Long goto err; 792e4dc99c7SXin Long } else if (TSN_lt(request_seq, asoc->strreset_inseq)) { 793e4dc99c7SXin Long i = asoc->strreset_inseq - request_seq - 1; 794e4dc99c7SXin Long result = asoc->strreset_result[i]; 795e4dc99c7SXin Long goto err; 79650a41591SXin Long } 797e4dc99c7SXin Long asoc->strreset_inseq++; 79850a41591SXin Long 79950a41591SXin Long if (!(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ)) 80050a41591SXin Long goto out; 80150a41591SXin Long 8028220c870SXin Long in = ntohs(addstrm->number_of_streams); 8038220c870SXin Long incnt = stream->incnt + in; 8048220c870SXin Long if (!in || incnt > SCTP_MAX_STREAM) 8058220c870SXin Long goto out; 8068220c870SXin Long 8078220c870SXin Long if (sctp_stream_alloc_in(stream, incnt, GFP_ATOMIC)) 8088220c870SXin Long goto out; 8098220c870SXin Long 81050a41591SXin Long if (asoc->strreset_chunk) { 81150a41591SXin Long if (!sctp_chunk_lookup_strreset_param( 81250a41591SXin Long asoc, 0, SCTP_PARAM_RESET_ADD_IN_STREAMS)) { 81350a41591SXin Long /* same process with outstanding isn't 0 */ 81450a41591SXin Long result = SCTP_STRRESET_ERR_IN_PROGRESS; 81550a41591SXin Long goto out; 81650a41591SXin Long } 81750a41591SXin Long 81850a41591SXin Long asoc->strreset_outstanding--; 81950a41591SXin Long asoc->strreset_outseq++; 82050a41591SXin Long 82150a41591SXin Long if (!asoc->strreset_outstanding) { 82250a41591SXin Long struct sctp_transport *t; 82350a41591SXin Long 82450a41591SXin Long t = asoc->strreset_chunk->transport; 82550a41591SXin Long if (del_timer(&t->reconf_timer)) 82650a41591SXin Long sctp_transport_put(t); 82750a41591SXin Long 82850a41591SXin Long sctp_chunk_put(asoc->strreset_chunk); 82950a41591SXin Long asoc->strreset_chunk = NULL; 83050a41591SXin Long } 83150a41591SXin Long } 83250a41591SXin Long 83350a41591SXin Long stream->incnt = incnt; 83450a41591SXin Long 83550a41591SXin Long result = SCTP_STRRESET_PERFORMED; 83650a41591SXin Long 83750a41591SXin Long *evp = sctp_ulpevent_make_stream_change_event(asoc, 83850a41591SXin Long 0, ntohs(addstrm->number_of_streams), 0, GFP_ATOMIC); 83950a41591SXin Long 84050a41591SXin Long out: 841e4dc99c7SXin Long sctp_update_strreset_result(asoc, result); 842e4dc99c7SXin Long err: 84350a41591SXin Long return sctp_make_strreset_resp(asoc, result, request_seq); 84450a41591SXin Long } 845c5c4ebb3SXin Long 846c5c4ebb3SXin Long struct sctp_chunk *sctp_process_strreset_addstrm_in( 847c5c4ebb3SXin Long struct sctp_association *asoc, 848c5c4ebb3SXin Long union sctp_params param, 849c5c4ebb3SXin Long struct sctp_ulpevent **evp) 850c5c4ebb3SXin Long { 851c5c4ebb3SXin Long struct sctp_strreset_addstrm *addstrm = param.v; 852cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 853c5c4ebb3SXin Long __u32 result = SCTP_STRRESET_DENIED; 854c5c4ebb3SXin Long struct sctp_chunk *chunk = NULL; 855c5c4ebb3SXin Long __u32 request_seq, outcnt; 856d0f025e6SXin Long __u16 out, i; 857e090abd0SMarcelo Ricardo Leitner int ret; 858c5c4ebb3SXin Long 859c5c4ebb3SXin Long request_seq = ntohl(addstrm->request_seq); 860d0f025e6SXin Long if (TSN_lt(asoc->strreset_inseq, request_seq) || 861d0f025e6SXin Long TSN_lt(request_seq, asoc->strreset_inseq - 2)) { 862c5c4ebb3SXin Long result = SCTP_STRRESET_ERR_BAD_SEQNO; 863d0f025e6SXin Long goto err; 864d0f025e6SXin Long } else if (TSN_lt(request_seq, asoc->strreset_inseq)) { 865d0f025e6SXin Long i = asoc->strreset_inseq - request_seq - 1; 866d0f025e6SXin Long result = asoc->strreset_result[i]; 867d0f025e6SXin Long if (result == SCTP_STRRESET_PERFORMED) 868d0f025e6SXin Long return NULL; 869d0f025e6SXin Long goto err; 870c5c4ebb3SXin Long } 871d0f025e6SXin Long asoc->strreset_inseq++; 872c5c4ebb3SXin Long 873c5c4ebb3SXin Long if (!(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ)) 874c5c4ebb3SXin Long goto out; 875c5c4ebb3SXin Long 876c5c4ebb3SXin Long if (asoc->strreset_outstanding) { 877c5c4ebb3SXin Long result = SCTP_STRRESET_ERR_IN_PROGRESS; 878c5c4ebb3SXin Long goto out; 879c5c4ebb3SXin Long } 880c5c4ebb3SXin Long 881c5c4ebb3SXin Long out = ntohs(addstrm->number_of_streams); 882c5c4ebb3SXin Long outcnt = stream->outcnt + out; 883c5c4ebb3SXin Long if (!out || outcnt > SCTP_MAX_STREAM) 884c5c4ebb3SXin Long goto out; 885c5c4ebb3SXin Long 886e090abd0SMarcelo Ricardo Leitner ret = sctp_stream_alloc_out(stream, outcnt, GFP_ATOMIC); 887e090abd0SMarcelo Ricardo Leitner if (ret) 888c5c4ebb3SXin Long goto out; 889c5c4ebb3SXin Long 890c5c4ebb3SXin Long chunk = sctp_make_strreset_addstrm(asoc, out, 0); 891c5c4ebb3SXin Long if (!chunk) 892c5c4ebb3SXin Long goto out; 893c5c4ebb3SXin Long 894c5c4ebb3SXin Long asoc->strreset_chunk = chunk; 895c5c4ebb3SXin Long asoc->strreset_outstanding = 1; 896c5c4ebb3SXin Long sctp_chunk_hold(asoc->strreset_chunk); 897c5c4ebb3SXin Long 898c5c4ebb3SXin Long stream->outcnt = outcnt; 899c5c4ebb3SXin Long 900d0f025e6SXin Long result = SCTP_STRRESET_PERFORMED; 901d0f025e6SXin Long 902c5c4ebb3SXin Long out: 903d0f025e6SXin Long sctp_update_strreset_result(asoc, result); 904d0f025e6SXin Long err: 905c5c4ebb3SXin Long if (!chunk) 906c5c4ebb3SXin Long chunk = sctp_make_strreset_resp(asoc, result, request_seq); 907c5c4ebb3SXin Long 908c5c4ebb3SXin Long return chunk; 909c5c4ebb3SXin Long } 91011ae76e6SXin Long 91111ae76e6SXin Long struct sctp_chunk *sctp_process_strreset_resp( 91211ae76e6SXin Long struct sctp_association *asoc, 91311ae76e6SXin Long union sctp_params param, 91411ae76e6SXin Long struct sctp_ulpevent **evp) 91511ae76e6SXin Long { 916cee360abSXin Long struct sctp_stream *stream = &asoc->stream; 91711ae76e6SXin Long struct sctp_strreset_resp *resp = param.v; 91811ae76e6SXin Long struct sctp_transport *t; 91911ae76e6SXin Long __u16 i, nums, flags = 0; 9203c918704SXin Long struct sctp_paramhdr *req; 92111ae76e6SXin Long __u32 result; 92211ae76e6SXin Long 92311ae76e6SXin Long req = sctp_chunk_lookup_strreset_param(asoc, resp->response_seq, 0); 92411ae76e6SXin Long if (!req) 92511ae76e6SXin Long return NULL; 92611ae76e6SXin Long 92711ae76e6SXin Long result = ntohl(resp->result); 92811ae76e6SXin Long if (result != SCTP_STRRESET_PERFORMED) { 92911ae76e6SXin Long /* if in progress, do nothing but retransmit */ 93011ae76e6SXin Long if (result == SCTP_STRRESET_IN_PROGRESS) 93111ae76e6SXin Long return NULL; 93211ae76e6SXin Long else if (result == SCTP_STRRESET_DENIED) 93311ae76e6SXin Long flags = SCTP_STREAM_RESET_DENIED; 93411ae76e6SXin Long else 93511ae76e6SXin Long flags = SCTP_STREAM_RESET_FAILED; 93611ae76e6SXin Long } 93711ae76e6SXin Long 93811ae76e6SXin Long if (req->type == SCTP_PARAM_RESET_OUT_REQUEST) { 93911ae76e6SXin Long struct sctp_strreset_outreq *outreq; 9401da4fc97SXin Long __be16 *str_p; 94111ae76e6SXin Long 94211ae76e6SXin Long outreq = (struct sctp_strreset_outreq *)req; 943edb12f2dSXin Long str_p = outreq->list_of_streams; 9443aa623daSXin Long nums = (ntohs(outreq->param_hdr.length) - sizeof(*outreq)) / 9453aa623daSXin Long sizeof(__u16); 94611ae76e6SXin Long 94711ae76e6SXin Long if (result == SCTP_STRRESET_PERFORMED) { 94805364ca0SKonstantin Khorenko struct sctp_stream_out *sout; 94911ae76e6SXin Long if (nums) { 950107e2425SXin Long for (i = 0; i < nums; i++) { 95105364ca0SKonstantin Khorenko sout = SCTP_SO(stream, ntohs(str_p[i])); 95205364ca0SKonstantin Khorenko sout->mid = 0; 95305364ca0SKonstantin Khorenko sout->mid_uo = 0; 954107e2425SXin Long } 95511ae76e6SXin Long } else { 956107e2425SXin Long for (i = 0; i < stream->outcnt; i++) { 95705364ca0SKonstantin Khorenko sout = SCTP_SO(stream, i); 95805364ca0SKonstantin Khorenko sout->mid = 0; 95905364ca0SKonstantin Khorenko sout->mid_uo = 0; 960107e2425SXin Long } 96111ae76e6SXin Long } 96211ae76e6SXin Long } 96311ae76e6SXin Long 9642e6dc4d9SXin Long flags |= SCTP_STREAM_RESET_OUTGOING_SSN; 9652e6dc4d9SXin Long 96611ae76e6SXin Long for (i = 0; i < stream->outcnt; i++) 96705364ca0SKonstantin Khorenko SCTP_SO(stream, i)->state = SCTP_STREAM_OPEN; 96811ae76e6SXin Long 96911ae76e6SXin Long *evp = sctp_ulpevent_make_stream_reset_event(asoc, flags, 97011ae76e6SXin Long nums, str_p, GFP_ATOMIC); 97111ae76e6SXin Long } else if (req->type == SCTP_PARAM_RESET_IN_REQUEST) { 97211ae76e6SXin Long struct sctp_strreset_inreq *inreq; 9731da4fc97SXin Long __be16 *str_p; 97411ae76e6SXin Long 97511ae76e6SXin Long /* if the result is performed, it's impossible for inreq */ 97611ae76e6SXin Long if (result == SCTP_STRRESET_PERFORMED) 97711ae76e6SXin Long return NULL; 97811ae76e6SXin Long 97911ae76e6SXin Long inreq = (struct sctp_strreset_inreq *)req; 980edb12f2dSXin Long str_p = inreq->list_of_streams; 9813aa623daSXin Long nums = (ntohs(inreq->param_hdr.length) - sizeof(*inreq)) / 9823aa623daSXin Long sizeof(__u16); 98311ae76e6SXin Long 9842e6dc4d9SXin Long flags |= SCTP_STREAM_RESET_INCOMING_SSN; 9852e6dc4d9SXin Long 98611ae76e6SXin Long *evp = sctp_ulpevent_make_stream_reset_event(asoc, flags, 98711ae76e6SXin Long nums, str_p, GFP_ATOMIC); 98811ae76e6SXin Long } else if (req->type == SCTP_PARAM_RESET_TSN_REQUEST) { 98911ae76e6SXin Long struct sctp_strreset_resptsn *resptsn; 99011ae76e6SXin Long __u32 stsn, rtsn; 99111ae76e6SXin Long 99211ae76e6SXin Long /* check for resptsn, as sctp_verify_reconf didn't do it*/ 99311ae76e6SXin Long if (ntohs(param.p->length) != sizeof(*resptsn)) 99411ae76e6SXin Long return NULL; 99511ae76e6SXin Long 99611ae76e6SXin Long resptsn = (struct sctp_strreset_resptsn *)resp; 99711ae76e6SXin Long stsn = ntohl(resptsn->senders_next_tsn); 99811ae76e6SXin Long rtsn = ntohl(resptsn->receivers_next_tsn); 99911ae76e6SXin Long 100011ae76e6SXin Long if (result == SCTP_STRRESET_PERFORMED) { 100111ae76e6SXin Long __u32 mtsn = sctp_tsnmap_get_max_tsn_seen( 100211ae76e6SXin Long &asoc->peer.tsn_map); 1003159f2a74SXin Long LIST_HEAD(temp); 100411ae76e6SXin Long 100547b20a88SXin Long asoc->stream.si->report_ftsn(&asoc->ulpq, mtsn); 100611ae76e6SXin Long 100711ae76e6SXin Long sctp_tsnmap_init(&asoc->peer.tsn_map, 100811ae76e6SXin Long SCTP_TSN_MAP_INITIAL, 100911ae76e6SXin Long stsn, GFP_ATOMIC); 101011ae76e6SXin Long 1011159f2a74SXin Long /* Clean up sacked and abandoned queues only. As the 1012159f2a74SXin Long * out_chunk_list may not be empty, splice it to temp, 1013159f2a74SXin Long * then get it back after sctp_outq_free is done. 1014159f2a74SXin Long */ 1015159f2a74SXin Long list_splice_init(&asoc->outqueue.out_chunk_list, &temp); 101611ae76e6SXin Long sctp_outq_free(&asoc->outqueue); 1017159f2a74SXin Long list_splice_init(&temp, &asoc->outqueue.out_chunk_list); 101811ae76e6SXin Long 101911ae76e6SXin Long asoc->next_tsn = rtsn; 102011ae76e6SXin Long asoc->ctsn_ack_point = asoc->next_tsn - 1; 102111ae76e6SXin Long asoc->adv_peer_ack_point = asoc->ctsn_ack_point; 102211ae76e6SXin Long 1023107e2425SXin Long for (i = 0; i < stream->outcnt; i++) { 102405364ca0SKonstantin Khorenko SCTP_SO(stream, i)->mid = 0; 102505364ca0SKonstantin Khorenko SCTP_SO(stream, i)->mid_uo = 0; 1026107e2425SXin Long } 102711ae76e6SXin Long for (i = 0; i < stream->incnt; i++) 102805364ca0SKonstantin Khorenko SCTP_SI(stream, i)->mid = 0; 102911ae76e6SXin Long } 103011ae76e6SXin Long 103111ae76e6SXin Long for (i = 0; i < stream->outcnt; i++) 103205364ca0SKonstantin Khorenko SCTP_SO(stream, i)->state = SCTP_STREAM_OPEN; 103311ae76e6SXin Long 103411ae76e6SXin Long *evp = sctp_ulpevent_make_assoc_reset_event(asoc, flags, 103511ae76e6SXin Long stsn, rtsn, GFP_ATOMIC); 103611ae76e6SXin Long } else if (req->type == SCTP_PARAM_RESET_ADD_OUT_STREAMS) { 103711ae76e6SXin Long struct sctp_strreset_addstrm *addstrm; 103811ae76e6SXin Long __u16 number; 103911ae76e6SXin Long 104011ae76e6SXin Long addstrm = (struct sctp_strreset_addstrm *)req; 104111ae76e6SXin Long nums = ntohs(addstrm->number_of_streams); 104211ae76e6SXin Long number = stream->outcnt - nums; 104311ae76e6SXin Long 104411ae76e6SXin Long if (result == SCTP_STRRESET_PERFORMED) 104511ae76e6SXin Long for (i = number; i < stream->outcnt; i++) 104605364ca0SKonstantin Khorenko SCTP_SO(stream, i)->state = SCTP_STREAM_OPEN; 104711ae76e6SXin Long else 104811ae76e6SXin Long stream->outcnt = number; 104911ae76e6SXin Long 105011ae76e6SXin Long *evp = sctp_ulpevent_make_stream_change_event(asoc, flags, 105111ae76e6SXin Long 0, nums, GFP_ATOMIC); 105211ae76e6SXin Long } else if (req->type == SCTP_PARAM_RESET_ADD_IN_STREAMS) { 105311ae76e6SXin Long struct sctp_strreset_addstrm *addstrm; 105411ae76e6SXin Long 105511ae76e6SXin Long /* if the result is performed, it's impossible for addstrm in 105611ae76e6SXin Long * request. 105711ae76e6SXin Long */ 105811ae76e6SXin Long if (result == SCTP_STRRESET_PERFORMED) 105911ae76e6SXin Long return NULL; 106011ae76e6SXin Long 106111ae76e6SXin Long addstrm = (struct sctp_strreset_addstrm *)req; 106211ae76e6SXin Long nums = ntohs(addstrm->number_of_streams); 106311ae76e6SXin Long 106411ae76e6SXin Long *evp = sctp_ulpevent_make_stream_change_event(asoc, flags, 106511ae76e6SXin Long nums, 0, GFP_ATOMIC); 106611ae76e6SXin Long } 106711ae76e6SXin Long 106811ae76e6SXin Long asoc->strreset_outstanding--; 106911ae76e6SXin Long asoc->strreset_outseq++; 107011ae76e6SXin Long 107111ae76e6SXin Long /* remove everything for this reconf request */ 107211ae76e6SXin Long if (!asoc->strreset_outstanding) { 107311ae76e6SXin Long t = asoc->strreset_chunk->transport; 107411ae76e6SXin Long if (del_timer(&t->reconf_timer)) 107511ae76e6SXin Long sctp_transport_put(t); 107611ae76e6SXin Long 107711ae76e6SXin Long sctp_chunk_put(asoc->strreset_chunk); 107811ae76e6SXin Long asoc->strreset_chunk = NULL; 107911ae76e6SXin Long } 108011ae76e6SXin Long 108111ae76e6SXin Long return NULL; 108211ae76e6SXin Long } 1083