Re: [PATCH 5/6] sctp: implement sender side procedures for the Stream Reset chunk

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Wei Yongjun wrote:
> STREAM-RESET Extension Section 4.1.1:
> 
>    Note that before sending a Stream Reset Chunk the sender MUST ensure
>    that the peer advertised support for the stream reset extension.  The
>    indication for support of the extensions MUST be determined using the
>    Supported Extensions Parameter in either the INIT or INIT-ACK.  This
>    parameter is defined in [RFC5061].  If the chunk value '0x82' does
>    NOT appear in the supported extensions list of chunks, then the
>    sender MUST NOT send any stream reset request to the peer and any
>    request by the application for such service SHOULD be responded to
>    with an appropriate error indicating the peer SCTP stack does not
>    support the stream reset extension.
> 
>    After packaging the Stream Reset Chunk and sending it to the peer the
>    sender MUST start a 'Stream Reset Timer' when the STREAM RESET chunk
>    contains at least one request parameter.  If it contains no request
>    parameter, the Stream Reset Timer MUST NOT be started.  This timer
>    MUST use the same value as SCTP's Data transmission timer (i.e. the
>    RTO timer) and MUST use exponential backoff doubling the value at
>    every expiration.  If the timer does expire, besides doubling the
>    value, the sender MUST retransmit the Stream Reset Chunk, increment
>    the appropriate error counts (both for the association and the
>    destination), and do threshold management possibly destroying the
>    association if SCTP retransmission thresholds are surpassed.
> 
> Signed-off-by: Wei Yongjun <yjwei@xxxxxxxxxxxxxx>
> ---
>  include/net/sctp/command.h   |    1 +
>  include/net/sctp/constants.h |    4 +-
>  include/net/sctp/sctp.h      |    2 +
>  include/net/sctp/sm.h        |    5 +
>  include/net/sctp/structs.h   |    3 +
>  net/sctp/associola.c         |    5 +
>  net/sctp/outqueue.c          |    1 +
>  net/sctp/primitive.c         |    4 +
>  net/sctp/proc.c              |    1 +
>  net/sctp/sm_make_chunk.c     |  185 ++++++++++++++++++++++++++++++++++++++++++
>  net/sctp/sm_sideeffect.c     |   29 +++++++
>  net/sctp/sm_statefuns.c      |   67 +++++++++++++++
>  net/sctp/sm_statetable.c     |   44 ++++++++++
>  net/sctp/socket.c            |  131 +++++++++++++++++++++++++++++
>  14 files changed, 481 insertions(+), 1 deletions(-)
> 
> diff --git a/include/net/sctp/command.h b/include/net/sctp/command.h
> index 3b96680..e7128a9 100644
> --- a/include/net/sctp/command.h
> +++ b/include/net/sctp/command.h
> @@ -106,6 +106,7 @@ typedef enum {
>  	SCTP_CMD_ASSOC_SHKEY,    /* generate the association shared keys */
>  	SCTP_CMD_T1_RETRAN,	 /* Mark for retransmission after T1 timeout  */
>  	SCTP_CMD_UPDATE_INITTAG, /* Update peer inittag */
> +	SCTP_CMD_SETUP_T6,	 /* STREAM-RESET, setup T6 RTO timer */
>  	SCTP_CMD_LAST
>  } sctp_verb_t;
>  
> diff --git a/include/net/sctp/constants.h b/include/net/sctp/constants.h
> index b05b055..85337c2 100644
> --- a/include/net/sctp/constants.h
> +++ b/include/net/sctp/constants.h
> @@ -101,6 +101,7 @@ typedef enum {
>  	SCTP_EVENT_TIMEOUT_T3_RTX,
>  	SCTP_EVENT_TIMEOUT_T4_RTO,
>  	SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD,
> +	SCTP_EVENT_TIMEOUT_T6_RTO,
>  	SCTP_EVENT_TIMEOUT_HEARTBEAT,
>  	SCTP_EVENT_TIMEOUT_SACK,
>  	SCTP_EVENT_TIMEOUT_AUTOCLOSE,
> @@ -125,9 +126,10 @@ typedef enum {
>  	SCTP_PRIMITIVE_SEND,
>  	SCTP_PRIMITIVE_REQUESTHEARTBEAT,
>  	SCTP_PRIMITIVE_ASCONF,
> +	SCTP_PRIMITIVE_STREAMRESET,
>  } sctp_event_primitive_t;
>  
> -#define SCTP_EVENT_PRIMITIVE_MAX	SCTP_PRIMITIVE_ASCONF
> +#define SCTP_EVENT_PRIMITIVE_MAX	SCTP_PRIMITIVE_STREAMRESET
>  #define SCTP_NUM_PRIMITIVE_TYPES	(SCTP_EVENT_PRIMITIVE_MAX + 1)
>  
>  /* We define here a utility type for manipulating subtypes.
> diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h
> index 9e226be..b3723e5 100644
> --- a/include/net/sctp/sctp.h
> +++ b/include/net/sctp/sctp.h
> @@ -151,6 +151,7 @@ int sctp_primitive_ABORT(struct sctp_association *, void *arg);
>  int sctp_primitive_SEND(struct sctp_association *, void *arg);
>  int sctp_primitive_REQUESTHEARTBEAT(struct sctp_association *, void *arg);
>  int sctp_primitive_ASCONF(struct sctp_association *, void *arg);
> +int sctp_primitive_STREAMRESET(struct sctp_association *, void *arg);
>  
>  /*
>   * sctp/input.c
> @@ -260,6 +261,7 @@ enum
>  	SCTP_MIB_T3_RTX_EXPIREDS,
>  	SCTP_MIB_T4_RTO_EXPIREDS,
>  	SCTP_MIB_T5_SHUTDOWN_GUARD_EXPIREDS,
> +	SCTP_MIB_T6_RTO_EXPIREDS,
>  	SCTP_MIB_DELAY_SACK_EXPIREDS,
>  	SCTP_MIB_AUTOCLOSE_EXPIREDS,
>  	SCTP_MIB_T1_RETRANSMITS,
> diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h
> index c1dd893..4f40111 100644
> --- a/include/net/sctp/sm.h
> +++ b/include/net/sctp/sm.h
> @@ -120,6 +120,7 @@ sctp_state_fn_t sctp_sf_t1_cookie_timer_expire;
>  sctp_state_fn_t sctp_sf_t2_timer_expire;
>  sctp_state_fn_t sctp_sf_t4_timer_expire;
>  sctp_state_fn_t sctp_sf_t5_timer_expire;
> +sctp_state_fn_t sctp_sf_t6_timer_expire;
>  sctp_state_fn_t sctp_sf_sendbeat_8_3;
>  sctp_state_fn_t sctp_sf_beat_8_3;
>  sctp_state_fn_t sctp_sf_backbeat_8_3;
> @@ -163,6 +164,7 @@ sctp_state_fn_t sctp_sf_error_shutdown;
>  sctp_state_fn_t sctp_sf_ignore_primitive;
>  sctp_state_fn_t sctp_sf_do_prm_requestheartbeat;
>  sctp_state_fn_t sctp_sf_do_prm_asconf;
> +sctp_state_fn_t sctp_sf_do_prm_stream_reset;
>  
>  /* Prototypes for other event state functions.  */
>  sctp_state_fn_t sctp_sf_do_9_2_start_shutdown;
> @@ -262,6 +264,9 @@ struct sctp_chunk *sctp_make_fwdtsn(const struct sctp_association *asoc,
>  				    __u32 new_cum_tsn, size_t nstreams,
>  				    struct sctp_fwdtsn_skip *skiplist);
>  struct sctp_chunk *sctp_make_auth(const struct sctp_association *asoc);
> +struct sctp_chunk *sctp_make_stream_reset(struct sctp_association *asoc,
> +					  __u16 flags, __u16 nstreams,
> +					  __u16 *streamlist);
>  
>  void sctp_chunk_assign_tsn(struct sctp_chunk *);
>  void sctp_chunk_assign_ssn(struct sctp_chunk *);
> diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
> index 24dc286..a16d35c 100644
> --- a/include/net/sctp/structs.h
> +++ b/include/net/sctp/structs.h
> @@ -1923,6 +1923,9 @@ struct sctp_association {
>  	 */
>  	__u32 strrst_seq;
>  
> +	/* Cache the STREAM-RESET chunk last send */
> +	struct sctp_chunk *strrst_last_stream_reset;
> +
>  	/* Need to send an ECNE Chunk? */
>  	char need_ecne;
>  
> diff --git a/net/sctp/associola.c b/net/sctp/associola.c
> index d8b2e25..a2e585d 100644
> --- a/net/sctp/associola.c
> +++ b/net/sctp/associola.c
> @@ -167,6 +167,7 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
>  	asoc->timeouts[SCTP_EVENT_TIMEOUT_SACK] = asoc->sackdelay;
>  	asoc->timeouts[SCTP_EVENT_TIMEOUT_AUTOCLOSE] =
>  		sp->autoclose * HZ;
> +	asoc->timeouts[SCTP_EVENT_TIMEOUT_T6_RTO] = 0;
>  
>  	/* Initilizes the timers */
>  	for (i = SCTP_EVENT_TIMEOUT_NONE; i < SCTP_NUM_TIMEOUT_TYPES; ++i)
> @@ -449,6 +450,10 @@ void sctp_association_free(struct sctp_association *asoc)
>  	if (asoc->addip_last_asconf)
>  		sctp_chunk_free(asoc->addip_last_asconf);
>  
> +	/* Free any cached STREAM-RESET chunk. */
> +	if (asoc->strrst_last_stream_reset)
> +		sctp_chunk_free(asoc->strrst_last_stream_reset);
> +
>  	/* AUTH - Free the endpoint shared keys */
>  	sctp_auth_destroy_keys(&asoc->endpoint_shared_keys);
>  
> diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c
> index d765fc5..6f0ddc5 100644
> --- a/net/sctp/outqueue.c
> +++ b/net/sctp/outqueue.c
> @@ -856,6 +856,7 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
>  		case SCTP_CID_ECN_ECNE:
>  		case SCTP_CID_ASCONF:
>  		case SCTP_CID_FWD_TSN:
> +		case SCTP_CID_STREAM_RESET:
>  			status = sctp_packet_transmit_chunk(packet, chunk,
>  							    one_packet);
>  			if (status  != SCTP_XMIT_OK) {
> diff --git a/net/sctp/primitive.c b/net/sctp/primitive.c
> index 8cb4f06..da82df1 100644
> --- a/net/sctp/primitive.c
> +++ b/net/sctp/primitive.c
> @@ -217,3 +217,7 @@ DECLARE_PRIMITIVE(REQUESTHEARTBEAT);
>  */
>  
>  DECLARE_PRIMITIVE(ASCONF);
> +
> +/* STREAM-RESET 3.1 STREAM RESET Chunk */
> +DECLARE_PRIMITIVE(STREAMRESET);
> +
> diff --git a/net/sctp/proc.c b/net/sctp/proc.c
> index f268910..aa52b28 100644
> --- a/net/sctp/proc.c
> +++ b/net/sctp/proc.c
> @@ -64,6 +64,7 @@ static struct snmp_mib sctp_snmp_list[] = {
>  	SNMP_MIB_ITEM("SctpT3RtxExpireds", SCTP_MIB_T3_RTX_EXPIREDS),
>  	SNMP_MIB_ITEM("SctpT4RtoExpireds", SCTP_MIB_T4_RTO_EXPIREDS),
>  	SNMP_MIB_ITEM("SctpT5ShutdownGuardExpireds", SCTP_MIB_T5_SHUTDOWN_GUARD_EXPIREDS),
> +	SNMP_MIB_ITEM("SctpT6RtoExpireds", SCTP_MIB_T6_RTO_EXPIREDS),
>  	SNMP_MIB_ITEM("SctpDelaySackExpireds", SCTP_MIB_DELAY_SACK_EXPIREDS),
>  	SNMP_MIB_ITEM("SctpAutocloseExpireds", SCTP_MIB_AUTOCLOSE_EXPIREDS),
>  	SNMP_MIB_ITEM("SctpT3Retransmits", SCTP_MIB_T3_RETRANSMITS),
> diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
> index d39499f..9a27454 100644
> --- a/net/sctp/sm_make_chunk.c
> +++ b/net/sctp/sm_make_chunk.c
> @@ -3368,3 +3368,188 @@ struct sctp_chunk *sctp_make_fwdtsn(const struct sctp_association *asoc,
>  
>  	return retval;
>  }
> +
> +/* STREAM-RESET 3.2.1 Outgoing SSN Reset Request Parameter
> + *
> + *      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 = 0x000d   |      Parameter Length         |
> + *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + *     |             Stream Reset Request Sequence Number              |
> + *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + *     |             Stream Reset Response Sequence Number             |
> + *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + *     |                Senders Last Assigned TSN                      |
> + *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + *     |  Stream Number 1 (optional)   |    Stream Number 2 (optional) |
> + *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + *     /                            ......                             /
> + *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + *     |  Stream Number N-1 (optional) |    Stream Number N (optional) |
> + *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + *
> + * Create an Outgoing SSN Reset Request Parameter and add it to
> + * STREAM-RESET chunk.
> + */
> +static void sctp_add_stream_reset_out(struct sctp_chunk *retval,
> +			__u32 seq_req, __u32 seq_res, __u32 next_tsn,
> +			int len, void *streams)
> +{
> +	struct sctp_reset_out_req out;
> +
> +	out.param_hdr.type = SCTP_PARAM_RESET_OUT_REQUEST;
> +	out.param_hdr.length = htons(sizeof(struct sctp_reset_out_req) + len);
> +	out.request_seq = htonl(seq_req);
> +	out.response_seq = htonl(seq_res);
> +	out.send_reset_at_tsn = htonl(next_tsn);
> +
> +	sctp_addto_chunk(retval, sizeof(out), &out);
> +
> +	if (len > 0)
> +		sctp_addto_param(retval, len, streams);
> +}
> +
> +/* STREAM-RESET 3.2.2 Incoming SSN Reset Request Parameter
> + *
> + *      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 = 0x000e   |      Parameter Length         |
> + *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + *     |            Stream Reset Request Sequence Number               |
> + *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + *     |  Stream Number 1 (optional)   |    Stream Number 2 (optional) |
> + *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + *     /                            ......                             /
> + *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + *     |  Stream Number N-1 (optional) |    Stream Number N (optional) |
> + *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + *
> + * Create an Incoming SSN Reset Request Parameter and add it to
> + * STREAM-RESET chunk.
> + */
> +static void sctp_add_stream_reset_in(struct sctp_chunk *retval,
> +			__u32 seq_req, int len, void *streams)
> +{
> +	struct sctp_reset_in_req in;
> +
> +	in.param_hdr.type = SCTP_PARAM_RESET_IN_REQUEST;
> +	in.param_hdr.length = htons(sizeof(struct sctp_reset_in_req) + len);
> +	in.request_seq = htonl(seq_req);
> +
> +	sctp_addto_chunk(retval, sizeof(in), &in);
> +
> +	if (len > 0)
> +		sctp_addto_param(retval, len, streams);
> +}
> +
> +/* STREAM-RESET 3.2.3 SSN/TSN Reset Request Parameter
> + *
> + *      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 = 0x000f   |      Parameter Length         |
> + *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + *     |           Stream Reset Request Sequence Number                |
> + *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + *
> + * Create a SSN/TSN Reset Request Parameter and add it to STREAM-RESET chunk.
> + */
> +static void sctp_add_stream_reset_tsn(struct sctp_chunk *retval, __u32 seq_req)
> +{
> +	struct sctp_reset_tsn_req tsn;
> +
> +	tsn.param_hdr.type = SCTP_PARAM_RESET_TSN_REQUEST;
> +	tsn.param_hdr.length = htons(sizeof(struct sctp_reset_tsn_req));
> +	tsn.request_seq = htonl(seq_req);
> +
> +	sctp_addto_chunk(retval, sizeof(tsn), &tsn);
> +}
> +
> +/* STREAM-RESET 3.2.5 Add Streams
> + *
> + *      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 = 0x0011   |      Parameter Length=12      |
> + *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + *     |            Stream Reset Request Sequence Number               |
> + *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + *     |      Number of new streams    |         Reserved              |
> + *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + *
> + * Create an Add Streams Parameter and add it to STREAM-RESET chunk.
> + */
> +static void sctp_add_stream_reset_add(struct sctp_chunk *retval,
> +				      __u32 seq_req, __u32 nstreams)
> +{
> +	struct sctp_add_stream add;
> +
> +	add.param_hdr.type = SCTP_PARAM_ADD_STREAM;
> +	add.param_hdr.length = htons(sizeof(struct sctp_add_stream));
> +	add.request_seq = htonl(seq_req);
> +	add.num_streams = htons(nstreams);
> +	add.reserved = 0;
> +
> +	sctp_addto_chunk(retval, sizeof(add), &add);
> +}
> +
> +/* STREAM-RESET 3.1 STREAM RESET Chunk
> + *
> + *      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
> + *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + *     | Type = 0x82   |  Chunk Flags  |      Chunk Length             |
> + *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + *     |                    Stream Reset Parameter                     |
> + *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + *     |                    Stream Reset Parameter (optional)          |
> + *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + *
> + * Create an STREAM-RESET chunk with enough space for the parameter.
> + */
> +struct sctp_chunk *sctp_make_stream_reset(struct sctp_association *asoc,
> +					  __u16 flags, __u16 nstreams,
> +					  __u16 *streamlist)
> +{
> +	struct sctp_chunk *retval;
> +	size_t length = sizeof(struct sctp_strrst_chunk);
> +
> +	if (flags & SCTP_STREAM_RESET_OUT)
> +		length += WORD_ROUND(sizeof(struct sctp_reset_out_req) +
> +				     nstreams * 2);
> +
> +	if (flags & SCTP_STREAM_RESET_IN)
> +		length += WORD_ROUND(sizeof(struct sctp_reset_in_req) +
> +				     nstreams * 2);
> +
> +	if (flags & SCTP_STREAM_RESET_TSN)
> +		length += sizeof(struct sctp_reset_tsn_req);
> +
> +	if (flags & SCTP_STREAM_RESET_ADD)
> +		length += sizeof(struct sctp_add_stream);
> +
> +	retval = sctp_make_chunk(asoc, SCTP_CID_STREAM_RESET, 0, length);
> +	if (!retval)
> +		return NULL;
> +
> +	if (flags & SCTP_STREAM_RESET_OUT)
> +		sctp_add_stream_reset_out(retval, asoc->strrst_seq++,
> +			asoc->peer.strrst_seq, asoc->next_tsn - 1,
> +			nstreams * 2, streamlist);
> +
> +	if (flags & SCTP_STREAM_RESET_IN)
> +		sctp_add_stream_reset_in(retval, asoc->strrst_seq++,
> +			nstreams * 2, streamlist);
> +
> +	if (flags & SCTP_STREAM_RESET_TSN)
> +		sctp_add_stream_reset_tsn(retval, asoc->strrst_seq++);
> +
> +	if (flags & SCTP_STREAM_RESET_ADD)
> +		sctp_add_stream_reset_add(retval, asoc->strrst_seq++,
> +					  nstreams);
> +
> +	return retval;
> +}
> +
> diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c
> index e2020eb..baf8fb0 100644
> --- a/net/sctp/sm_sideeffect.c
> +++ b/net/sctp/sm_sideeffect.c
> @@ -404,6 +404,12 @@ static void sctp_generate_sack_event(unsigned long data)
>  	sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_SACK);
>  }
>  
> +static void sctp_generate_t6_rto_event(unsigned long data)
> +{
> +	struct sctp_association *asoc = (struct sctp_association *) data;
> +	sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_T6_RTO);
> +}
> +
>  sctp_timer_event_t *sctp_timer_events[SCTP_NUM_TIMEOUT_TYPES] = {
>  	NULL,
>  	sctp_generate_t1_cookie_event,
> @@ -412,6 +418,7 @@ sctp_timer_event_t *sctp_timer_events[SCTP_NUM_TIMEOUT_TYPES] = {
>  	NULL,
>  	sctp_generate_t4_rto_event,
>  	sctp_generate_t5_shutdown_guard_event,
> +	sctp_generate_t6_rto_event,
>  	NULL,
>  	sctp_generate_sack_event,
>  	sctp_generate_autoclose_event,
> @@ -782,6 +789,24 @@ static void sctp_cmd_setup_t4(sctp_cmd_seq_t *cmds,
>  	chunk->transport = t;
>  }
>  
> +/*
> + * STREAM-RESET Section 4.1.1 Sender side procedures for the Stream Reset Chunk
> + *
> + * After packaging the Stream Reset Chunk and sending it to the peer the
> + * sender MUST start a 'Stream Reset Timer' when the STREAM RESET chunk
> + * contains at least one request parameter.
> + */
> +static void sctp_cmd_setup_t6(sctp_cmd_seq_t *cmds,
> +				struct sctp_association *asoc,
> +				struct sctp_chunk *chunk)
> +{
> +	struct sctp_transport *t;
> +
> +	t = asoc->peer.active_path;
> +	asoc->timeouts[SCTP_EVENT_TIMEOUT_T6_RTO] = t->rto;
> +	chunk->transport = t;
> +}
> +
>  /* Process an incoming Operation Error Chunk. */
>  static void sctp_cmd_process_operr(sctp_cmd_seq_t *cmds,
>  				   struct sctp_association *asoc,
> @@ -1574,6 +1599,10 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
>  			asoc->peer.i.init_tag = cmd->obj.u32;
>  			break;
>  
> +		case SCTP_CMD_SETUP_T6:
> +			sctp_cmd_setup_t6(commands, asoc, cmd->obj.ptr);
> +			break;
> +
>  		default:
>  			printk(KERN_WARNING "Impossible command: %u, %p\n",
>  			       cmd->verb, cmd->obj.ptr);
> diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
> index 55a61aa..1a001d7 100644
> --- a/net/sctp/sm_statefuns.c
> +++ b/net/sctp/sm_statefuns.c
> @@ -4982,6 +4982,22 @@ sctp_disposition_t sctp_sf_do_prm_asconf(const struct sctp_endpoint *ep,
>  	return SCTP_DISPOSITION_CONSUME;
>  }
>  
> +/* STREAM-RESET Section 4.1 Sender side procedures */
> +sctp_disposition_t sctp_sf_do_prm_stream_reset(const struct sctp_endpoint *ep,
> +					const struct sctp_association *asoc,
> +					const sctp_subtype_t type,
> +					void *arg,
> +					sctp_cmd_seq_t *commands)
> +{
> +	struct sctp_chunk *chunk = arg;
> +
> +	sctp_add_cmd_sf(commands, SCTP_CMD_SETUP_T6, SCTP_CHUNK(chunk));
> +	sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_START,
> +			SCTP_TO(SCTP_EVENT_TIMEOUT_T6_RTO));
> +	sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(chunk));
> +	return SCTP_DISPOSITION_CONSUME;
> +}
> +
>  /*
>   * Ignore the primitive event
>   *
> @@ -5588,6 +5604,57 @@ sctp_disposition_t sctp_sf_autoclose_timer_expire(
>  	return disposition;
>  }
>  
> +/*
> + * STREAM-RESET Section 4.1.1 Sender side procedures for the Stream Reset Chunk
> + *
> + * If the timer does expire, besides doubling the value, the sender MUST
> + * retransmit the Stream Reset Chunk, increment the appropriate error
> + * counts (both for the association and the destination), and do threshold
> + * management possibly destroying the association if SCTP retransmission
> + * thresholds are surpassed.
> + */
> +sctp_disposition_t sctp_sf_t6_timer_expire(
> +	const struct sctp_endpoint *ep,
> +	const struct sctp_association *asoc,
> +	const sctp_subtype_t type,
> +	void *arg,
> +	sctp_cmd_seq_t *commands)
> +{
> +	struct sctp_chunk *chunk = asoc->strrst_last_stream_reset;
> +	struct sctp_transport *transport = chunk->transport;
> +
> +	SCTP_INC_STATS(SCTP_MIB_T6_RTO_EXPIREDS);
> +
> +	/* Increment the appropriate error counts */
> +	sctp_add_cmd_sf(commands, SCTP_CMD_STRIKE, SCTP_TRANSPORT(transport));
> +
> +	/* Reconfig T6 timer and transport. */
> +	sctp_add_cmd_sf(commands, SCTP_CMD_SETUP_T6, SCTP_CHUNK(chunk));
> +
> +	/* do threshold management possibly destroying the association if
> +	 * SCTP retransmission thresholds are surpassed
> +	 */
> +	if (asoc->overall_error_count >= asoc->max_retrans) {
> +		sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
> +				SCTP_TO(SCTP_EVENT_TIMEOUT_T6_RTO));
> +		sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR,
> +				SCTP_ERROR(ETIMEDOUT));
> +		sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
> +				SCTP_PERR(SCTP_ERROR_NO_ERROR));
> +		SCTP_INC_STATS(SCTP_MIB_ABORTEDS);
> +		SCTP_DEC_STATS(SCTP_MIB_CURRESTAB);
> +		return SCTP_DISPOSITION_ABORT;
> +	}
> +
> +	sctp_chunk_hold(chunk);
> +	sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(chunk));
> +
> +	sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART,
> +			SCTP_TO(SCTP_EVENT_TIMEOUT_T6_RTO));
> +
> +	return SCTP_DISPOSITION_CONSUME;
> +}
> +
>  /*****************************************************************************
>   * These are sa state functions which could apply to all types of events.
>   ****************************************************************************/
> diff --git a/net/sctp/sm_statetable.c b/net/sctp/sm_statetable.c
> index 5c8186d..40c9472 100644
> --- a/net/sctp/sm_statetable.c
> +++ b/net/sctp/sm_statetable.c
> @@ -700,6 +700,27 @@ chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = {
>  	TYPE_SCTP_FUNC(sctp_sf_error_shutdown), \
>  } /* TYPE_SCTP_PRIMITIVE_REQUESTHEARTBEAT */
>  
> +#define TYPE_SCTP_PRIMITIVE_STREAMRESET { \
> +	/* SCTP_STATE_EMPTY */ \
> +	TYPE_SCTP_FUNC(sctp_sf_bug), \
> +	/* SCTP_STATE_CLOSED */ \
> +	TYPE_SCTP_FUNC(sctp_sf_error_closed), \
> +	/* SCTP_STATE_COOKIE_WAIT */ \
> +	TYPE_SCTP_FUNC(sctp_sf_error_closed), \
> +	/* SCTP_STATE_COOKIE_ECHOED */ \
> +	TYPE_SCTP_FUNC(sctp_sf_error_closed), \
> +	/* SCTP_STATE_ESTABLISHED */ \
> +	TYPE_SCTP_FUNC(sctp_sf_do_prm_stream_reset), \
> +	/* SCTP_STATE_SHUTDOWN_PENDING */ \
> +	TYPE_SCTP_FUNC(sctp_sf_do_prm_stream_reset), \
> +	/* SCTP_STATE_SHUTDOWN_SENT */ \
> +	TYPE_SCTP_FUNC(sctp_sf_do_prm_stream_reset), \

