/* SCTP kernel implementation
* (C) Copyright IBM Corp. 2001, 2004
* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 Intel Corp.
*
* This file is part of the SCTP kernel implementation
*
* These functions manipulate sctp tsn mapping array.
*
* This SCTP implementation is free software;
* you can redistribute it and/or modify it under the terms of
* the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This SCTP implementation is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied
* ************************
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU CC; see the file COPYING. If not, see
* <http://www.gnu.org/licenses/>.
*
* Please send any bug reports or fixes you make to the
* email address(es):
* lksctp developers <linux-sctp@vger.kernel.org>
*
* Written or modified by:
* Xin Long <lucien.xin@gmail.com>
*/
#include <net/sctp/sctp.h>
#include <net/sctp/sm.h>
int sctp_stream_init(struct sctp_stream *stream, __u16 outcnt, __u16 incnt,
gfp_t gfp)
{
int i;
/* Initial stream->out size may be very big, so free it and alloc
* a new one with new outcnt to save memory.
*/
kfree(stream->out);
stream->out = kcalloc(outcnt, sizeof(*stream->out), gfp);
if (!stream->out)
return -ENOMEM;
stream->outcnt = outcnt;
for (i = 0; i < stream->outcnt; i++)
stream->out[i].state = SCTP_STREAM_OPEN;
if (!incnt)
return 0;
stream->in = kcalloc(incnt, sizeof(*stream->in), gfp);
if (!stream->in) {
kfree(stream->out);
stream->out = NULL;
return -ENOMEM;
}
stream->incnt = incnt;
return 0;
}
void sctp_stream_free(struct sctp_stream *stream)
{
kfree(stream->out);
kfree(stream->in);
}
void sctp_stream_clear(struct sctp_stream *stream)
{
int i;
for (i = 0; i < stream->outcnt; i++)
stream->out[i].ssn = 0;
for (i = 0; i < stream->incnt; i++)
stream->in[i].ssn = 0;
}
void sctp_stream_update(struct sctp_stream *stream, struct sctp_stream *new)
{
sctp_stream_free(stream);
stream->out = new->out;
stream->in = new->in;
stream->outcnt = new->outcnt;
stream->incnt = new->incnt;
new->out = NULL;
new->in = NULL;
}
static int sctp_send_reconf(struct sctp_association *asoc,
struct sctp_chunk *chunk)
{
struct net *net = sock_net(asoc->base.sk);
int retval = 0;
retval = sctp_primitive_RECONF(net, asoc, chunk);
if (retval)
sctp_chunk_free(chunk);
return retval;
}
int sctp_send_reset_streams(struct sctp_association *asoc,
struct sctp_reset_streams *params)
{
struct sctp_stream *stream = &asoc->stream;
__u16 i, str_nums, *str_list;
struct sctp_chunk *chunk;
int retval = -EINVAL;
bool out, in;
if (!asoc->peer.reconf_capable ||
!(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ)) {
retval = -ENOPROTOOPT;
goto out;
}
if (asoc->strreset_outstanding) {
retval = -EINPROGRESS;
goto out;
}
out = params->srs_flags & SCTP_STREAM_RESET_OUTGOING;
in = params->srs_flags & SCTP_STREAM_RESET_INCOMING;
if (!out && !in)
goto out;
str_nums = params->srs_number_streams;
str_list = params->srs_stream_list;
if (out && str_nums)
for (i = 0; i < str_nums; i++)
if (str_list[i] >= stream->outcnt)
goto out;
if (in && str_nums)
for (i = 0; i < str_nums; i++)
if (str_list[i] >= stream->incnt)
goto out;
for (i = 0; i < str_nums; i++)
str_list[i] = htons(str_list[i]);
chunk = sctp_make_strreset_req(asoc, str_nums, str_list, out, in);
for (i = 0; i < str_nums; i++)
str_list[i] = ntohs(str_list[i]);
if (!chunk) {
retval = -ENOMEM;
goto out;
}
if (out) {
if (str_nums)
for (i = 0; i