162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* SCTP kernel implementation 362306a36Sopenharmony_ci * (C) Copyright IBM Corp. 2001, 2004 462306a36Sopenharmony_ci * Copyright (c) 1999-2000 Cisco, Inc. 562306a36Sopenharmony_ci * Copyright (c) 1999-2001 Motorola, Inc. 662306a36Sopenharmony_ci * Copyright (c) 2001 Intel Corp. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * This file is part of the SCTP kernel implementation 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * This file contains sctp stream maniuplation primitives and helpers. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Please send any bug reports or fixes you make to the 1362306a36Sopenharmony_ci * email address(es): 1462306a36Sopenharmony_ci * lksctp developers <linux-sctp@vger.kernel.org> 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * Written or modified by: 1762306a36Sopenharmony_ci * Xin Long <lucien.xin@gmail.com> 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <linux/list.h> 2162306a36Sopenharmony_ci#include <net/sctp/sctp.h> 2262306a36Sopenharmony_ci#include <net/sctp/sm.h> 2362306a36Sopenharmony_ci#include <net/sctp/stream_sched.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic void sctp_stream_shrink_out(struct sctp_stream *stream, __u16 outcnt) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci struct sctp_association *asoc; 2862306a36Sopenharmony_ci struct sctp_chunk *ch, *temp; 2962306a36Sopenharmony_ci struct sctp_outq *outq; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci asoc = container_of(stream, struct sctp_association, stream); 3262306a36Sopenharmony_ci outq = &asoc->outqueue; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci list_for_each_entry_safe(ch, temp, &outq->out_chunk_list, list) { 3562306a36Sopenharmony_ci __u16 sid = sctp_chunk_stream_no(ch); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci if (sid < outcnt) 3862306a36Sopenharmony_ci continue; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci sctp_sched_dequeue_common(outq, ch); 4162306a36Sopenharmony_ci /* No need to call dequeue_done here because 4262306a36Sopenharmony_ci * the chunks are not scheduled by now. 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci /* Mark as failed send. */ 4662306a36Sopenharmony_ci sctp_chunk_fail(ch, (__force __u32)SCTP_ERROR_INV_STRM); 4762306a36Sopenharmony_ci if (asoc->peer.prsctp_capable && 4862306a36Sopenharmony_ci SCTP_PR_PRIO_ENABLED(ch->sinfo.sinfo_flags)) 4962306a36Sopenharmony_ci asoc->sent_cnt_removable--; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci sctp_chunk_free(ch); 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic void sctp_stream_free_ext(struct sctp_stream *stream, __u16 sid) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci struct sctp_sched_ops *sched; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci if (!SCTP_SO(stream, sid)->ext) 6062306a36Sopenharmony_ci return; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci sched = sctp_sched_ops_from_stream(stream); 6362306a36Sopenharmony_ci sched->free_sid(stream, sid); 6462306a36Sopenharmony_ci kfree(SCTP_SO(stream, sid)->ext); 6562306a36Sopenharmony_ci SCTP_SO(stream, sid)->ext = NULL; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci/* Migrates chunks from stream queues to new stream queues if needed, 6962306a36Sopenharmony_ci * but not across associations. Also, removes those chunks to streams 7062306a36Sopenharmony_ci * higher than the new max. 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_cistatic void sctp_stream_outq_migrate(struct sctp_stream *stream, 7362306a36Sopenharmony_ci struct sctp_stream *new, __u16 outcnt) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci int i; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci if (stream->outcnt > outcnt) 7862306a36Sopenharmony_ci sctp_stream_shrink_out(stream, outcnt); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci if (new) { 8162306a36Sopenharmony_ci /* Here we actually move the old ext stuff into the new 8262306a36Sopenharmony_ci * buffer, because we want to keep it. Then 8362306a36Sopenharmony_ci * sctp_stream_update will swap ->out pointers. 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_ci for (i = 0; i < outcnt; i++) { 8662306a36Sopenharmony_ci sctp_stream_free_ext(new, i); 8762306a36Sopenharmony_ci SCTP_SO(new, i)->ext = SCTP_SO(stream, i)->ext; 8862306a36Sopenharmony_ci SCTP_SO(stream, i)->ext = NULL; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci for (i = outcnt; i < stream->outcnt; i++) 9362306a36Sopenharmony_ci sctp_stream_free_ext(stream, i); 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic int sctp_stream_alloc_out(struct sctp_stream *stream, __u16 outcnt, 9762306a36Sopenharmony_ci gfp_t gfp) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci int ret; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (outcnt <= stream->outcnt) 10262306a36Sopenharmony_ci goto out; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci ret = genradix_prealloc(&stream->out, outcnt, gfp); 10562306a36Sopenharmony_ci if (ret) 10662306a36Sopenharmony_ci return ret; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ciout: 10962306a36Sopenharmony_ci stream->outcnt = outcnt; 11062306a36Sopenharmony_ci return 0; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic int sctp_stream_alloc_in(struct sctp_stream *stream, __u16 incnt, 11462306a36Sopenharmony_ci gfp_t gfp) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci int ret; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci if (incnt <= stream->incnt) 11962306a36Sopenharmony_ci goto out; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci ret = genradix_prealloc(&stream->in, incnt, gfp); 12262306a36Sopenharmony_ci if (ret) 12362306a36Sopenharmony_ci return ret; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ciout: 12662306a36Sopenharmony_ci stream->incnt = incnt; 12762306a36Sopenharmony_ci return 0; 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ciint sctp_stream_init(struct sctp_stream *stream, __u16 outcnt, __u16 incnt, 13162306a36Sopenharmony_ci gfp_t gfp) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream); 13462306a36Sopenharmony_ci int i, ret = 0; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci gfp |= __GFP_NOWARN; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci /* Initial stream->out size may be very big, so free it and alloc 13962306a36Sopenharmony_ci * a new one with new outcnt to save memory if needed. 14062306a36Sopenharmony_ci */ 14162306a36Sopenharmony_ci if (outcnt == stream->outcnt) 14262306a36Sopenharmony_ci goto handle_in; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* Filter out chunks queued on streams that won't exist anymore */ 14562306a36Sopenharmony_ci sched->unsched_all(stream); 14662306a36Sopenharmony_ci sctp_stream_outq_migrate(stream, NULL, outcnt); 14762306a36Sopenharmony_ci sched->sched_all(stream); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci ret = sctp_stream_alloc_out(stream, outcnt, gfp); 15062306a36Sopenharmony_ci if (ret) 15162306a36Sopenharmony_ci return ret; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci for (i = 0; i < stream->outcnt; i++) 15462306a36Sopenharmony_ci SCTP_SO(stream, i)->state = SCTP_STREAM_OPEN; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cihandle_in: 15762306a36Sopenharmony_ci sctp_stream_interleave_init(stream); 15862306a36Sopenharmony_ci if (!incnt) 15962306a36Sopenharmony_ci return 0; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci return sctp_stream_alloc_in(stream, incnt, gfp); 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ciint sctp_stream_init_ext(struct sctp_stream *stream, __u16 sid) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci struct sctp_stream_out_ext *soute; 16762306a36Sopenharmony_ci int ret; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci soute = kzalloc(sizeof(*soute), GFP_KERNEL); 17062306a36Sopenharmony_ci if (!soute) 17162306a36Sopenharmony_ci return -ENOMEM; 17262306a36Sopenharmony_ci SCTP_SO(stream, sid)->ext = soute; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci ret = sctp_sched_init_sid(stream, sid, GFP_KERNEL); 17562306a36Sopenharmony_ci if (ret) { 17662306a36Sopenharmony_ci kfree(SCTP_SO(stream, sid)->ext); 17762306a36Sopenharmony_ci SCTP_SO(stream, sid)->ext = NULL; 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci return ret; 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_civoid sctp_stream_free(struct sctp_stream *stream) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream); 18662306a36Sopenharmony_ci int i; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci sched->unsched_all(stream); 18962306a36Sopenharmony_ci for (i = 0; i < stream->outcnt; i++) 19062306a36Sopenharmony_ci sctp_stream_free_ext(stream, i); 19162306a36Sopenharmony_ci genradix_free(&stream->out); 19262306a36Sopenharmony_ci genradix_free(&stream->in); 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_civoid sctp_stream_clear(struct sctp_stream *stream) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci int i; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci for (i = 0; i < stream->outcnt; i++) { 20062306a36Sopenharmony_ci SCTP_SO(stream, i)->mid = 0; 20162306a36Sopenharmony_ci SCTP_SO(stream, i)->mid_uo = 0; 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci for (i = 0; i < stream->incnt; i++) 20562306a36Sopenharmony_ci SCTP_SI(stream, i)->mid = 0; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_civoid sctp_stream_update(struct sctp_stream *stream, struct sctp_stream *new) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci sched->unsched_all(stream); 21362306a36Sopenharmony_ci sctp_stream_outq_migrate(stream, new, new->outcnt); 21462306a36Sopenharmony_ci sctp_stream_free(stream); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci stream->out = new->out; 21762306a36Sopenharmony_ci stream->in = new->in; 21862306a36Sopenharmony_ci stream->outcnt = new->outcnt; 21962306a36Sopenharmony_ci stream->incnt = new->incnt; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci sched->sched_all(stream); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci new->out.tree.root = NULL; 22462306a36Sopenharmony_ci new->in.tree.root = NULL; 22562306a36Sopenharmony_ci new->outcnt = 0; 22662306a36Sopenharmony_ci new->incnt = 0; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic int sctp_send_reconf(struct sctp_association *asoc, 23062306a36Sopenharmony_ci struct sctp_chunk *chunk) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci int retval = 0; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci retval = sctp_primitive_RECONF(asoc->base.net, asoc, chunk); 23562306a36Sopenharmony_ci if (retval) 23662306a36Sopenharmony_ci sctp_chunk_free(chunk); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci return retval; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic bool sctp_stream_outq_is_empty(struct sctp_stream *stream, 24262306a36Sopenharmony_ci __u16 str_nums, __be16 *str_list) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci struct sctp_association *asoc; 24562306a36Sopenharmony_ci __u16 i; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci asoc = container_of(stream, struct sctp_association, stream); 24862306a36Sopenharmony_ci if (!asoc->outqueue.out_qlen) 24962306a36Sopenharmony_ci return true; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (!str_nums) 25262306a36Sopenharmony_ci return false; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci for (i = 0; i < str_nums; i++) { 25562306a36Sopenharmony_ci __u16 sid = ntohs(str_list[i]); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (SCTP_SO(stream, sid)->ext && 25862306a36Sopenharmony_ci !list_empty(&SCTP_SO(stream, sid)->ext->outq)) 25962306a36Sopenharmony_ci return false; 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci return true; 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ciint sctp_send_reset_streams(struct sctp_association *asoc, 26662306a36Sopenharmony_ci struct sctp_reset_streams *params) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci struct sctp_stream *stream = &asoc->stream; 26962306a36Sopenharmony_ci __u16 i, str_nums, *str_list; 27062306a36Sopenharmony_ci struct sctp_chunk *chunk; 27162306a36Sopenharmony_ci int retval = -EINVAL; 27262306a36Sopenharmony_ci __be16 *nstr_list; 27362306a36Sopenharmony_ci bool out, in; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if (!asoc->peer.reconf_capable || 27662306a36Sopenharmony_ci !(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ)) { 27762306a36Sopenharmony_ci retval = -ENOPROTOOPT; 27862306a36Sopenharmony_ci goto out; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (asoc->strreset_outstanding) { 28262306a36Sopenharmony_ci retval = -EINPROGRESS; 28362306a36Sopenharmony_ci goto out; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci out = params->srs_flags & SCTP_STREAM_RESET_OUTGOING; 28762306a36Sopenharmony_ci in = params->srs_flags & SCTP_STREAM_RESET_INCOMING; 28862306a36Sopenharmony_ci if (!out && !in) 28962306a36Sopenharmony_ci goto out; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci str_nums = params->srs_number_streams; 29262306a36Sopenharmony_ci str_list = params->srs_stream_list; 29362306a36Sopenharmony_ci if (str_nums) { 29462306a36Sopenharmony_ci int param_len = 0; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci if (out) { 29762306a36Sopenharmony_ci for (i = 0; i < str_nums; i++) 29862306a36Sopenharmony_ci if (str_list[i] >= stream->outcnt) 29962306a36Sopenharmony_ci goto out; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci param_len = str_nums * sizeof(__u16) + 30262306a36Sopenharmony_ci sizeof(struct sctp_strreset_outreq); 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (in) { 30662306a36Sopenharmony_ci for (i = 0; i < str_nums; i++) 30762306a36Sopenharmony_ci if (str_list[i] >= stream->incnt) 30862306a36Sopenharmony_ci goto out; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci param_len += str_nums * sizeof(__u16) + 31162306a36Sopenharmony_ci sizeof(struct sctp_strreset_inreq); 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (param_len > SCTP_MAX_CHUNK_LEN - 31562306a36Sopenharmony_ci sizeof(struct sctp_reconf_chunk)) 31662306a36Sopenharmony_ci goto out; 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci nstr_list = kcalloc(str_nums, sizeof(__be16), GFP_KERNEL); 32062306a36Sopenharmony_ci if (!nstr_list) { 32162306a36Sopenharmony_ci retval = -ENOMEM; 32262306a36Sopenharmony_ci goto out; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci for (i = 0; i < str_nums; i++) 32662306a36Sopenharmony_ci nstr_list[i] = htons(str_list[i]); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (out && !sctp_stream_outq_is_empty(stream, str_nums, nstr_list)) { 32962306a36Sopenharmony_ci kfree(nstr_list); 33062306a36Sopenharmony_ci retval = -EAGAIN; 33162306a36Sopenharmony_ci goto out; 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci chunk = sctp_make_strreset_req(asoc, str_nums, nstr_list, out, in); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci kfree(nstr_list); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if (!chunk) { 33962306a36Sopenharmony_ci retval = -ENOMEM; 34062306a36Sopenharmony_ci goto out; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (out) { 34462306a36Sopenharmony_ci if (str_nums) 34562306a36Sopenharmony_ci for (i = 0; i < str_nums; i++) 34662306a36Sopenharmony_ci SCTP_SO(stream, str_list[i])->state = 34762306a36Sopenharmony_ci SCTP_STREAM_CLOSED; 34862306a36Sopenharmony_ci else 34962306a36Sopenharmony_ci for (i = 0; i < stream->outcnt; i++) 35062306a36Sopenharmony_ci SCTP_SO(stream, i)->state = SCTP_STREAM_CLOSED; 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci asoc->strreset_chunk = chunk; 35462306a36Sopenharmony_ci sctp_chunk_hold(asoc->strreset_chunk); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci retval = sctp_send_reconf(asoc, chunk); 35762306a36Sopenharmony_ci if (retval) { 35862306a36Sopenharmony_ci sctp_chunk_put(asoc->strreset_chunk); 35962306a36Sopenharmony_ci asoc->strreset_chunk = NULL; 36062306a36Sopenharmony_ci if (!out) 36162306a36Sopenharmony_ci goto out; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci if (str_nums) 36462306a36Sopenharmony_ci for (i = 0; i < str_nums; i++) 36562306a36Sopenharmony_ci SCTP_SO(stream, str_list[i])->state = 36662306a36Sopenharmony_ci SCTP_STREAM_OPEN; 36762306a36Sopenharmony_ci else 36862306a36Sopenharmony_ci for (i = 0; i < stream->outcnt; i++) 36962306a36Sopenharmony_ci SCTP_SO(stream, i)->state = SCTP_STREAM_OPEN; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci goto out; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci asoc->strreset_outstanding = out + in; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ciout: 37762306a36Sopenharmony_ci return retval; 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ciint sctp_send_reset_assoc(struct sctp_association *asoc) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci struct sctp_stream *stream = &asoc->stream; 38362306a36Sopenharmony_ci struct sctp_chunk *chunk = NULL; 38462306a36Sopenharmony_ci int retval; 38562306a36Sopenharmony_ci __u16 i; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci if (!asoc->peer.reconf_capable || 38862306a36Sopenharmony_ci !(asoc->strreset_enable & SCTP_ENABLE_RESET_ASSOC_REQ)) 38962306a36Sopenharmony_ci return -ENOPROTOOPT; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci if (asoc->strreset_outstanding) 39262306a36Sopenharmony_ci return -EINPROGRESS; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci if (!sctp_outq_is_empty(&asoc->outqueue)) 39562306a36Sopenharmony_ci return -EAGAIN; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci chunk = sctp_make_strreset_tsnreq(asoc); 39862306a36Sopenharmony_ci if (!chunk) 39962306a36Sopenharmony_ci return -ENOMEM; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci /* Block further xmit of data until this request is completed */ 40262306a36Sopenharmony_ci for (i = 0; i < stream->outcnt; i++) 40362306a36Sopenharmony_ci SCTP_SO(stream, i)->state = SCTP_STREAM_CLOSED; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci asoc->strreset_chunk = chunk; 40662306a36Sopenharmony_ci sctp_chunk_hold(asoc->strreset_chunk); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci retval = sctp_send_reconf(asoc, chunk); 40962306a36Sopenharmony_ci if (retval) { 41062306a36Sopenharmony_ci sctp_chunk_put(asoc->strreset_chunk); 41162306a36Sopenharmony_ci asoc->strreset_chunk = NULL; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci for (i = 0; i < stream->outcnt; i++) 41462306a36Sopenharmony_ci SCTP_SO(stream, i)->state = SCTP_STREAM_OPEN; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci return retval; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci asoc->strreset_outstanding = 1; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci return 0; 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ciint sctp_send_add_streams(struct sctp_association *asoc, 42562306a36Sopenharmony_ci struct sctp_add_streams *params) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci struct sctp_stream *stream = &asoc->stream; 42862306a36Sopenharmony_ci struct sctp_chunk *chunk = NULL; 42962306a36Sopenharmony_ci int retval; 43062306a36Sopenharmony_ci __u32 outcnt, incnt; 43162306a36Sopenharmony_ci __u16 out, in; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci if (!asoc->peer.reconf_capable || 43462306a36Sopenharmony_ci !(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ)) { 43562306a36Sopenharmony_ci retval = -ENOPROTOOPT; 43662306a36Sopenharmony_ci goto out; 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci if (asoc->strreset_outstanding) { 44062306a36Sopenharmony_ci retval = -EINPROGRESS; 44162306a36Sopenharmony_ci goto out; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci out = params->sas_outstrms; 44562306a36Sopenharmony_ci in = params->sas_instrms; 44662306a36Sopenharmony_ci outcnt = stream->outcnt + out; 44762306a36Sopenharmony_ci incnt = stream->incnt + in; 44862306a36Sopenharmony_ci if (outcnt > SCTP_MAX_STREAM || incnt > SCTP_MAX_STREAM || 44962306a36Sopenharmony_ci (!out && !in)) { 45062306a36Sopenharmony_ci retval = -EINVAL; 45162306a36Sopenharmony_ci goto out; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci if (out) { 45562306a36Sopenharmony_ci retval = sctp_stream_alloc_out(stream, outcnt, GFP_KERNEL); 45662306a36Sopenharmony_ci if (retval) 45762306a36Sopenharmony_ci goto out; 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci chunk = sctp_make_strreset_addstrm(asoc, out, in); 46162306a36Sopenharmony_ci if (!chunk) { 46262306a36Sopenharmony_ci retval = -ENOMEM; 46362306a36Sopenharmony_ci goto out; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci asoc->strreset_chunk = chunk; 46762306a36Sopenharmony_ci sctp_chunk_hold(asoc->strreset_chunk); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci retval = sctp_send_reconf(asoc, chunk); 47062306a36Sopenharmony_ci if (retval) { 47162306a36Sopenharmony_ci sctp_chunk_put(asoc->strreset_chunk); 47262306a36Sopenharmony_ci asoc->strreset_chunk = NULL; 47362306a36Sopenharmony_ci goto out; 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci asoc->strreset_outstanding = !!out + !!in; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ciout: 47962306a36Sopenharmony_ci return retval; 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cistatic struct sctp_paramhdr *sctp_chunk_lookup_strreset_param( 48362306a36Sopenharmony_ci struct sctp_association *asoc, __be32 resp_seq, 48462306a36Sopenharmony_ci __be16 type) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci struct sctp_chunk *chunk = asoc->strreset_chunk; 48762306a36Sopenharmony_ci struct sctp_reconf_chunk *hdr; 48862306a36Sopenharmony_ci union sctp_params param; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci if (!chunk) 49162306a36Sopenharmony_ci return NULL; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci hdr = (struct sctp_reconf_chunk *)chunk->chunk_hdr; 49462306a36Sopenharmony_ci sctp_walk_params(param, hdr) { 49562306a36Sopenharmony_ci /* sctp_strreset_tsnreq is actually the basic structure 49662306a36Sopenharmony_ci * of all stream reconf params, so it's safe to use it 49762306a36Sopenharmony_ci * to access request_seq. 49862306a36Sopenharmony_ci */ 49962306a36Sopenharmony_ci struct sctp_strreset_tsnreq *req = param.v; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci if ((!resp_seq || req->request_seq == resp_seq) && 50262306a36Sopenharmony_ci (!type || type == req->param_hdr.type)) 50362306a36Sopenharmony_ci return param.v; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci return NULL; 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_cistatic void sctp_update_strreset_result(struct sctp_association *asoc, 51062306a36Sopenharmony_ci __u32 result) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci asoc->strreset_result[1] = asoc->strreset_result[0]; 51362306a36Sopenharmony_ci asoc->strreset_result[0] = result; 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_cistruct sctp_chunk *sctp_process_strreset_outreq( 51762306a36Sopenharmony_ci struct sctp_association *asoc, 51862306a36Sopenharmony_ci union sctp_params param, 51962306a36Sopenharmony_ci struct sctp_ulpevent **evp) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci struct sctp_strreset_outreq *outreq = param.v; 52262306a36Sopenharmony_ci struct sctp_stream *stream = &asoc->stream; 52362306a36Sopenharmony_ci __u32 result = SCTP_STRRESET_DENIED; 52462306a36Sopenharmony_ci __be16 *str_p = NULL; 52562306a36Sopenharmony_ci __u32 request_seq; 52662306a36Sopenharmony_ci __u16 i, nums; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci request_seq = ntohl(outreq->request_seq); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci if (ntohl(outreq->send_reset_at_tsn) > 53162306a36Sopenharmony_ci sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map)) { 53262306a36Sopenharmony_ci result = SCTP_STRRESET_IN_PROGRESS; 53362306a36Sopenharmony_ci goto err; 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci if (TSN_lt(asoc->strreset_inseq, request_seq) || 53762306a36Sopenharmony_ci TSN_lt(request_seq, asoc->strreset_inseq - 2)) { 53862306a36Sopenharmony_ci result = SCTP_STRRESET_ERR_BAD_SEQNO; 53962306a36Sopenharmony_ci goto err; 54062306a36Sopenharmony_ci } else if (TSN_lt(request_seq, asoc->strreset_inseq)) { 54162306a36Sopenharmony_ci i = asoc->strreset_inseq - request_seq - 1; 54262306a36Sopenharmony_ci result = asoc->strreset_result[i]; 54362306a36Sopenharmony_ci goto err; 54462306a36Sopenharmony_ci } 54562306a36Sopenharmony_ci asoc->strreset_inseq++; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci /* Check strreset_enable after inseq inc, as sender cannot tell 54862306a36Sopenharmony_ci * the peer doesn't enable strreset after receiving response with 54962306a36Sopenharmony_ci * result denied, as well as to keep consistent with bsd. 55062306a36Sopenharmony_ci */ 55162306a36Sopenharmony_ci if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ)) 55262306a36Sopenharmony_ci goto out; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci nums = (ntohs(param.p->length) - sizeof(*outreq)) / sizeof(__u16); 55562306a36Sopenharmony_ci str_p = outreq->list_of_streams; 55662306a36Sopenharmony_ci for (i = 0; i < nums; i++) { 55762306a36Sopenharmony_ci if (ntohs(str_p[i]) >= stream->incnt) { 55862306a36Sopenharmony_ci result = SCTP_STRRESET_ERR_WRONG_SSN; 55962306a36Sopenharmony_ci goto out; 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci if (asoc->strreset_chunk) { 56462306a36Sopenharmony_ci if (!sctp_chunk_lookup_strreset_param( 56562306a36Sopenharmony_ci asoc, outreq->response_seq, 56662306a36Sopenharmony_ci SCTP_PARAM_RESET_IN_REQUEST)) { 56762306a36Sopenharmony_ci /* same process with outstanding isn't 0 */ 56862306a36Sopenharmony_ci result = SCTP_STRRESET_ERR_IN_PROGRESS; 56962306a36Sopenharmony_ci goto out; 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci asoc->strreset_outstanding--; 57362306a36Sopenharmony_ci asoc->strreset_outseq++; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci if (!asoc->strreset_outstanding) { 57662306a36Sopenharmony_ci struct sctp_transport *t; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci t = asoc->strreset_chunk->transport; 57962306a36Sopenharmony_ci if (del_timer(&t->reconf_timer)) 58062306a36Sopenharmony_ci sctp_transport_put(t); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci sctp_chunk_put(asoc->strreset_chunk); 58362306a36Sopenharmony_ci asoc->strreset_chunk = NULL; 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci if (nums) 58862306a36Sopenharmony_ci for (i = 0; i < nums; i++) 58962306a36Sopenharmony_ci SCTP_SI(stream, ntohs(str_p[i]))->mid = 0; 59062306a36Sopenharmony_ci else 59162306a36Sopenharmony_ci for (i = 0; i < stream->incnt; i++) 59262306a36Sopenharmony_ci SCTP_SI(stream, i)->mid = 0; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci result = SCTP_STRRESET_PERFORMED; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci *evp = sctp_ulpevent_make_stream_reset_event(asoc, 59762306a36Sopenharmony_ci SCTP_STREAM_RESET_INCOMING_SSN, nums, str_p, GFP_ATOMIC); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ciout: 60062306a36Sopenharmony_ci sctp_update_strreset_result(asoc, result); 60162306a36Sopenharmony_cierr: 60262306a36Sopenharmony_ci return sctp_make_strreset_resp(asoc, result, request_seq); 60362306a36Sopenharmony_ci} 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_cistruct sctp_chunk *sctp_process_strreset_inreq( 60662306a36Sopenharmony_ci struct sctp_association *asoc, 60762306a36Sopenharmony_ci union sctp_params param, 60862306a36Sopenharmony_ci struct sctp_ulpevent **evp) 60962306a36Sopenharmony_ci{ 61062306a36Sopenharmony_ci struct sctp_strreset_inreq *inreq = param.v; 61162306a36Sopenharmony_ci struct sctp_stream *stream = &asoc->stream; 61262306a36Sopenharmony_ci __u32 result = SCTP_STRRESET_DENIED; 61362306a36Sopenharmony_ci struct sctp_chunk *chunk = NULL; 61462306a36Sopenharmony_ci __u32 request_seq; 61562306a36Sopenharmony_ci __u16 i, nums; 61662306a36Sopenharmony_ci __be16 *str_p; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci request_seq = ntohl(inreq->request_seq); 61962306a36Sopenharmony_ci if (TSN_lt(asoc->strreset_inseq, request_seq) || 62062306a36Sopenharmony_ci TSN_lt(request_seq, asoc->strreset_inseq - 2)) { 62162306a36Sopenharmony_ci result = SCTP_STRRESET_ERR_BAD_SEQNO; 62262306a36Sopenharmony_ci goto err; 62362306a36Sopenharmony_ci } else if (TSN_lt(request_seq, asoc->strreset_inseq)) { 62462306a36Sopenharmony_ci i = asoc->strreset_inseq - request_seq - 1; 62562306a36Sopenharmony_ci result = asoc->strreset_result[i]; 62662306a36Sopenharmony_ci if (result == SCTP_STRRESET_PERFORMED) 62762306a36Sopenharmony_ci return NULL; 62862306a36Sopenharmony_ci goto err; 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci asoc->strreset_inseq++; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ)) 63362306a36Sopenharmony_ci goto out; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci if (asoc->strreset_outstanding) { 63662306a36Sopenharmony_ci result = SCTP_STRRESET_ERR_IN_PROGRESS; 63762306a36Sopenharmony_ci goto out; 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci nums = (ntohs(param.p->length) - sizeof(*inreq)) / sizeof(__u16); 64162306a36Sopenharmony_ci str_p = inreq->list_of_streams; 64262306a36Sopenharmony_ci for (i = 0; i < nums; i++) { 64362306a36Sopenharmony_ci if (ntohs(str_p[i]) >= stream->outcnt) { 64462306a36Sopenharmony_ci result = SCTP_STRRESET_ERR_WRONG_SSN; 64562306a36Sopenharmony_ci goto out; 64662306a36Sopenharmony_ci } 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci if (!sctp_stream_outq_is_empty(stream, nums, str_p)) { 65062306a36Sopenharmony_ci result = SCTP_STRRESET_IN_PROGRESS; 65162306a36Sopenharmony_ci asoc->strreset_inseq--; 65262306a36Sopenharmony_ci goto err; 65362306a36Sopenharmony_ci } 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci chunk = sctp_make_strreset_req(asoc, nums, str_p, 1, 0); 65662306a36Sopenharmony_ci if (!chunk) 65762306a36Sopenharmony_ci goto out; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci if (nums) 66062306a36Sopenharmony_ci for (i = 0; i < nums; i++) 66162306a36Sopenharmony_ci SCTP_SO(stream, ntohs(str_p[i]))->state = 66262306a36Sopenharmony_ci SCTP_STREAM_CLOSED; 66362306a36Sopenharmony_ci else 66462306a36Sopenharmony_ci for (i = 0; i < stream->outcnt; i++) 66562306a36Sopenharmony_ci SCTP_SO(stream, i)->state = SCTP_STREAM_CLOSED; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci asoc->strreset_chunk = chunk; 66862306a36Sopenharmony_ci asoc->strreset_outstanding = 1; 66962306a36Sopenharmony_ci sctp_chunk_hold(asoc->strreset_chunk); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci result = SCTP_STRRESET_PERFORMED; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ciout: 67462306a36Sopenharmony_ci sctp_update_strreset_result(asoc, result); 67562306a36Sopenharmony_cierr: 67662306a36Sopenharmony_ci if (!chunk) 67762306a36Sopenharmony_ci chunk = sctp_make_strreset_resp(asoc, result, request_seq); 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci return chunk; 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_cistruct sctp_chunk *sctp_process_strreset_tsnreq( 68362306a36Sopenharmony_ci struct sctp_association *asoc, 68462306a36Sopenharmony_ci union sctp_params param, 68562306a36Sopenharmony_ci struct sctp_ulpevent **evp) 68662306a36Sopenharmony_ci{ 68762306a36Sopenharmony_ci __u32 init_tsn = 0, next_tsn = 0, max_tsn_seen; 68862306a36Sopenharmony_ci struct sctp_strreset_tsnreq *tsnreq = param.v; 68962306a36Sopenharmony_ci struct sctp_stream *stream = &asoc->stream; 69062306a36Sopenharmony_ci __u32 result = SCTP_STRRESET_DENIED; 69162306a36Sopenharmony_ci __u32 request_seq; 69262306a36Sopenharmony_ci __u16 i; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci request_seq = ntohl(tsnreq->request_seq); 69562306a36Sopenharmony_ci if (TSN_lt(asoc->strreset_inseq, request_seq) || 69662306a36Sopenharmony_ci TSN_lt(request_seq, asoc->strreset_inseq - 2)) { 69762306a36Sopenharmony_ci result = SCTP_STRRESET_ERR_BAD_SEQNO; 69862306a36Sopenharmony_ci goto err; 69962306a36Sopenharmony_ci } else if (TSN_lt(request_seq, asoc->strreset_inseq)) { 70062306a36Sopenharmony_ci i = asoc->strreset_inseq - request_seq - 1; 70162306a36Sopenharmony_ci result = asoc->strreset_result[i]; 70262306a36Sopenharmony_ci if (result == SCTP_STRRESET_PERFORMED) { 70362306a36Sopenharmony_ci next_tsn = asoc->ctsn_ack_point + 1; 70462306a36Sopenharmony_ci init_tsn = 70562306a36Sopenharmony_ci sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map) + 1; 70662306a36Sopenharmony_ci } 70762306a36Sopenharmony_ci goto err; 70862306a36Sopenharmony_ci } 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci if (!sctp_outq_is_empty(&asoc->outqueue)) { 71162306a36Sopenharmony_ci result = SCTP_STRRESET_IN_PROGRESS; 71262306a36Sopenharmony_ci goto err; 71362306a36Sopenharmony_ci } 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci asoc->strreset_inseq++; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_ASSOC_REQ)) 71862306a36Sopenharmony_ci goto out; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci if (asoc->strreset_outstanding) { 72162306a36Sopenharmony_ci result = SCTP_STRRESET_ERR_IN_PROGRESS; 72262306a36Sopenharmony_ci goto out; 72362306a36Sopenharmony_ci } 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci /* G4: The same processing as though a FWD-TSN chunk (as defined in 72662306a36Sopenharmony_ci * [RFC3758]) with all streams affected and a new cumulative TSN 72762306a36Sopenharmony_ci * ACK of the Receiver's Next TSN minus 1 were received MUST be 72862306a36Sopenharmony_ci * performed. 72962306a36Sopenharmony_ci */ 73062306a36Sopenharmony_ci max_tsn_seen = sctp_tsnmap_get_max_tsn_seen(&asoc->peer.tsn_map); 73162306a36Sopenharmony_ci asoc->stream.si->report_ftsn(&asoc->ulpq, max_tsn_seen); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci /* G1: Compute an appropriate value for the Receiver's Next TSN -- the 73462306a36Sopenharmony_ci * TSN that the peer should use to send the next DATA chunk. The 73562306a36Sopenharmony_ci * value SHOULD be the smallest TSN not acknowledged by the 73662306a36Sopenharmony_ci * receiver of the request plus 2^31. 73762306a36Sopenharmony_ci */ 73862306a36Sopenharmony_ci init_tsn = sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map) + (1 << 31); 73962306a36Sopenharmony_ci sctp_tsnmap_init(&asoc->peer.tsn_map, SCTP_TSN_MAP_INITIAL, 74062306a36Sopenharmony_ci init_tsn, GFP_ATOMIC); 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci /* G3: The same processing as though a SACK chunk with no gap report 74362306a36Sopenharmony_ci * and a cumulative TSN ACK of the Sender's Next TSN minus 1 were 74462306a36Sopenharmony_ci * received MUST be performed. 74562306a36Sopenharmony_ci */ 74662306a36Sopenharmony_ci sctp_outq_free(&asoc->outqueue); 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci /* G2: Compute an appropriate value for the local endpoint's next TSN, 74962306a36Sopenharmony_ci * i.e., the next TSN assigned by the receiver of the SSN/TSN reset 75062306a36Sopenharmony_ci * chunk. The value SHOULD be the highest TSN sent by the receiver 75162306a36Sopenharmony_ci * of the request plus 1. 75262306a36Sopenharmony_ci */ 75362306a36Sopenharmony_ci next_tsn = asoc->next_tsn; 75462306a36Sopenharmony_ci asoc->ctsn_ack_point = next_tsn - 1; 75562306a36Sopenharmony_ci asoc->adv_peer_ack_point = asoc->ctsn_ack_point; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci /* G5: The next expected and outgoing SSNs MUST be reset to 0 for all 75862306a36Sopenharmony_ci * incoming and outgoing streams. 75962306a36Sopenharmony_ci */ 76062306a36Sopenharmony_ci for (i = 0; i < stream->outcnt; i++) { 76162306a36Sopenharmony_ci SCTP_SO(stream, i)->mid = 0; 76262306a36Sopenharmony_ci SCTP_SO(stream, i)->mid_uo = 0; 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci for (i = 0; i < stream->incnt; i++) 76562306a36Sopenharmony_ci SCTP_SI(stream, i)->mid = 0; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci result = SCTP_STRRESET_PERFORMED; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci *evp = sctp_ulpevent_make_assoc_reset_event(asoc, 0, init_tsn, 77062306a36Sopenharmony_ci next_tsn, GFP_ATOMIC); 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ciout: 77362306a36Sopenharmony_ci sctp_update_strreset_result(asoc, result); 77462306a36Sopenharmony_cierr: 77562306a36Sopenharmony_ci return sctp_make_strreset_tsnresp(asoc, result, request_seq, 77662306a36Sopenharmony_ci next_tsn, init_tsn); 77762306a36Sopenharmony_ci} 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_cistruct sctp_chunk *sctp_process_strreset_addstrm_out( 78062306a36Sopenharmony_ci struct sctp_association *asoc, 78162306a36Sopenharmony_ci union sctp_params param, 78262306a36Sopenharmony_ci struct sctp_ulpevent **evp) 78362306a36Sopenharmony_ci{ 78462306a36Sopenharmony_ci struct sctp_strreset_addstrm *addstrm = param.v; 78562306a36Sopenharmony_ci struct sctp_stream *stream = &asoc->stream; 78662306a36Sopenharmony_ci __u32 result = SCTP_STRRESET_DENIED; 78762306a36Sopenharmony_ci __u32 request_seq, incnt; 78862306a36Sopenharmony_ci __u16 in, i; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci request_seq = ntohl(addstrm->request_seq); 79162306a36Sopenharmony_ci if (TSN_lt(asoc->strreset_inseq, request_seq) || 79262306a36Sopenharmony_ci TSN_lt(request_seq, asoc->strreset_inseq - 2)) { 79362306a36Sopenharmony_ci result = SCTP_STRRESET_ERR_BAD_SEQNO; 79462306a36Sopenharmony_ci goto err; 79562306a36Sopenharmony_ci } else if (TSN_lt(request_seq, asoc->strreset_inseq)) { 79662306a36Sopenharmony_ci i = asoc->strreset_inseq - request_seq - 1; 79762306a36Sopenharmony_ci result = asoc->strreset_result[i]; 79862306a36Sopenharmony_ci goto err; 79962306a36Sopenharmony_ci } 80062306a36Sopenharmony_ci asoc->strreset_inseq++; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci if (!(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ)) 80362306a36Sopenharmony_ci goto out; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci in = ntohs(addstrm->number_of_streams); 80662306a36Sopenharmony_ci incnt = stream->incnt + in; 80762306a36Sopenharmony_ci if (!in || incnt > SCTP_MAX_STREAM) 80862306a36Sopenharmony_ci goto out; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci if (sctp_stream_alloc_in(stream, incnt, GFP_ATOMIC)) 81162306a36Sopenharmony_ci goto out; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci if (asoc->strreset_chunk) { 81462306a36Sopenharmony_ci if (!sctp_chunk_lookup_strreset_param( 81562306a36Sopenharmony_ci asoc, 0, SCTP_PARAM_RESET_ADD_IN_STREAMS)) { 81662306a36Sopenharmony_ci /* same process with outstanding isn't 0 */ 81762306a36Sopenharmony_ci result = SCTP_STRRESET_ERR_IN_PROGRESS; 81862306a36Sopenharmony_ci goto out; 81962306a36Sopenharmony_ci } 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci asoc->strreset_outstanding--; 82262306a36Sopenharmony_ci asoc->strreset_outseq++; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci if (!asoc->strreset_outstanding) { 82562306a36Sopenharmony_ci struct sctp_transport *t; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci t = asoc->strreset_chunk->transport; 82862306a36Sopenharmony_ci if (del_timer(&t->reconf_timer)) 82962306a36Sopenharmony_ci sctp_transport_put(t); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci sctp_chunk_put(asoc->strreset_chunk); 83262306a36Sopenharmony_ci asoc->strreset_chunk = NULL; 83362306a36Sopenharmony_ci } 83462306a36Sopenharmony_ci } 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci stream->incnt = incnt; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci result = SCTP_STRRESET_PERFORMED; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci *evp = sctp_ulpevent_make_stream_change_event(asoc, 84162306a36Sopenharmony_ci 0, ntohs(addstrm->number_of_streams), 0, GFP_ATOMIC); 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ciout: 84462306a36Sopenharmony_ci sctp_update_strreset_result(asoc, result); 84562306a36Sopenharmony_cierr: 84662306a36Sopenharmony_ci return sctp_make_strreset_resp(asoc, result, request_seq); 84762306a36Sopenharmony_ci} 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_cistruct sctp_chunk *sctp_process_strreset_addstrm_in( 85062306a36Sopenharmony_ci struct sctp_association *asoc, 85162306a36Sopenharmony_ci union sctp_params param, 85262306a36Sopenharmony_ci struct sctp_ulpevent **evp) 85362306a36Sopenharmony_ci{ 85462306a36Sopenharmony_ci struct sctp_strreset_addstrm *addstrm = param.v; 85562306a36Sopenharmony_ci struct sctp_stream *stream = &asoc->stream; 85662306a36Sopenharmony_ci __u32 result = SCTP_STRRESET_DENIED; 85762306a36Sopenharmony_ci struct sctp_chunk *chunk = NULL; 85862306a36Sopenharmony_ci __u32 request_seq, outcnt; 85962306a36Sopenharmony_ci __u16 out, i; 86062306a36Sopenharmony_ci int ret; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci request_seq = ntohl(addstrm->request_seq); 86362306a36Sopenharmony_ci if (TSN_lt(asoc->strreset_inseq, request_seq) || 86462306a36Sopenharmony_ci TSN_lt(request_seq, asoc->strreset_inseq - 2)) { 86562306a36Sopenharmony_ci result = SCTP_STRRESET_ERR_BAD_SEQNO; 86662306a36Sopenharmony_ci goto err; 86762306a36Sopenharmony_ci } else if (TSN_lt(request_seq, asoc->strreset_inseq)) { 86862306a36Sopenharmony_ci i = asoc->strreset_inseq - request_seq - 1; 86962306a36Sopenharmony_ci result = asoc->strreset_result[i]; 87062306a36Sopenharmony_ci if (result == SCTP_STRRESET_PERFORMED) 87162306a36Sopenharmony_ci return NULL; 87262306a36Sopenharmony_ci goto err; 87362306a36Sopenharmony_ci } 87462306a36Sopenharmony_ci asoc->strreset_inseq++; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci if (!(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ)) 87762306a36Sopenharmony_ci goto out; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci if (asoc->strreset_outstanding) { 88062306a36Sopenharmony_ci result = SCTP_STRRESET_ERR_IN_PROGRESS; 88162306a36Sopenharmony_ci goto out; 88262306a36Sopenharmony_ci } 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci out = ntohs(addstrm->number_of_streams); 88562306a36Sopenharmony_ci outcnt = stream->outcnt + out; 88662306a36Sopenharmony_ci if (!out || outcnt > SCTP_MAX_STREAM) 88762306a36Sopenharmony_ci goto out; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci ret = sctp_stream_alloc_out(stream, outcnt, GFP_ATOMIC); 89062306a36Sopenharmony_ci if (ret) 89162306a36Sopenharmony_ci goto out; 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci chunk = sctp_make_strreset_addstrm(asoc, out, 0); 89462306a36Sopenharmony_ci if (!chunk) 89562306a36Sopenharmony_ci goto out; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci asoc->strreset_chunk = chunk; 89862306a36Sopenharmony_ci asoc->strreset_outstanding = 1; 89962306a36Sopenharmony_ci sctp_chunk_hold(asoc->strreset_chunk); 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci stream->outcnt = outcnt; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci result = SCTP_STRRESET_PERFORMED; 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ciout: 90662306a36Sopenharmony_ci sctp_update_strreset_result(asoc, result); 90762306a36Sopenharmony_cierr: 90862306a36Sopenharmony_ci if (!chunk) 90962306a36Sopenharmony_ci chunk = sctp_make_strreset_resp(asoc, result, request_seq); 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci return chunk; 91262306a36Sopenharmony_ci} 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_cistruct sctp_chunk *sctp_process_strreset_resp( 91562306a36Sopenharmony_ci struct sctp_association *asoc, 91662306a36Sopenharmony_ci union sctp_params param, 91762306a36Sopenharmony_ci struct sctp_ulpevent **evp) 91862306a36Sopenharmony_ci{ 91962306a36Sopenharmony_ci struct sctp_stream *stream = &asoc->stream; 92062306a36Sopenharmony_ci struct sctp_strreset_resp *resp = param.v; 92162306a36Sopenharmony_ci struct sctp_transport *t; 92262306a36Sopenharmony_ci __u16 i, nums, flags = 0; 92362306a36Sopenharmony_ci struct sctp_paramhdr *req; 92462306a36Sopenharmony_ci __u32 result; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci req = sctp_chunk_lookup_strreset_param(asoc, resp->response_seq, 0); 92762306a36Sopenharmony_ci if (!req) 92862306a36Sopenharmony_ci return NULL; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci result = ntohl(resp->result); 93162306a36Sopenharmony_ci if (result != SCTP_STRRESET_PERFORMED) { 93262306a36Sopenharmony_ci /* if in progress, do nothing but retransmit */ 93362306a36Sopenharmony_ci if (result == SCTP_STRRESET_IN_PROGRESS) 93462306a36Sopenharmony_ci return NULL; 93562306a36Sopenharmony_ci else if (result == SCTP_STRRESET_DENIED) 93662306a36Sopenharmony_ci flags = SCTP_STREAM_RESET_DENIED; 93762306a36Sopenharmony_ci else 93862306a36Sopenharmony_ci flags = SCTP_STREAM_RESET_FAILED; 93962306a36Sopenharmony_ci } 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci if (req->type == SCTP_PARAM_RESET_OUT_REQUEST) { 94262306a36Sopenharmony_ci struct sctp_strreset_outreq *outreq; 94362306a36Sopenharmony_ci __be16 *str_p; 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci outreq = (struct sctp_strreset_outreq *)req; 94662306a36Sopenharmony_ci str_p = outreq->list_of_streams; 94762306a36Sopenharmony_ci nums = (ntohs(outreq->param_hdr.length) - sizeof(*outreq)) / 94862306a36Sopenharmony_ci sizeof(__u16); 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci if (result == SCTP_STRRESET_PERFORMED) { 95162306a36Sopenharmony_ci struct sctp_stream_out *sout; 95262306a36Sopenharmony_ci if (nums) { 95362306a36Sopenharmony_ci for (i = 0; i < nums; i++) { 95462306a36Sopenharmony_ci sout = SCTP_SO(stream, ntohs(str_p[i])); 95562306a36Sopenharmony_ci sout->mid = 0; 95662306a36Sopenharmony_ci sout->mid_uo = 0; 95762306a36Sopenharmony_ci } 95862306a36Sopenharmony_ci } else { 95962306a36Sopenharmony_ci for (i = 0; i < stream->outcnt; i++) { 96062306a36Sopenharmony_ci sout = SCTP_SO(stream, i); 96162306a36Sopenharmony_ci sout->mid = 0; 96262306a36Sopenharmony_ci sout->mid_uo = 0; 96362306a36Sopenharmony_ci } 96462306a36Sopenharmony_ci } 96562306a36Sopenharmony_ci } 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci flags |= SCTP_STREAM_RESET_OUTGOING_SSN; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci for (i = 0; i < stream->outcnt; i++) 97062306a36Sopenharmony_ci SCTP_SO(stream, i)->state = SCTP_STREAM_OPEN; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci *evp = sctp_ulpevent_make_stream_reset_event(asoc, flags, 97362306a36Sopenharmony_ci nums, str_p, GFP_ATOMIC); 97462306a36Sopenharmony_ci } else if (req->type == SCTP_PARAM_RESET_IN_REQUEST) { 97562306a36Sopenharmony_ci struct sctp_strreset_inreq *inreq; 97662306a36Sopenharmony_ci __be16 *str_p; 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci /* if the result is performed, it's impossible for inreq */ 97962306a36Sopenharmony_ci if (result == SCTP_STRRESET_PERFORMED) 98062306a36Sopenharmony_ci return NULL; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci inreq = (struct sctp_strreset_inreq *)req; 98362306a36Sopenharmony_ci str_p = inreq->list_of_streams; 98462306a36Sopenharmony_ci nums = (ntohs(inreq->param_hdr.length) - sizeof(*inreq)) / 98562306a36Sopenharmony_ci sizeof(__u16); 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci flags |= SCTP_STREAM_RESET_INCOMING_SSN; 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci *evp = sctp_ulpevent_make_stream_reset_event(asoc, flags, 99062306a36Sopenharmony_ci nums, str_p, GFP_ATOMIC); 99162306a36Sopenharmony_ci } else if (req->type == SCTP_PARAM_RESET_TSN_REQUEST) { 99262306a36Sopenharmony_ci struct sctp_strreset_resptsn *resptsn; 99362306a36Sopenharmony_ci __u32 stsn, rtsn; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci /* check for resptsn, as sctp_verify_reconf didn't do it*/ 99662306a36Sopenharmony_ci if (ntohs(param.p->length) != sizeof(*resptsn)) 99762306a36Sopenharmony_ci return NULL; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci resptsn = (struct sctp_strreset_resptsn *)resp; 100062306a36Sopenharmony_ci stsn = ntohl(resptsn->senders_next_tsn); 100162306a36Sopenharmony_ci rtsn = ntohl(resptsn->receivers_next_tsn); 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci if (result == SCTP_STRRESET_PERFORMED) { 100462306a36Sopenharmony_ci __u32 mtsn = sctp_tsnmap_get_max_tsn_seen( 100562306a36Sopenharmony_ci &asoc->peer.tsn_map); 100662306a36Sopenharmony_ci LIST_HEAD(temp); 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci asoc->stream.si->report_ftsn(&asoc->ulpq, mtsn); 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci sctp_tsnmap_init(&asoc->peer.tsn_map, 101162306a36Sopenharmony_ci SCTP_TSN_MAP_INITIAL, 101262306a36Sopenharmony_ci stsn, GFP_ATOMIC); 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci /* Clean up sacked and abandoned queues only. As the 101562306a36Sopenharmony_ci * out_chunk_list may not be empty, splice it to temp, 101662306a36Sopenharmony_ci * then get it back after sctp_outq_free is done. 101762306a36Sopenharmony_ci */ 101862306a36Sopenharmony_ci list_splice_init(&asoc->outqueue.out_chunk_list, &temp); 101962306a36Sopenharmony_ci sctp_outq_free(&asoc->outqueue); 102062306a36Sopenharmony_ci list_splice_init(&temp, &asoc->outqueue.out_chunk_list); 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci asoc->next_tsn = rtsn; 102362306a36Sopenharmony_ci asoc->ctsn_ack_point = asoc->next_tsn - 1; 102462306a36Sopenharmony_ci asoc->adv_peer_ack_point = asoc->ctsn_ack_point; 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci for (i = 0; i < stream->outcnt; i++) { 102762306a36Sopenharmony_ci SCTP_SO(stream, i)->mid = 0; 102862306a36Sopenharmony_ci SCTP_SO(stream, i)->mid_uo = 0; 102962306a36Sopenharmony_ci } 103062306a36Sopenharmony_ci for (i = 0; i < stream->incnt; i++) 103162306a36Sopenharmony_ci SCTP_SI(stream, i)->mid = 0; 103262306a36Sopenharmony_ci } 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci for (i = 0; i < stream->outcnt; i++) 103562306a36Sopenharmony_ci SCTP_SO(stream, i)->state = SCTP_STREAM_OPEN; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci *evp = sctp_ulpevent_make_assoc_reset_event(asoc, flags, 103862306a36Sopenharmony_ci stsn, rtsn, GFP_ATOMIC); 103962306a36Sopenharmony_ci } else if (req->type == SCTP_PARAM_RESET_ADD_OUT_STREAMS) { 104062306a36Sopenharmony_ci struct sctp_strreset_addstrm *addstrm; 104162306a36Sopenharmony_ci __u16 number; 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci addstrm = (struct sctp_strreset_addstrm *)req; 104462306a36Sopenharmony_ci nums = ntohs(addstrm->number_of_streams); 104562306a36Sopenharmony_ci number = stream->outcnt - nums; 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci if (result == SCTP_STRRESET_PERFORMED) { 104862306a36Sopenharmony_ci for (i = number; i < stream->outcnt; i++) 104962306a36Sopenharmony_ci SCTP_SO(stream, i)->state = SCTP_STREAM_OPEN; 105062306a36Sopenharmony_ci } else { 105162306a36Sopenharmony_ci sctp_stream_shrink_out(stream, number); 105262306a36Sopenharmony_ci stream->outcnt = number; 105362306a36Sopenharmony_ci } 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci *evp = sctp_ulpevent_make_stream_change_event(asoc, flags, 105662306a36Sopenharmony_ci 0, nums, GFP_ATOMIC); 105762306a36Sopenharmony_ci } else if (req->type == SCTP_PARAM_RESET_ADD_IN_STREAMS) { 105862306a36Sopenharmony_ci struct sctp_strreset_addstrm *addstrm; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci /* if the result is performed, it's impossible for addstrm in 106162306a36Sopenharmony_ci * request. 106262306a36Sopenharmony_ci */ 106362306a36Sopenharmony_ci if (result == SCTP_STRRESET_PERFORMED) 106462306a36Sopenharmony_ci return NULL; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci addstrm = (struct sctp_strreset_addstrm *)req; 106762306a36Sopenharmony_ci nums = ntohs(addstrm->number_of_streams); 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci *evp = sctp_ulpevent_make_stream_change_event(asoc, flags, 107062306a36Sopenharmony_ci nums, 0, GFP_ATOMIC); 107162306a36Sopenharmony_ci } 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci asoc->strreset_outstanding--; 107462306a36Sopenharmony_ci asoc->strreset_outseq++; 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci /* remove everything for this reconf request */ 107762306a36Sopenharmony_ci if (!asoc->strreset_outstanding) { 107862306a36Sopenharmony_ci t = asoc->strreset_chunk->transport; 107962306a36Sopenharmony_ci if (del_timer(&t->reconf_timer)) 108062306a36Sopenharmony_ci sctp_transport_put(t); 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci sctp_chunk_put(asoc->strreset_chunk); 108362306a36Sopenharmony_ci asoc->strreset_chunk = NULL; 108462306a36Sopenharmony_ci } 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci return NULL; 108762306a36Sopenharmony_ci} 1088