Not sure if it makes sense to generate a stream reset after we've sent a SHUTDOWN.
This means that the local association is closing down, i.e. the user either issued
a close(), a shutdown(), or SCTP_EOF.  After this any user initiated sends typically
don't work.
The states where this chunk is valid are not addressed in the ID and that's one of
the existing holes there.

> +	/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
> +	TYPE_SCTP_FUNC(sctp_sf_do_prm_stream_reset), \
> +	/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
> +	TYPE_SCTP_FUNC(sctp_sf_error_shutdown), \
> +} /* TYPE_SCTP_PRIMITIVE_STREAMRESET */
> +
>  /* The primary index for this table is the primitive type.
>   * The secondary index for this table is the state.
>   */
> @@ -710,6 +731,7 @@ static const sctp_sm_table_entry_t primitive_event_table[SCTP_NUM_PRIMITIVE_TYPE
>  	TYPE_SCTP_PRIMITIVE_SEND,
>  	TYPE_SCTP_PRIMITIVE_REQUESTHEARTBEAT,
>  	TYPE_SCTP_PRIMITIVE_ASCONF,
> +	TYPE_SCTP_PRIMITIVE_STREAMRESET,
>  };
>  
>  #define TYPE_SCTP_OTHER_NO_PENDING_TSN  { \
> @@ -906,6 +928,27 @@ static const sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_
>  	TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
>  }
>  
> +#define TYPE_SCTP_EVENT_TIMEOUT_T6_RTO { \
> +	/* SCTP_STATE_EMPTY */ \
> +	TYPE_SCTP_FUNC(sctp_sf_bug), \
> +	/* SCTP_STATE_CLOSED */ \
> +	TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
> +	/* SCTP_STATE_COOKIE_WAIT */ \
> +	TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
> +	/* SCTP_STATE_COOKIE_ECHOED */ \
> +	TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
> +	/* SCTP_STATE_ESTABLISHED */ \
> +	TYPE_SCTP_FUNC(sctp_sf_t6_timer_expire), \
> +	/* SCTP_STATE_SHUTDOWN_PENDING */ \
> +	TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
> +	/* SCTP_STATE_SHUTDOWN_SENT */ \
> +	TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
> +	/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
> +	TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
> +	/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
> +	TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \

