Commit 585396bc authored by David S. Miller's avatar David S. Miller

Merge branch 'sctp-rcv-side-stream-reconf-ssn-reset-req-chunk'

Xin Long says:

====================
sctp: add receiver-side procedures for stream reconf ssn reset request chunk

Patch 3/7 and 4/7 are to implement receiver-side procedures for the
Outgoing and Incoming SSN Reset Request Parameter described in rfc6525
section 5.2.2 and 5.2.3

Patch 1/7 and 2/7 are ahead of them to define some apis.

Patch 5/7-7/7 are to add the process of reconf chunk event in rx path.

Note that with this patchset, asoc->reconf_enable has no chance yet to
be set, until the patch "sctp: add get and set sockopt for reconf_enable"
is applied in the future. As we can not just enable it when sctp is not
capable of processing reconf chunk yet.

v1->v2:
  - re-split the patchset and make sure it has no dead codes for review.
  - rename the titles of the commits and improve some changelogs.
  - drop __packed from some structures in patch 1/7.
  - fix some kbuild warnings in patch 3/7 by initializing str_p = NULL.
  - sctp_chunk_lookup_strreset_param changes to return sctp_paramhdr_t *
    and uses sctp_strreset_tsnreq to access request_seq in patch 3/7.
  - use __u<size> in uapi sctp.h in patch 1/7.
  - do str_list endian conversion when generating stream_reset_event in patch
    2/7.
  - remove str_list endian conversion, pass resp_seq param with network endian
    to lookup_strreset_param in 3/7.
  - move str_list endian conversion out of sctp_make_strreset_req, so that
    sctp_make_strreset_req can be used more conveniently to process inreq in
    patch 4/7.
  - remove sctp_merge_reconf_chunk and not support response with multiparam
    in patch 6/7.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 61845d20 d884aa63
......@@ -749,4 +749,28 @@ struct sctp_strreset_addstrm {
__u16 reserved;
};
enum {
SCTP_STRRESET_NOTHING_TO_DO = 0x00,
SCTP_STRRESET_PERFORMED = 0x01,
SCTP_STRRESET_DENIED = 0x02,
SCTP_STRRESET_ERR_WRONG_SSN = 0x03,
SCTP_STRRESET_ERR_IN_PROGRESS = 0x04,
SCTP_STRRESET_ERR_BAD_SEQNO = 0x05,
SCTP_STRRESET_IN_PROGRESS = 0x06,
};
struct sctp_strreset_resp {
sctp_paramhdr_t param_hdr;
__u32 response_seq;
__u32 result;
};
struct sctp_strreset_resptsn {
sctp_paramhdr_t param_hdr;
__u32 response_seq;
__u32 result;
__u32 senders_next_tsn;
__u32 receivers_next_tsn;
};
#endif /* __LINUX_SCTP_H__ */
......@@ -60,11 +60,14 @@ enum { SCTP_DEFAULT_INSTREAMS = SCTP_MAX_STREAM };
#define SCTP_NUM_PRSCTP_CHUNK_TYPES 1
#define SCTP_NUM_RECONF_CHUNK_TYPES 1
#define SCTP_NUM_AUTH_CHUNK_TYPES 1
#define SCTP_NUM_CHUNK_TYPES (SCTP_NUM_BASE_CHUNK_TYPES + \
SCTP_NUM_ADDIP_CHUNK_TYPES +\
SCTP_NUM_PRSCTP_CHUNK_TYPES +\
SCTP_NUM_RECONF_CHUNK_TYPES +\
SCTP_NUM_AUTH_CHUNK_TYPES)
/* These are the different flavours of event. */
......
......@@ -135,6 +135,7 @@ sctp_state_fn_t sctp_sf_do_8_5_1_E_sa;
sctp_state_fn_t sctp_sf_cookie_echoed_err;
sctp_state_fn_t sctp_sf_do_asconf;
sctp_state_fn_t sctp_sf_do_asconf_ack;
sctp_state_fn_t sctp_sf_do_reconf;
sctp_state_fn_t sctp_sf_do_9_2_reshutack;
sctp_state_fn_t sctp_sf_eat_fwd_tsn;
sctp_state_fn_t sctp_sf_eat_fwd_tsn_fast;
......@@ -270,9 +271,29 @@ struct sctp_chunk *sctp_make_strreset_tsnreq(
struct sctp_chunk *sctp_make_strreset_addstrm(
const struct sctp_association *asoc,
__u16 out, __u16 in);
struct sctp_chunk *sctp_make_strreset_resp(
const struct sctp_association *asoc,
__u32 result, __u32 sn);
struct sctp_chunk *sctp_make_strreset_tsnresp(
struct sctp_association *asoc,
__u32 result, __u32 sn,
__u32 sender_tsn, __u32 receiver_tsn);
bool sctp_verify_reconf(const struct sctp_association *asoc,
struct sctp_chunk *chunk,
struct sctp_paramhdr **errp);
void sctp_chunk_assign_tsn(struct sctp_chunk *);
void sctp_chunk_assign_ssn(struct sctp_chunk *);
/* Prototypes for stream-processing functions. */
struct sctp_chunk *sctp_process_strreset_outreq(
struct sctp_association *asoc,
union sctp_params param,
struct sctp_ulpevent **evp);
struct sctp_chunk *sctp_process_strreset_inreq(
struct sctp_association *asoc,
union sctp_params param,
struct sctp_ulpevent **evp);
/* Prototypes for statetable processing. */
int sctp_do_sm(struct net *net, sctp_event_t event_type, sctp_subtype_t subtype,
......
......@@ -128,6 +128,10 @@ struct sctp_ulpevent *sctp_ulpevent_make_authkey(
struct sctp_ulpevent *sctp_ulpevent_make_sender_dry_event(
const struct sctp_association *asoc, gfp_t gfp);
struct sctp_ulpevent *sctp_ulpevent_make_stream_reset_event(
const struct sctp_association *asoc, __u16 flags,
__u16 stream_num, __u16 *stream_list, gfp_t gfp);
void sctp_ulpevent_read_sndrcvinfo(const struct sctp_ulpevent *event,
struct msghdr *);
void sctp_ulpevent_read_rcvinfo(const struct sctp_ulpevent *event,
......
......@@ -490,6 +490,18 @@ struct sctp_sender_dry_event {
sctp_assoc_t sender_dry_assoc_id;
};
#define SCTP_STREAM_RESET_INCOMING_SSN 0x0001
#define SCTP_STREAM_RESET_OUTGOING_SSN 0x0002
#define SCTP_STREAM_RESET_DENIED 0x0004
#define SCTP_STREAM_RESET_FAILED 0x0008
struct sctp_stream_reset_event {
__u16 strreset_type;
__u16 strreset_flags;
__u32 strreset_length;
sctp_assoc_t strreset_assoc_id;
__u16 strreset_stream_list[];
};
/*
* Described in Section 7.3
* Ancillary Data and Notification Interest Options
......@@ -505,6 +517,7 @@ struct sctp_event_subscribe {
__u8 sctp_adaptation_layer_event;
__u8 sctp_authentication_event;
__u8 sctp_sender_dry_event;
__u8 sctp_stream_reset_event;
};
/*
......@@ -529,6 +542,7 @@ union sctp_notification {
struct sctp_pdapi_event sn_pdapi_event;
struct sctp_authkey_event sn_authkey_event;
struct sctp_sender_dry_event sn_sender_dry_event;
struct sctp_stream_reset_event sn_strreset_event;
};
/* Section 5.3.1
......@@ -556,6 +570,8 @@ enum sctp_sn_type {
#define SCTP_AUTHENTICATION_INDICATION SCTP_AUTHENTICATION_EVENT
SCTP_SENDER_DRY_EVENT,
#define SCTP_SENDER_DRY_EVENT SCTP_SENDER_DRY_EVENT
SCTP_STREAM_RESET_EVENT,
#define SCTP_STREAM_RESET_EVENT SCTP_STREAM_RESET_EVENT
};
/* Notification error codes used to fill up the error fields in some
......
......@@ -3617,7 +3617,7 @@ struct sctp_chunk *sctp_make_strreset_req(
__u16 stream_len = stream_num * 2;
struct sctp_strreset_inreq inreq;
struct sctp_chunk *retval;
__u16 outlen, inlen, i;
__u16 outlen, inlen;
outlen = (sizeof(outreq) + stream_len) * out;
inlen = (sizeof(inreq) + stream_len) * in;
......@@ -3626,9 +3626,6 @@ struct sctp_chunk *sctp_make_strreset_req(
if (!retval)
return NULL;
for (i = 0; i < stream_num; i++)
stream_list[i] = htons(stream_list[i]);
if (outlen) {
outreq.param_hdr.type = SCTP_PARAM_RESET_OUT_REQUEST;
outreq.param_hdr.length = htons(outlen);
......@@ -3653,9 +3650,6 @@ struct sctp_chunk *sctp_make_strreset_req(
sctp_addto_chunk(retval, stream_len, stream_list);
}
for (i = 0; i < stream_num; i++)
stream_list[i] = ntohs(stream_list[i]);
return retval;
}
......@@ -3733,3 +3727,136 @@ struct sctp_chunk *sctp_make_strreset_addstrm(
return retval;
}
/* RE-CONFIG 4.4 (RESP)
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Parameter Type = 16 | Parameter Length |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Re-configuration Response Sequence Number |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Result |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
struct sctp_chunk *sctp_make_strreset_resp(
const struct sctp_association *asoc,
__u32 result, __u32 sn)
{
struct sctp_strreset_resp resp;
__u16 length = sizeof(resp);
struct sctp_chunk *retval;
retval = sctp_make_reconf(asoc, length);
if (!retval)
return NULL;
resp.param_hdr.type = SCTP_PARAM_RESET_RESPONSE;
resp.param_hdr.length = htons(length);
resp.response_seq = htonl(sn);
resp.result = htonl(result);
sctp_addto_chunk(retval, sizeof(resp), &resp);
return retval;
}
/* RE-CONFIG 4.4 OPTIONAL (TSNRESP)
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Parameter Type = 16 | Parameter Length |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Re-configuration Response Sequence Number |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Result |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Sender's Next TSN (optional) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Receiver's Next TSN (optional) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
struct sctp_chunk *sctp_make_strreset_tsnresp(
struct sctp_association *asoc,
__u32 result, __u32 sn,
__u32 sender_tsn, __u32 receiver_tsn)
{
struct sctp_strreset_resptsn tsnresp;
__u16 length = sizeof(tsnresp);
struct sctp_chunk *retval;
retval = sctp_make_reconf(asoc, length);
if (!retval)
return NULL;
tsnresp.param_hdr.type = SCTP_PARAM_RESET_RESPONSE;
tsnresp.param_hdr.length = htons(length);
tsnresp.response_seq = htonl(sn);
tsnresp.result = htonl(result);
tsnresp.senders_next_tsn = htonl(sender_tsn);
tsnresp.receivers_next_tsn = htonl(receiver_tsn);
sctp_addto_chunk(retval, sizeof(tsnresp), &tsnresp);
return retval;
}
bool sctp_verify_reconf(const struct sctp_association *asoc,
struct sctp_chunk *chunk,
struct sctp_paramhdr **errp)
{
struct sctp_reconf_chunk *hdr;
union sctp_params param;
__u16 last = 0, cnt = 0;
hdr = (struct sctp_reconf_chunk *)chunk->chunk_hdr;
sctp_walk_params(param, hdr, params) {
__u16 length = ntohs(param.p->length);
*errp = param.p;
if (cnt++ > 2)
return false;
switch (param.p->type) {
case SCTP_PARAM_RESET_OUT_REQUEST:
if (length < sizeof(struct sctp_strreset_outreq) ||
(last && last != SCTP_PARAM_RESET_RESPONSE &&
last != SCTP_PARAM_RESET_IN_REQUEST))
return false;
break;
case SCTP_PARAM_RESET_IN_REQUEST:
if (length < sizeof(struct sctp_strreset_inreq) ||
(last && last != SCTP_PARAM_RESET_OUT_REQUEST))
return false;
break;
case SCTP_PARAM_RESET_RESPONSE:
if ((length != sizeof(struct sctp_strreset_resp) &&
length != sizeof(struct sctp_strreset_resptsn)) ||
(last && last != SCTP_PARAM_RESET_RESPONSE &&
last != SCTP_PARAM_RESET_OUT_REQUEST))
return false;
break;
case SCTP_PARAM_RESET_TSN_REQUEST:
if (length !=
sizeof(struct sctp_strreset_tsnreq) || last)
return false;
break;
case SCTP_PARAM_RESET_ADD_IN_STREAMS:
if (length != sizeof(struct sctp_strreset_addstrm) ||
(last && last != SCTP_PARAM_RESET_ADD_OUT_STREAMS))
return false;
break;
case SCTP_PARAM_RESET_ADD_OUT_STREAMS:
if (length != sizeof(struct sctp_strreset_addstrm) ||
(last && last != SCTP_PARAM_RESET_ADD_IN_STREAMS))
return false;
break;
default:
return false;
}
last = param.p->type;
}
return true;
}
......@@ -3834,6 +3834,60 @@ sctp_disposition_t sctp_sf_do_asconf_ack(struct net *net,
return SCTP_DISPOSITION_DISCARD;
}
/* RE-CONFIG Section 5.2 Upon reception of an RECONF Chunk. */
sctp_disposition_t sctp_sf_do_reconf(struct net *net,
const struct sctp_endpoint *ep,
const struct sctp_association *asoc,
const sctp_subtype_t type, void *arg,
sctp_cmd_seq_t *commands)
{
struct sctp_paramhdr *err_param = NULL;
struct sctp_chunk *chunk = arg;
struct sctp_reconf_chunk *hdr;
union sctp_params param;
if (!sctp_vtag_verify(chunk, asoc)) {
sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_BAD_TAG,
SCTP_NULL());
return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
}
/* Make sure that the RECONF chunk has a valid length. */
if (!sctp_chunk_length_valid(chunk, sizeof(*hdr)))
return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
commands);
if (!sctp_verify_reconf(asoc, chunk, &err_param))
return sctp_sf_violation_paramlen(net, ep, asoc, type, arg,
(void *)err_param, commands);
hdr = (struct sctp_reconf_chunk *)chunk->chunk_hdr;
sctp_walk_params(param, hdr, params) {
struct sctp_chunk *reply = NULL;
struct sctp_ulpevent *ev = NULL;
if (param.p->type == SCTP_PARAM_RESET_OUT_REQUEST)
reply = sctp_process_strreset_outreq(
(struct sctp_association *)asoc, param, &ev);
else if (param.p->type == SCTP_PARAM_RESET_IN_REQUEST)
reply = sctp_process_strreset_inreq(
(struct sctp_association *)asoc, param, &ev);
/* More handles for other types will be added here, by now it
* just ignores other types.
*/
if (ev)
sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP,
SCTP_ULPEVENT(ev));
if (reply)
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
SCTP_CHUNK(reply));
}
return SCTP_DISPOSITION_CONSUME;
}
/*
* PR-SCTP Section 3.6 Receiver Side Implementation of PR-SCTP
*
......
......@@ -482,6 +482,32 @@ static const sctp_sm_table_entry_t prsctp_chunk_event_table[SCTP_NUM_PRSCTP_CHUN
TYPE_SCTP_FWD_TSN,
}; /*state_fn_t prsctp_chunk_event_table[][] */
#define TYPE_SCTP_RECONF { \
/* SCTP_STATE_CLOSED */ \
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
/* SCTP_STATE_COOKIE_WAIT */ \
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
/* SCTP_STATE_COOKIE_ECHOED */ \
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
/* SCTP_STATE_ESTABLISHED */ \
TYPE_SCTP_FUNC(sctp_sf_do_reconf), \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
TYPE_SCTP_FUNC(sctp_sf_do_reconf), \
/* SCTP_STATE_SHUTDOWN_SENT */ \
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
} /* TYPE_SCTP_RECONF */
/* The primary index for this table is the chunk type.
* The secondary index for this table is the state.
*/
static const sctp_sm_table_entry_t reconf_chunk_event_table[SCTP_NUM_RECONF_CHUNK_TYPES][SCTP_STATE_NUM_STATES] = {
TYPE_SCTP_RECONF,
}; /*state_fn_t reconf_chunk_event_table[][] */
#define TYPE_SCTP_AUTH { \
/* SCTP_STATE_CLOSED */ \
TYPE_SCTP_FUNC(sctp_sf_ootb), \
......@@ -964,6 +990,10 @@ static const sctp_sm_table_entry_t *sctp_chunk_event_lookup(struct net *net,
return &addip_chunk_event_table[1][state];
}
if (net->sctp.reconf_enable)
if (cid == SCTP_CID_RECONF)
return &reconf_chunk_event_table[0][state];
if (net->sctp.auth_enable) {
if (cid == SCTP_CID_AUTH)
return &auth_chunk_event_table[0][state];
......
......@@ -135,7 +135,14 @@ int sctp_send_reset_streams(struct sctp_association *asoc,
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;
......@@ -294,3 +301,179 @@ int sctp_send_add_streams(struct sctp_association *asoc,
out:
return retval;
}
static sctp_paramhdr_t *sctp_chunk_lookup_strreset_param(
struct sctp_association *asoc, __u32 resp_seq)
{
struct sctp_chunk *chunk = asoc->strreset_chunk;
struct sctp_reconf_chunk *hdr;
union sctp_params param;
if (ntohl(resp_seq) != asoc->strreset_outseq || !chunk)
return NULL;
hdr = (struct sctp_reconf_chunk *)chunk->chunk_hdr;
sctp_walk_params(param, hdr, params) {
/* sctp_strreset_tsnreq is actually the basic structure
* of all stream reconf params, so it's safe to use it
* to access request_seq.
*/
struct sctp_strreset_tsnreq *req = param.v;
if (req->request_seq == resp_seq)
return param.v;
}
return NULL;
}
struct sctp_chunk *sctp_process_strreset_outreq(
struct sctp_association *asoc,
union sctp_params param,
struct sctp_ulpevent **evp)
{
struct sctp_strreset_outreq *outreq = param.v;
struct sctp_stream *stream = asoc->stream;
__u16 i, nums, flags = 0, *str_p = NULL;
__u32 result = SCTP_STRRESET_DENIED;
__u32 request_seq;
request_seq = ntohl(outreq->request_seq);
if (ntohl(outreq->send_reset_at_tsn) >
sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map)) {
result = SCTP_STRRESET_IN_PROGRESS;
goto out;
}
if (request_seq > asoc->strreset_inseq) {
result = SCTP_STRRESET_ERR_BAD_SEQNO;
goto out;
} else if (request_seq == asoc->strreset_inseq) {
asoc->strreset_inseq++;
}
/* Check strreset_enable after inseq inc, as sender cannot tell
* the peer doesn't enable strreset after receiving response with
* result denied, as well as to keep consistent with bsd.
*/
if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ))
goto out;
if (asoc->strreset_chunk) {
sctp_paramhdr_t *param_hdr;
struct sctp_transport *t;
param_hdr = sctp_chunk_lookup_strreset_param(
asoc, outreq->response_seq);
if (!param_hdr || param_hdr->type !=
SCTP_PARAM_RESET_IN_REQUEST) {
/* same process with outstanding isn't 0 */
result = SCTP_STRRESET_ERR_IN_PROGRESS;
goto out;
}
asoc->strreset_outstanding--;
asoc->strreset_outseq++;
if (!asoc->strreset_outstanding) {
t = asoc->strreset_chunk->transport;
if (del_timer(&t->reconf_timer))
sctp_transport_put(t);
sctp_chunk_put(asoc->strreset_chunk);
asoc->strreset_chunk = NULL;
}
flags = SCTP_STREAM_RESET_INCOMING_SSN;
}
nums = (ntohs(param.p->length) - sizeof(*outreq)) / 2;
if (nums) {
str_p = outreq->list_of_streams;
for (i = 0; i < nums; i++) {
if (ntohs(str_p[i]) >= stream->incnt) {
result = SCTP_STRRESET_ERR_WRONG_SSN;
goto out;
}
}
for (i = 0; i < nums; i++)
stream->in[ntohs(str_p[i])].ssn = 0;
} else {
for (i = 0; i < stream->incnt; i++)
stream->in[i].ssn = 0;
}
result = SCTP_STRRESET_PERFORMED;
*evp = sctp_ulpevent_make_stream_reset_event(asoc,
flags | SCTP_STREAM_RESET_OUTGOING_SSN, nums, str_p,
GFP_ATOMIC);
out:
return sctp_make_strreset_resp(asoc, result, request_seq);
}
struct sctp_chunk *sctp_process_strreset_inreq(
struct sctp_association *asoc,
union sctp_params param,
struct sctp_ulpevent **evp)
{
struct sctp_strreset_inreq *inreq = param.v;
struct sctp_stream *stream = asoc->stream;
__u32 result = SCTP_STRRESET_DENIED;
struct sctp_chunk *chunk = NULL;
__u16 i, nums, *str_p;
__u32 request_seq;
request_seq = ntohl(inreq->request_seq);
if (request_seq > asoc->strreset_inseq) {
result = SCTP_STRRESET_ERR_BAD_SEQNO;
goto out;
} else if (request_seq == asoc->strreset_inseq) {
asoc->strreset_inseq++;
}
if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ))
goto out;
if (asoc->strreset_outstanding) {
result = SCTP_STRRESET_ERR_IN_PROGRESS;
goto out;
}
nums = (ntohs(param.p->length) - sizeof(*inreq)) / 2;
str_p = inreq->list_of_streams;
for (i = 0; i < nums; i++) {
if (ntohs(str_p[i]) >= stream->outcnt) {
result = SCTP_STRRESET_ERR_WRONG_SSN;
goto out;
}
}
chunk = sctp_make_strreset_req(asoc, nums, str_p, 1, 0);
if (!chunk)
goto out;
if (nums)
for (i = 0; i < nums; i++)
stream->out[ntohs(str_p[i])].state =
SCTP_STREAM_CLOSED;
else
for (i = 0; i < stream->outcnt; i++)
stream->out[i].state = SCTP_STREAM_CLOSED;
asoc->strreset_chunk = chunk;
asoc->strreset_outstanding = 1;
sctp_chunk_hold(asoc->strreset_chunk);
*evp = sctp_ulpevent_make_stream_reset_event(asoc,
SCTP_STREAM_RESET_INCOMING_SSN, nums, str_p, GFP_ATOMIC);
out:
if (!chunk)
chunk = sctp_make_strreset_resp(asoc, result, request_seq);
return chunk;
}
......@@ -854,6 +854,35 @@ struct sctp_ulpevent *sctp_ulpevent_make_sender_dry_event(
return event;
}
struct sctp_ulpevent *sctp_ulpevent_make_stream_reset_event(
const struct sctp_association *asoc, __u16 flags, __u16 stream_num,
__u16 *stream_list, gfp_t gfp)
{
struct sctp_stream_reset_event *sreset;
struct sctp_ulpevent *event;
struct sk_buff *skb;
int length, i;
length = sizeof(struct sctp_stream_reset_event) + 2 * stream_num;
event = sctp_ulpevent_new(length, MSG_NOTIFICATION, gfp);
if (!event)
return NULL;
skb = sctp_event2skb(event);
sreset = (struct sctp_stream_reset_event *)skb_put(skb, length);
sreset->strreset_type = SCTP_STREAM_RESET_EVENT;
sreset->strreset_flags = flags;
sreset->strreset_length = length;
sctp_ulpevent_set_owner(event, asoc);
sreset->strreset_assoc_id = sctp_assoc2id(asoc);
for (i = 0; i < stream_num; i++)
sreset->strreset_stream_list[i] = ntohs(stream_list[i]);
return event;
}
/* Return the notification type, assuming this is a notification
* event.
*/
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment