Commit 1ce463dd authored by David S. Miller's avatar David S. Miller

Merge branch 'sctp-sender-side-stream-reconf-ssn-reset-request-chunk'

Xin Long says:

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

Patch 6/6 is to implement sender-side procedures for the Outgoing
and Incoming SSN Reset Request Parameter described in rfc6525
section 5.1.2 and 5.1.3

Patches 1-5/6 are ahead of it to define some apis and asoc members
for it.

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:
  - put these into a smaller group.
  - rename some temporary variables in the codes.
  - rename the titles of the commits and improve some changelogs.
v2->v3:
  - re-split the patchset and make sure it has no dead codes for review.
v3->v4:
  - move sctp_make_reconf() into patch 1/6 to avoid kbuild warning.
  - drop unused struct sctp_strreset_req.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents b16ed2b1 7f9d68ac
......@@ -108,6 +108,7 @@ typedef enum {
/* Use hex, as defined in ADDIP sec. 3.1 */
SCTP_CID_ASCONF = 0xC1,
SCTP_CID_ASCONF_ACK = 0x80,
SCTP_CID_RECONF = 0x82,
} sctp_cid_t; /* enum */
......@@ -199,6 +200,13 @@ typedef enum {
SCTP_PARAM_SUCCESS_REPORT = cpu_to_be16(0xc005),
SCTP_PARAM_ADAPTATION_LAYER_IND = cpu_to_be16(0xc006),
/* RE-CONFIG. Section 4 */
SCTP_PARAM_RESET_OUT_REQUEST = cpu_to_be16(0x000d),
SCTP_PARAM_RESET_IN_REQUEST = cpu_to_be16(0x000e),
SCTP_PARAM_RESET_TSN_REQUEST = cpu_to_be16(0x000f),
SCTP_PARAM_RESET_RESPONSE = cpu_to_be16(0x0010),
SCTP_PARAM_RESET_ADD_OUT_STREAMS = cpu_to_be16(0x0011),
SCTP_PARAM_RESET_ADD_IN_STREAMS = cpu_to_be16(0x0012),
} sctp_param_t; /* enum */
......@@ -710,4 +718,23 @@ struct sctp_infox {
struct sctp_association *asoc;
};
struct sctp_reconf_chunk {
sctp_chunkhdr_t chunk_hdr;
__u8 params[0];
} __packed;
struct sctp_strreset_outreq {
sctp_paramhdr_t param_hdr;
__u32 request_seq;
__u32 response_seq;
__u32 send_reset_at_tsn;
__u16 list_of_streams[0];
} __packed;
struct sctp_strreset_inreq {
sctp_paramhdr_t param_hdr;
__u32 request_seq;
__u16 list_of_streams[0];
} __packed;
#endif /* __LINUX_SCTP_H__ */
......@@ -118,6 +118,9 @@ struct netns_sctp {
/* Flag to indicate if PR-SCTP is enabled. */
int prsctp_enable;
/* Flag to indicate if PR-CONFIG is enabled. */
int reconf_enable;
/* Flag to idicate if SCTP-AUTH is enabled */
int auth_enable;
......
......@@ -90,6 +90,7 @@ typedef enum {
SCTP_EVENT_TIMEOUT_T4_RTO,
SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD,
SCTP_EVENT_TIMEOUT_HEARTBEAT,
SCTP_EVENT_TIMEOUT_RECONF,
SCTP_EVENT_TIMEOUT_SACK,
SCTP_EVENT_TIMEOUT_AUTOCLOSE,
} sctp_event_timeout_t;
......@@ -113,9 +114,10 @@ typedef enum {
SCTP_PRIMITIVE_SEND,
SCTP_PRIMITIVE_REQUESTHEARTBEAT,
SCTP_PRIMITIVE_ASCONF,
SCTP_PRIMITIVE_RECONF,
} sctp_event_primitive_t;
#define SCTP_EVENT_PRIMITIVE_MAX SCTP_PRIMITIVE_ASCONF
#define SCTP_EVENT_PRIMITIVE_MAX SCTP_PRIMITIVE_RECONF
#define SCTP_NUM_PRIMITIVE_TYPES (SCTP_EVENT_PRIMITIVE_MAX + 1)
/* We define here a utility type for manipulating subtypes.
......
......@@ -141,6 +141,8 @@ int sctp_primitive_ABORT(struct net *, struct sctp_association *, void *arg);
int sctp_primitive_SEND(struct net *, struct sctp_association *, void *arg);
int sctp_primitive_REQUESTHEARTBEAT(struct net *, struct sctp_association *, void *arg);
int sctp_primitive_ASCONF(struct net *, struct sctp_association *, void *arg);
int sctp_primitive_RECONF(struct net *net, struct sctp_association *asoc,
void *arg);
/*
* sctp/input.c
......@@ -191,6 +193,12 @@ void sctp_remaddr_proc_exit(struct net *net);
*/
int sctp_offload_init(void);
/*
* sctp/stream.c
*/
int sctp_send_reset_streams(struct sctp_association *asoc,
struct sctp_reset_streams *params);
/*
* Module global variables
*/
......
......@@ -157,6 +157,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_reconf;
/* Prototypes for other event state functions. */
sctp_state_fn_t sctp_sf_do_no_pending_tsn;
......@@ -167,6 +168,7 @@ sctp_state_fn_t sctp_sf_cookie_wait_icmp_abort;
/* Prototypes for timeout event state functions. */
sctp_state_fn_t sctp_sf_do_6_3_3_rtx;
sctp_state_fn_t sctp_sf_send_reconf;
sctp_state_fn_t sctp_sf_do_6_2_sack;
sctp_state_fn_t sctp_sf_autoclose_timer_expire;
......@@ -259,7 +261,10 @@ 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_strreset_req(
const struct sctp_association *asoc,
__u16 stream_num, __u16 *stream_list,
bool out, bool in);
void sctp_chunk_assign_tsn(struct sctp_chunk *);
void sctp_chunk_assign_ssn(struct sctp_chunk *);
......@@ -275,6 +280,7 @@ int sctp_do_sm(struct net *net, sctp_event_t event_type, sctp_subtype_t subtype,
/* 2nd level prototypes */
void sctp_generate_t3_rtx_event(unsigned long peer);
void sctp_generate_heartbeat_event(unsigned long peer);
void sctp_generate_reconf_event(unsigned long peer);
void sctp_generate_proto_unreach_event(unsigned long peer);
void sctp_ootb_pkt_free(struct sctp_packet *);
......
......@@ -877,6 +877,9 @@ struct sctp_transport {
/* Timer to handle ICMP proto unreachable envets */
struct timer_list proto_unreach_timer;
/* Timer to handler reconf chunk rtx */
struct timer_list reconf_timer;
/* Since we're using per-destination retransmission timers
* (see above), we're also using per-destination "transmitted"
* queues. This probably ought to be a private struct
......@@ -935,6 +938,7 @@ void sctp_transport_pmtu(struct sctp_transport *, struct sock *sk);
void sctp_transport_free(struct sctp_transport *);
void sctp_transport_reset_t3_rtx(struct sctp_transport *);
void sctp_transport_reset_hb_timer(struct sctp_transport *);
void sctp_transport_reset_reconf_timer(struct sctp_transport *transport);
int sctp_transport_hold(struct sctp_transport *);
void sctp_transport_put(struct sctp_transport *);
void sctp_transport_update_rto(struct sctp_transport *, __u32);
......@@ -1251,7 +1255,10 @@ struct sctp_endpoint {
struct list_head endpoint_shared_keys;
__u16 active_key_id;
__u8 auth_enable:1,
prsctp_enable:1;
prsctp_enable:1,
reconf_enable:1;
__u8 strreset_enable;
};
/* Recover the outter endpoint structure. */
......@@ -1504,6 +1511,7 @@ struct sctp_association {
hostname_address:1, /* Peer understands DNS addresses? */
asconf_capable:1, /* Does peer support ADDIP? */
prsctp_capable:1, /* Can peer do PR-SCTP? */
reconf_capable:1, /* Can peer do RE-CONFIG? */
auth_capable:1; /* Is peer doing SCTP-AUTH? */
/* sack_needed : This flag indicates if the next received
......@@ -1863,7 +1871,16 @@ struct sctp_association {
__u8 need_ecne:1, /* Need to send an ECNE Chunk? */
temp:1, /* Is it a temporary association? */
prsctp_enable:1;
prsctp_enable:1,
reconf_enable:1;
__u8 strreset_enable;
__u8 strreset_outstanding; /* request param count on the fly */
__u32 strreset_outseq; /* Update after receiving response */
__u32 strreset_inseq; /* Update after receiving request */
struct sctp_chunk *strreset_chunk; /* save request chunk */
struct sctp_priv_assoc_stats stats;
......
......@@ -115,6 +115,8 @@ typedef __s32 sctp_assoc_t;
#define SCTP_PR_SUPPORTED 113
#define SCTP_DEFAULT_PRINFO 114
#define SCTP_PR_ASSOC_STATUS 115
#define SCTP_ENABLE_STREAM_RESET 118
#define SCTP_RESET_STREAMS 119
/* PR-SCTP policies */
#define SCTP_PR_SCTP_NONE 0x0000
......@@ -138,6 +140,15 @@ typedef __s32 sctp_assoc_t;
#define SCTP_PR_RTX_ENABLED(x) (SCTP_PR_POLICY(x) == SCTP_PR_SCTP_RTX)
#define SCTP_PR_PRIO_ENABLED(x) (SCTP_PR_POLICY(x) == SCTP_PR_SCTP_PRIO)
/* For enable stream reset */
#define SCTP_ENABLE_RESET_STREAM_REQ 0x01
#define SCTP_ENABLE_RESET_ASSOC_REQ 0x02
#define SCTP_ENABLE_CHANGE_ASSOC_REQ 0x04
#define SCTP_ENABLE_STRRESET_MASK 0x07
#define SCTP_STREAM_RESET_INCOMING 0x01
#define SCTP_STREAM_RESET_OUTGOING 0x02
/* These are bit fields for msghdr->msg_flags. See section 5.1. */
/* On user space Linux, these live in <bits/socket.h> as an enum. */
enum sctp_msg_flags {
......@@ -1008,4 +1019,11 @@ struct sctp_info {
__u32 __reserved3;
};
struct sctp_reset_streams {
sctp_assoc_t srs_assoc_id;
uint16_t srs_flags;
uint16_t srs_number_streams; /* 0 == ALL */
uint16_t srs_stream_list[]; /* list if srs_num_streams is not 0 */
};
#endif /* _UAPI_SCTP_H */
......@@ -207,6 +207,7 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
* association to the same value as the initial TSN.
*/
asoc->addip_serial = asoc->c.initial_tsn;
asoc->strreset_outseq = asoc->c.initial_tsn;
INIT_LIST_HEAD(&asoc->addip_chunk_list);
INIT_LIST_HEAD(&asoc->asconf_ack_list);
......@@ -269,6 +270,8 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
asoc->active_key_id = ep->active_key_id;
asoc->prsctp_enable = ep->prsctp_enable;
asoc->reconf_enable = ep->reconf_enable;
asoc->strreset_enable = ep->strreset_enable;
/* Save the hmacs and chunks list into this association */
if (ep->auth_hmacs_list)
......@@ -361,6 +364,9 @@ void sctp_association_free(struct sctp_association *asoc)
/* Free stream information. */
sctp_stream_free(asoc->stream);
if (asoc->strreset_chunk)
sctp_chunk_free(asoc->strreset_chunk);
/* Clean up the bound address list. */
sctp_bind_addr_free(&asoc->base.bind_addr);
......@@ -519,6 +525,12 @@ void sctp_assoc_rm_peer(struct sctp_association *asoc,
if (asoc->peer.last_data_from == peer)
asoc->peer.last_data_from = transport;
if (asoc->strreset_chunk &&
asoc->strreset_chunk->transport == peer) {
asoc->strreset_chunk->transport = transport;
sctp_transport_reset_reconf_timer(transport);
}
/* If we remove the transport an INIT was last sent to, set it to
* NULL. Combined with the update of the retran path above, this
* will cause the next INIT to be sent to the next available
......
......@@ -164,6 +164,7 @@ static struct sctp_endpoint *sctp_endpoint_init(struct sctp_endpoint *ep,
ep->auth_hmacs_list = auth_hmacs;
ep->auth_chunk_list = auth_chunks;
ep->prsctp_enable = net->sctp.prsctp_enable;
ep->reconf_enable = net->sctp.reconf_enable;
return ep;
......
......@@ -915,22 +915,28 @@ static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp)
case SCTP_CID_ECN_ECNE:
case SCTP_CID_ASCONF:
case SCTP_CID_FWD_TSN:
case SCTP_CID_RECONF:
status = sctp_packet_transmit_chunk(packet, chunk,
one_packet, gfp);
if (status != SCTP_XMIT_OK) {
/* put the chunk back */
list_add(&chunk->list, &q->control_chunk_list);
} else {
asoc->stats.octrlchunks++;
/* PR-SCTP C5) If a FORWARD TSN is sent, the
* sender MUST assure that at least one T3-rtx
* timer is running.
*/
if (chunk->chunk_hdr->type == SCTP_CID_FWD_TSN) {
sctp_transport_reset_t3_rtx(transport);
transport->last_time_sent = jiffies;
}
break;
}
asoc->stats.octrlchunks++;
/* PR-SCTP C5) If a FORWARD TSN is sent, the
* sender MUST assure that at least one T3-rtx
* timer is running.
*/
if (chunk->chunk_hdr->type == SCTP_CID_FWD_TSN) {
sctp_transport_reset_t3_rtx(transport);
transport->last_time_sent = jiffies;
}
if (chunk == asoc->strreset_chunk)
sctp_transport_reset_reconf_timer(transport);
break;
default:
......@@ -1016,6 +1022,8 @@ static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp)
/* Finally, transmit new packets. */
while ((chunk = sctp_outq_dequeue_data(q)) != NULL) {
__u32 sid = ntohs(chunk->subh.data_hdr->stream);
/* RFC 2960 6.5 Every DATA chunk MUST carry a valid
* stream identifier.
*/
......@@ -1038,6 +1046,11 @@ static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp)
continue;
}
if (asoc->stream->out[sid].state == SCTP_STREAM_CLOSED) {
sctp_outq_head_data(q, chunk);
goto sctp_flush_out;
}
/* If there is a specified transport, use it.
* Otherwise, we want to use the active path.
*/
......
......@@ -211,3 +211,6 @@ DECLARE_PRIMITIVE(REQUESTHEARTBEAT);
*/
DECLARE_PRIMITIVE(ASCONF);
/* RE-CONFIG 5.1 */
DECLARE_PRIMITIVE(RECONF);
......@@ -1258,6 +1258,9 @@ static int __net_init sctp_defaults_init(struct net *net)
/* Enable PR-SCTP by default. */
net->sctp.prsctp_enable = 1;
/* Disable RECONF by default. */
net->sctp.reconf_enable = 0;
/* Disable AUTH by default. */
net->sctp.auth_enable = 0;
......
......@@ -270,6 +270,11 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc,
num_ext += 2;
}
if (asoc->reconf_enable) {
extensions[num_ext] = SCTP_CID_RECONF;
num_ext += 1;
}
if (sp->adaptation_ind)
chunksize += sizeof(aiparam);
......@@ -434,6 +439,11 @@ struct sctp_chunk *sctp_make_init_ack(const struct sctp_association *asoc,
num_ext += 2;
}
if (asoc->peer.reconf_capable) {
extensions[num_ext] = SCTP_CID_RECONF;
num_ext += 1;
}
if (sp->adaptation_ind)
chunksize += sizeof(aiparam);
......@@ -1844,6 +1854,7 @@ struct sctp_association *sctp_unpack_cookie(
retval->next_tsn = retval->c.initial_tsn;
retval->ctsn_ack_point = retval->next_tsn - 1;
retval->addip_serial = retval->c.initial_tsn;
retval->strreset_outseq = retval->c.initial_tsn;
retval->adv_peer_ack_point = retval->ctsn_ack_point;
retval->peer.prsctp_capable = retval->c.prsctp_capable;
retval->peer.adaptation_ind = retval->c.adaptation_ind;
......@@ -2011,6 +2022,11 @@ static void sctp_process_ext_param(struct sctp_association *asoc,
for (i = 0; i < num_ext; i++) {
switch (param.ext->chunks[i]) {
case SCTP_CID_RECONF:
if (asoc->reconf_enable &&
!asoc->peer.reconf_capable)
asoc->peer.reconf_capable = 1;
break;
case SCTP_CID_FWD_TSN:
if (asoc->prsctp_enable && !asoc->peer.prsctp_capable)
asoc->peer.prsctp_capable = 1;
......@@ -2387,6 +2403,8 @@ int sctp_process_init(struct sctp_association *asoc, struct sctp_chunk *chunk,
asoc->peer.i.initial_tsn =
ntohl(peer_init->init_hdr.initial_tsn);
asoc->strreset_inseq = asoc->peer.i.initial_tsn;
/* Apply the upper bounds for output streams based on peer's
* number of inbound streams.
*/
......@@ -3524,3 +3542,121 @@ struct sctp_chunk *sctp_make_fwdtsn(const struct sctp_association *asoc,
return retval;
}
/* RE-CONFIG 3.1 (RE-CONFIG 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 = 130 | Chunk Flags | Chunk Length |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* \ \
* / Re-configuration Parameter /
* \ \
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* \ \
* / Re-configuration Parameter (optional) /
* \ \
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
static struct sctp_chunk *sctp_make_reconf(
const struct sctp_association *asoc,
int length)
{
struct sctp_reconf_chunk *reconf;
struct sctp_chunk *retval;
retval = sctp_make_control(asoc, SCTP_CID_RECONF, 0, length,
GFP_ATOMIC);
if (!retval)
return NULL;
reconf = (struct sctp_reconf_chunk *)retval->chunk_hdr;
retval->param_hdr.v = reconf->params;
return retval;
}
/* RE-CONFIG 4.1 (STREAM OUT RESET)
* 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 = 13 | Parameter Length = 16 + 2 * N |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Re-configuration Request Sequence Number |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Re-configuration Response Sequence Number |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Sender's Last Assigned TSN |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Stream Number 1 (optional) | Stream Number 2 (optional) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* / ...... /
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Stream Number N-1 (optional) | Stream Number N (optional) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* RE-CONFIG 4.2 (STREAM IN RESET)
* 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 = 14 | Parameter Length = 8 + 2 * N |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Re-configuration Request Sequence Number |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Stream Number 1 (optional) | Stream Number 2 (optional) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* / ...... /
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Stream Number N-1 (optional) | Stream Number N (optional) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
struct sctp_chunk *sctp_make_strreset_req(
const struct sctp_association *asoc,
__u16 stream_num, __u16 *stream_list,
bool out, bool in)
{
struct sctp_strreset_outreq outreq;
__u16 stream_len = stream_num * 2;
struct sctp_strreset_inreq inreq;
struct sctp_chunk *retval;
__u16 outlen, inlen, i;
outlen = (sizeof(outreq) + stream_len) * out;
inlen = (sizeof(inreq) + stream_len) * in;
retval = sctp_make_reconf(asoc, outlen + inlen);
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);
outreq.request_seq = htonl(asoc->strreset_outseq);
outreq.response_seq = htonl(asoc->strreset_inseq - 1);
outreq.send_reset_at_tsn = htonl(asoc->next_tsn - 1);
sctp_addto_chunk(retval, sizeof(outreq), &outreq);
if (stream_len)
sctp_addto_chunk(retval, stream_len, stream_list);
}
if (inlen) {
inreq.param_hdr.type = SCTP_PARAM_RESET_IN_REQUEST;
inreq.param_hdr.length = htons(inlen);
inreq.request_seq = htonl(asoc->strreset_outseq + out);
sctp_addto_chunk(retval, sizeof(inreq), &inreq);
if (stream_len)
sctp_addto_chunk(retval, stream_len, stream_list);
}
for (i = 0; i < stream_num; i++)
stream_list[i] = ntohs(stream_list[i]);
return retval;
}
......@@ -436,6 +436,37 @@ void sctp_generate_proto_unreach_event(unsigned long data)
sctp_association_put(asoc);
}
/* Handle the timeout of the RE-CONFIG timer. */
void sctp_generate_reconf_event(unsigned long data)
{
struct sctp_transport *transport = (struct sctp_transport *)data;
struct sctp_association *asoc = transport->asoc;
struct sock *sk = asoc->base.sk;
struct net *net = sock_net(sk);
int error = 0;
bh_lock_sock(sk);
if (sock_owned_by_user(sk)) {
pr_debug("%s: sock is busy\n", __func__);
/* Try again later. */
if (!mod_timer(&transport->reconf_timer, jiffies + (HZ / 20)))
sctp_transport_hold(transport);
goto out_unlock;
}
error = sctp_do_sm(net, SCTP_EVENT_T_TIMEOUT,
SCTP_ST_TIMEOUT(SCTP_EVENT_TIMEOUT_RECONF),
asoc->state, asoc->ep, asoc,
transport, GFP_ATOMIC);
if (error)
sk->sk_err = -error;
out_unlock:
bh_unlock_sock(sk);
sctp_transport_put(transport);
}
/* Inject a SACK Timeout event into the state machine. */
static void sctp_generate_sack_event(unsigned long data)
......@@ -453,6 +484,7 @@ sctp_timer_event_t *sctp_timer_events[SCTP_NUM_TIMEOUT_TYPES] = {
sctp_generate_t4_rto_event,
sctp_generate_t5_shutdown_guard_event,
NULL,
NULL,
sctp_generate_sack_event,
sctp_generate_autoclose_event,
};
......
......@@ -1021,6 +1021,34 @@ sctp_disposition_t sctp_sf_sendbeat_8_3(struct net *net,
return SCTP_DISPOSITION_CONSUME;
}
/* resend asoc strreset_chunk. */
sctp_disposition_t sctp_sf_send_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_transport *transport = arg;
if (asoc->overall_error_count >= asoc->max_retrans) {
sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR,
SCTP_ERROR(ETIMEDOUT));
/* CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */
sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
SCTP_PERR(SCTP_ERROR_NO_ERROR));
SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS);
SCTP_DEC_STATS(net, SCTP_MIB_CURRESTAB);
return SCTP_DISPOSITION_DELETE_TCB;
}
sctp_chunk_hold(asoc->strreset_chunk);
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
SCTP_CHUNK(asoc->strreset_chunk));
sctp_add_cmd_sf(commands, SCTP_CMD_STRIKE, SCTP_TRANSPORT(transport));
return SCTP_DISPOSITION_CONSUME;
}
/*
* Process an heartbeat request.
*
......@@ -5157,6 +5185,19 @@ sctp_disposition_t sctp_sf_do_prm_asconf(struct net *net,
return SCTP_DISPOSITION_CONSUME;
}
/* RE-CONFIG Section 5.1 RECONF Chunk Procedures */
sctp_disposition_t sctp_sf_do_prm_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_chunk *chunk = arg;
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(chunk));
return SCTP_DISPOSITION_CONSUME;
}
/*
* Ignore the primitive event
*
......
......@@ -643,6 +643,25 @@ chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = {
TYPE_SCTP_FUNC(sctp_sf_error_shutdown), \
} /* TYPE_SCTP_PRIMITIVE_ASCONF */
#define TYPE_SCTP_PRIMITIVE_RECONF { \
/* 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_reconf), \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
TYPE_SCTP_FUNC(sctp_sf_do_prm_reconf), \
/* SCTP_STATE_SHUTDOWN_SENT */ \
TYPE_SCTP_FUNC(sctp_sf_do_prm_reconf), \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
TYPE_SCTP_FUNC(sctp_sf_do_prm_reconf), \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
TYPE_SCTP_FUNC(sctp_sf_error_shutdown), \
} /* TYPE_SCTP_PRIMITIVE_RECONF */
/* The primary index for this table is the primitive type.
* The secondary index for this table is the state.
*/
......@@ -653,6 +672,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_RECONF,
};
#define TYPE_SCTP_OTHER_NO_PENDING_TSN { \
......@@ -888,6 +908,25 @@ 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_RECONF { \
/* 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_send_reconf), \
/* 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), \
}
static const sctp_sm_table_entry_t timeout_event_table[SCTP_NUM_TIMEOUT_TYPES][SCTP_STATE_NUM_STATES] = {
TYPE_SCTP_EVENT_TIMEOUT_NONE,
TYPE_SCTP_EVENT_TIMEOUT_T1_COOKIE,
......@@ -897,6 +936,7 @@ static const sctp_sm_table_entry_t timeout_event_table[SCTP_NUM_TIMEOUT_TYPES][S
TYPE_SCTP_EVENT_TIMEOUT_T4_RTO,
TYPE_SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD,
TYPE_SCTP_EVENT_TIMEOUT_HEARTBEAT,
TYPE_SCTP_EVENT_TIMEOUT_RECONF,
TYPE_SCTP_EVENT_TIMEOUT_SACK,
TYPE_SCTP_EVENT_TIMEOUT_AUTOCLOSE,
};
......
......@@ -3750,6 +3750,68 @@ static int sctp_setsockopt_default_prinfo(struct sock *sk,
return retval;
}
static int sctp_setsockopt_enable_strreset(struct sock *sk,
char __user *optval,
unsigned int optlen)
{
struct sctp_assoc_value params;
struct sctp_association *asoc;
int retval = -EINVAL;
if (optlen != sizeof(params))
goto out;
if (copy_from_user(&params, optval, optlen)) {
retval = -EFAULT;
goto out;
}
if (params.assoc_value & (~SCTP_ENABLE_STRRESET_MASK))
goto out;
asoc = sctp_id2assoc(sk, params.assoc_id);
if (asoc) {
asoc->strreset_enable = params.assoc_value;
} else if (!params.assoc_id) {
struct sctp_sock *sp = sctp_sk(sk);
sp->ep->strreset_enable = params.assoc_value;
} else {
goto out;
}
retval = 0;
out:
return retval;
}
static int sctp_setsockopt_reset_streams(struct sock *sk,
char __user *optval,
unsigned int optlen)
{
struct sctp_reset_streams *params;
struct sctp_association *asoc;
int retval = -EINVAL;
if (optlen < sizeof(struct sctp_reset_streams))
return -EINVAL;
params = memdup_user(optval, optlen);
if (IS_ERR(params))
return PTR_ERR(params);
asoc = sctp_id2assoc(sk, params->srs_assoc_id);
if (!asoc)
goto out;
retval = sctp_send_reset_streams(asoc, params);
out:
kfree(params);
return retval;
}
/* API 6.2 setsockopt(), getsockopt()
*
* Applications use setsockopt() and getsockopt() to set or retrieve
......@@ -3916,6 +3978,12 @@ static int sctp_setsockopt(struct sock *sk, int level, int optname,
case SCTP_DEFAULT_PRINFO:
retval = sctp_setsockopt_default_prinfo(sk, optval, optlen);
break;
case SCTP_ENABLE_STREAM_RESET:
retval = sctp_setsockopt_enable_strreset(sk, optval, optlen);
break;
case SCTP_RESET_STREAMS:
retval = sctp_setsockopt_reset_streams(sk, optval, optlen);
break;
default:
retval = -ENOPROTOOPT;
break;
......@@ -6400,6 +6468,47 @@ static int sctp_getsockopt_pr_assocstatus(struct sock *sk, int len,
return retval;
}
static int sctp_getsockopt_enable_strreset(struct sock *sk, int len,
char __user *optval,
int __user *optlen)
{
struct sctp_assoc_value params;
struct sctp_association *asoc;
int retval = -EFAULT;
if (len < sizeof(params)) {
retval = -EINVAL;
goto out;
}
len = sizeof(params);
if (copy_from_user(&params, optval, len))
goto out;
asoc = sctp_id2assoc(sk, params.assoc_id);
if (asoc) {
params.assoc_value = asoc->strreset_enable;
} else if (!params.assoc_id) {
struct sctp_sock *sp = sctp_sk(sk);
params.assoc_value = sp->ep->strreset_enable;
} else {
retval = -EINVAL;
goto out;
}
if (put_user(len, optlen))
goto out;
if (copy_to_user(optval, &params, len))
goto out;
retval = 0;
out:
return retval;
}
static int sctp_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen)
{
......@@ -6567,6 +6676,10 @@ static int sctp_getsockopt(struct sock *sk, int level, int optname,
retval = sctp_getsockopt_pr_assocstatus(sk, len, optval,
optlen);
break;
case SCTP_ENABLE_STREAM_RESET:
retval = sctp_getsockopt_enable_strreset(sk, len, optval,
optlen);
break;
default:
retval = -ENOPROTOOPT;
break;
......
......@@ -33,6 +33,7 @@
*/
#include <net/sctp/sctp.h>
#include <net/sctp/sm.h>
struct sctp_stream *sctp_stream_new(__u16 incnt, __u16 outcnt, gfp_t gfp)
{
......@@ -83,3 +84,81 @@ void sctp_stream_clear(struct sctp_stream *stream)
for (i = 0; i < stream->incnt; i++)
stream->in[i].ssn = 0;
}
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;
chunk = sctp_make_strreset_req(asoc, str_nums, str_list, out, in);
if (!chunk)
goto out;
if (out) {
if (str_nums)
for (i = 0; i < str_nums; i++)
stream->out[str_list[i]].state =
SCTP_STREAM_CLOSED;
else
for (i = 0; i < stream->outcnt; i++)
stream->out[i].state = SCTP_STREAM_CLOSED;
}
asoc->strreset_outstanding = out + in;
asoc->strreset_chunk = chunk;
sctp_chunk_hold(asoc->strreset_chunk);
retval = sctp_send_reconf(asoc, chunk);
if (retval) {
sctp_chunk_put(asoc->strreset_chunk);
asoc->strreset_chunk = NULL;
}
out:
return retval;
}
......@@ -88,9 +88,11 @@ static struct sctp_transport *sctp_transport_init(struct net *net,
INIT_LIST_HEAD(&peer->transports);
setup_timer(&peer->T3_rtx_timer, sctp_generate_t3_rtx_event,
(unsigned long)peer);
(unsigned long)peer);
setup_timer(&peer->hb_timer, sctp_generate_heartbeat_event,
(unsigned long)peer);
(unsigned long)peer);
setup_timer(&peer->reconf_timer, sctp_generate_reconf_event,
(unsigned long)peer);
setup_timer(&peer->proto_unreach_timer,
sctp_generate_proto_unreach_event, (unsigned long)peer);
......@@ -144,6 +146,9 @@ void sctp_transport_free(struct sctp_transport *transport)
if (del_timer(&transport->T3_rtx_timer))
sctp_transport_put(transport);
if (del_timer(&transport->reconf_timer))
sctp_transport_put(transport);
/* Delete the ICMP proto unreachable timer if it's active. */
if (del_timer(&transport->proto_unreach_timer))
sctp_association_put(transport->asoc);
......@@ -211,6 +216,14 @@ void sctp_transport_reset_hb_timer(struct sctp_transport *transport)
sctp_transport_hold(transport);
}
void sctp_transport_reset_reconf_timer(struct sctp_transport *transport)
{
if (!timer_pending(&transport->reconf_timer))
if (!mod_timer(&transport->reconf_timer,
jiffies + transport->rto))
sctp_transport_hold(transport);
}
/* This transport has been assigned to an association.
* Initialize fields from the association or from the sock itself.
* Register the reference count in the association.
......
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