Especially since you are ignoring the timeouts, we shouldn't allow the user to
generate the chunk above.

> +}
> +
>  #define TYPE_SCTP_EVENT_TIMEOUT_HEARTBEAT { \
>  	/* SCTP_STATE_EMPTY */ \
>  	TYPE_SCTP_FUNC(sctp_sf_bug), \
> @@ -977,6 +1020,7 @@ static const sctp_sm_table_entry_t timeout_event_table[SCTP_NUM_TIMEOUT_TYPES][S
>  	TYPE_SCTP_EVENT_TIMEOUT_T3_RTX,
>  	TYPE_SCTP_EVENT_TIMEOUT_T4_RTO,
>  	TYPE_SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD,
> +	TYPE_SCTP_EVENT_TIMEOUT_T6_RTO,
>  	TYPE_SCTP_EVENT_TIMEOUT_HEARTBEAT,
>  	TYPE_SCTP_EVENT_TIMEOUT_SACK,
>  	TYPE_SCTP_EVENT_TIMEOUT_AUTOCLOSE,
> diff --git a/net/sctp/socket.c b/net/sctp/socket.c
> index bbd3cd2..33d6c58 100644
> --- a/net/sctp/socket.c
> +++ b/net/sctp/socket.c
> @@ -602,6 +602,25 @@ out:
>  	return retval;
>  }
>  
> +static int sctp_send_stream_reset(struct sctp_association *asoc,
> +				  struct sctp_chunk *chunk)
> +{
> +	int retval = 0;
> +
> +	if (asoc->strrst_last_stream_reset)
> +		return -EBUSY;
> +
> +	/* Hold the chunk until an Response is received. */
> +	sctp_chunk_hold(chunk);
> +	retval = sctp_primitive_STREAMRESET(asoc, chunk);
> +	if (retval)
> +		sctp_chunk_free(chunk);
> +	else
> +		asoc->strrst_last_stream_reset = chunk;
> +
> +	return retval;
> +}
> +
>  /* Remove a list of addresses from bind addresses list.  Do not remove the
>   * last address.
>   *
> @@ -3276,6 +3295,115 @@ static int sctp_setsockopt_del_key(struct sock *sk,
>  
>  }
>  
> +static int sctp_setsockopt_reset_stream(struct sock *sk,
> +					char __user *optval,
> +					int optlen)
> +{
> +	struct sctp_stream_reset *streams;
> +	struct sctp_association *asoc;
> +	struct sctp_chunk *chunk;
> +	int ret = 0, i;
> +	__u16 nstreams;
> +
> +	if (!sctp_strrst_enable)
> +		return -EPERM;
> +
> +	if (optlen < sizeof(struct sctp_stream_reset))
> +		return -EINVAL;
> +
> +	streams = kmalloc(optlen, GFP_KERNEL);
> +	if (!streams)
> +		return -ENOMEM;
> +
> +	if (copy_from_user(streams, optval, optlen)) {
> +		ret = -EFAULT;
> +		goto out;
> +	}
> +
> +	nstreams = streams->strrst_number_of_streams;
> +	if ((nstreams * 2 > optlen - sizeof(struct sctp_stream_reset) &&
> +	     streams->strrst_flags != SCTP_STREAM_RESET_ADD) ||
> +	    (nstreams != 0 && streams->strrst_flags == SCTP_STREAM_RESET_TSN) ||
> +	    (nstreams == 0 && streams->strrst_flags == SCTP_STREAM_RESET_ADD)) {
> +		ret = -EINVAL;
> +		goto out;
> +	}
> +
> +	asoc = sctp_id2assoc(sk, streams->strrst_assoc_id);
> +	if (!asoc) {
> +		ret = -EINVAL;
> +		goto out;
> +	}
> +
> +	if (!asoc->peer.strrst_capable) {
> +		ret = -EPERM;
> +		goto out;
> +	}
> +
> +	if (!sctp_state(asoc, ESTABLISHED)) {
> +		ret = -ENOTCONN;
> +		goto out;
> +	}
> +
> +	if (asoc->strrst_last_stream_reset)
> +		return -EBUSY;
> +
> +	/* STREAM-RESET 3.1 STREAM RESET Chunk
> +	 *
> +	 * Note each STREAM RESET chunk holds at least one parameter and at most
> +	 * two parameters.  Only the following combinations are allowed:
> +	 * 1.  Outgoing SSN Reset Request Parameter.
> +	 * 2.  Incoming SSN Reset Request Parameter.
> +	 * 3.  Outgoing SSN Reset Request Parameter, Incoming SSN Reset Request
> +	 *     Parameter.
> +	 * 4.  SSN/TSN Reset Request Parameter.
> +	 * 5.  Stream Reset Response Parameter.
> +	 * 6.  Stream Reset Response Parameter, Outgoing SSN Reset Request
> +	 *     Parameter.
> +	 * 7.  Stream Reset Response Parameter, Stream Reset Response Parameter.
> +	 */
> +	switch (streams->strrst_flags) {
> +	case SCTP_STREAM_RESET_IN:
> +	case SCTP_STREAM_RESET_OUT:
> +	case SCTP_STREAM_RESET_BOTH:
> +	{
> +		__u16 *list = streams->strrst_streams;
> +		for (i = 0; i < nstreams; i++) {
> +			if ((streams->strrst_flags & SCTP_STREAM_RESET_IN) &&
> +			    list[i] >= asoc->c.sinit_max_instreams) {
> +				ret = -EINVAL;
> +				goto out;
> +			}
> +			if ((streams->strrst_flags & SCTP_STREAM_RESET_OUT) &&
> +			    list[i] >= asoc->c.sinit_num_ostreams) {
> +				ret = -EINVAL;
> +				goto out;
> +			}
> +			list[i] = htons(list[i]);
> +		}
> +		break;
> +	}
> +	case SCTP_STREAM_RESET_TSN:
> +	case SCTP_STREAM_RESET_ADD:
> +		break;
> +	default:
> +		ret = -EINVAL;
> +		goto out;
> +	}
> +
> +	/* Create an STREAM-RESET chunk with request parameters */
> +	chunk = sctp_make_stream_reset(asoc, streams->strrst_flags,
> +				       nstreams, streams->strrst_streams);
> +	if (!chunk) {
> +		ret = -ENOMEM;
> +		goto out;
> +	}
> +
> +	ret = sctp_send_stream_reset(asoc, chunk);
> +out:
> +	kfree(streams);
> +	return ret;
> +}
>  
>  /* API 6.2 setsockopt(), getsockopt()
>   *
> @@ -3423,6 +3551,9 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname,
>  	case SCTP_AUTH_DELETE_KEY:
>  		retval = sctp_setsockopt_del_key(sk, optval, optlen);
>  		break;
> +	case SCTP_RESET_STREAMS:
> +		retval = sctp_setsockopt_reset_stream(sk, optval, optlen);
> +		break;
>  	default:
>  		retval = -ENOPROTOOPT;
>  		break;

The API part is not really appropriate yet.  You should at least extract it as a separate patch.

-vlad
--
To unsubscribe from this list: send the line "unsubscribe linux-sctp" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Networking Development]     [Linux OMAP]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux