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: David S. Miller <davem@davemloft.net>
This commit is contained in:
@@ -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.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user