Commit 1bafb09e authored by Jon Grimm's avatar Jon Grimm

[SCTP] Add SEND_FAILED support. (Ardelle Fan)

parent 9850a96f
......@@ -872,6 +872,9 @@ struct sctp_outq {
unsigned out_qlen; /* Total length of queued data chunks. */
/* Error of send failed, may used in SCTP_SEND_FAILED event. */
unsigned error;
/* These are control chunks we want to send. */
struct sk_buff_head control;
......
......@@ -138,13 +138,13 @@ void sctp_outq_init(sctp_association_t *asoc, struct sctp_outq *q)
}
/* Free the outqueue structure and any related pending chunks.
* FIXME: Add SEND_FAILED support.
*/
void sctp_outq_teardown(struct sctp_outq *q)
{
struct sctp_transport *transport;
struct list_head *lchunk, *pos, *temp;
sctp_chunk_t *chunk;
struct sctp_ulpevent *ev;
/* Throw away unacknowledged chunks. */
list_for_each(pos, &q->asoc->peer.transport_addr_list) {
......@@ -152,6 +152,14 @@ void sctp_outq_teardown(struct sctp_outq *q)
while ((lchunk = sctp_list_dequeue(&transport->transmitted))) {
chunk = list_entry(lchunk, sctp_chunk_t,
transmitted_list);
/* Generate a SEND FAILED event. */
ev = sctp_ulpevent_make_send_failed(q->asoc,
chunk, SCTP_DATA_SENT,
q->error, GFP_ATOMIC);
if (ev)
sctp_ulpq_tail_event(&q->asoc->ulpq, ev);
sctp_free_chunk(chunk);
}
}
......@@ -171,8 +179,19 @@ void sctp_outq_teardown(struct sctp_outq *q)
}
/* Throw away any leftover data chunks. */
while ((chunk = sctp_outq_dequeue_data(q)))
while ((chunk = sctp_outq_dequeue_data(q))) {
/* Generate a SEND FAILED event. */
ev = sctp_ulpevent_make_send_failed(q->asoc,
chunk, SCTP_DATA_UNSENT,
q->error, GFP_ATOMIC);
if (ev)
sctp_ulpq_tail_event(&q->asoc->ulpq, ev);
sctp_free_chunk(chunk);
}
q->error = 0;
/* Throw away any leftover control chunks. */
while ((chunk = (sctp_chunk_t *) skb_dequeue(&q->control)))
......
......@@ -133,7 +133,7 @@ static void sctp_do_ecn_cwr_work(sctp_association_t *asoc,
}
/* Generate SACK if necessary. We call this at the end of a packet. */
int sctp_gen_sack(struct sctp_association *asoc, int force,
int sctp_gen_sack(struct sctp_association *asoc, int force,
sctp_cmd_seq_t *commands)
{
__u32 ctsn, max_tsn_seen;
......@@ -410,7 +410,8 @@ static void sctp_do_8_2_transport_strike(sctp_association_t *asoc,
/* Worker routine to handle INIT command failure. */
static void sctp_cmd_init_failed(sctp_cmd_seq_t *commands,
sctp_association_t *asoc)
sctp_association_t *asoc,
unsigned error)
{
struct sctp_ulpevent *event;
......@@ -421,46 +422,27 @@ static void sctp_cmd_init_failed(sctp_cmd_seq_t *commands,
sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP,
SCTP_ULPEVENT(event));
/* FIXME: We need to handle data possibly either
* sent via COOKIE-ECHO bundling or just waiting in
* the transmit queue, if the user has enabled
* SEND_FAILED notifications.
*/
/* SEND_FAILED sent later when cleaning up the association. */
asoc->outqueue.error = error;
sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL());
}
/* Worker routine to handle SCTP_CMD_ASSOC_FAILED. */
static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *commands,
sctp_association_t *asoc,
struct sctp_association *asoc,
sctp_event_t event_type,
sctp_subtype_t subtype,
sctp_chunk_t *chunk)
struct sctp_chunk *chunk,
unsigned error)
{
struct sctp_ulpevent *event;
__u16 error = 0;
switch(event_type) {
case SCTP_EVENT_T_PRIMITIVE:
if (SCTP_PRIMITIVE_ABORT == subtype.primitive)
error = SCTP_ERROR_USER_ABORT;
break;
case SCTP_EVENT_T_CHUNK:
if (chunk && (SCTP_CID_ABORT == chunk->chunk_hdr->type) &&
(ntohs(chunk->chunk_hdr->length) >=
(sizeof(struct sctp_chunkhdr) +
sizeof(struct sctp_errhdr)))) {
error = ((sctp_errhdr_t *)chunk->skb->data)->cause;
}
break;
default:
break;
}
/* Cancel any partial delivery in progress. */
sctp_ulpq_abort_pd(&asoc->ulpq, GFP_ATOMIC);
event = sctp_ulpevent_make_assoc_change(asoc, 0, SCTP_COMM_LOST,
error, 0, 0, GFP_ATOMIC);
(__u16)error, 0, 0,
GFP_ATOMIC);
if (event)
sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP,
SCTP_ULPEVENT(event));
......@@ -468,9 +450,8 @@ static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *commands,
sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
SCTP_STATE(SCTP_STATE_CLOSED));
/* FIXME: We need to handle data that could not be sent or was not
* acked, if the user has enabled SEND_FAILED notifications.
*/
/* SEND_FAILED sent later when cleaning up the association. */
asoc->outqueue.error = error;
sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL());
}
......@@ -640,9 +621,9 @@ static void sctp_cmd_new_state(sctp_cmd_seq_t *cmds, sctp_association_t *asoc,
asoc->state_timestamp = jiffies;
if ((SCTP_STATE_ESTABLISHED == asoc->state) ||
(SCTP_STATE_CLOSED == asoc->state)) {
(SCTP_STATE_CLOSED == asoc->state)) {
/* Wake up any processes waiting in the asoc's wait queue in
* sctp_wait_for_connect() or sctp_wait_for_sndbuf().
* sctp_wait_for_connect() or sctp_wait_for_sndbuf().
*/
if (waitqueue_active(&asoc->wait))
wake_up_interruptible(&asoc->wait);
......@@ -1077,12 +1058,12 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
break;
case SCTP_CMD_INIT_FAILED:
sctp_cmd_init_failed(commands, asoc);
sctp_cmd_init_failed(commands, asoc, cmd->obj.u32);
break;
case SCTP_CMD_ASSOC_FAILED:
sctp_cmd_assoc_failed(commands, asoc, event_type,
subtype, chunk);
subtype, chunk, cmd->obj.u32);
break;
case SCTP_CMD_COUNTER_INC:
......@@ -1094,7 +1075,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
break;
case SCTP_CMD_REPORT_DUP:
sctp_tsnmap_mark_dup(&asoc->peer.tsn_map,
sctp_tsnmap_mark_dup(&asoc->peer.tsn_map,
cmd->obj.u32);
break;
......
......@@ -729,7 +729,8 @@ sctp_disposition_t sctp_sf_sendbeat_8_3(const sctp_endpoint_t *ep,
if (asoc->overall_error_count >= asoc->overall_error_threshold) {
/* CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */
sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_NULL());
sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
SCTP_U32(SCTP_ERROR_NO_ERROR));
SCTP_INC_STATS(SctpAborteds);
SCTP_DEC_STATS(SctpCurrEstab);
return SCTP_DISPOSITION_DELETE_TCB;
......@@ -1772,14 +1773,16 @@ sctp_disposition_t sctp_sf_cookie_echoed_err(const sctp_endpoint_t *ep,
sctp_chunk_t *chunk = arg;
sctp_errhdr_t *err;
err = (sctp_errhdr_t *)(chunk->skb->data);
/* If we have gotten too many failures, give up. */
if (1 + asoc->counters[SCTP_COUNTER_INIT_ERROR] >
asoc->max_init_attempts) {
/* INIT_FAILED will issue an ulpevent. */
sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, SCTP_NULL());
sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED,
SCTP_U32(err->cause));
return SCTP_DISPOSITION_DELETE_TCB;
}
err = (sctp_errhdr_t *)(chunk->skb->data);
/* Process the error here */
switch (err->cause) {
......@@ -1834,7 +1837,8 @@ sctp_disposition_t sctp_sf_do_5_2_6_stale(const sctp_endpoint_t *ep,
attempts = asoc->counters[SCTP_COUNTER_INIT_ERROR] + 1;
if (attempts >= asoc->max_init_attempts) {
sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, SCTP_NULL());
sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED,
SCTP_U32(SCTP_ERROR_STALE_COOKIE));
return SCTP_DISPOSITION_DELETE_TCB;
}
......@@ -1936,12 +1940,18 @@ sctp_disposition_t sctp_sf_do_9_1_abort(const sctp_endpoint_t *ep,
sctp_cmd_seq_t *commands)
{
sctp_chunk_t *chunk = arg;
__u16 error = SCTP_ERROR_NO_ERROR;
if (!sctp_vtag_verify_either(chunk, asoc))
return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
if (chunk && (ntohs(chunk->chunk_hdr->length) >=
(sizeof(struct sctp_chunkhdr) +
sizeof(struct sctp_errhdr))))
error = ((sctp_errhdr_t *)chunk->skb->data)->cause;
/* ASSOC_FAILED will DELETE_TCB. */
sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_NULL());
sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_U32(error));
SCTP_INC_STATS(SctpAborteds);
SCTP_DEC_STATS(SctpCurrEstab);
......@@ -1961,6 +1971,7 @@ sctp_disposition_t sctp_sf_cookie_wait_abort(const sctp_endpoint_t *ep,
sctp_cmd_seq_t *commands)
{
sctp_chunk_t *chunk = arg;
__u16 error = SCTP_ERROR_NO_ERROR;
if (!sctp_vtag_verify_either(chunk, asoc))
return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
......@@ -1971,10 +1982,14 @@ sctp_disposition_t sctp_sf_cookie_wait_abort(const sctp_endpoint_t *ep,
sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT));
if (chunk && (ntohs(chunk->chunk_hdr->length) >=
(sizeof(struct sctp_chunkhdr) +
sizeof(struct sctp_errhdr))))
error = ((sctp_errhdr_t *)chunk->skb->data)->cause;
/* CMD_INIT_FAILED will DELETE_TCB. */
sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, SCTP_NULL());
sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, SCTP_U32(error));
/* BUG? This does not look complete... */
return SCTP_DISPOSITION_ABORT;
}
......@@ -2381,7 +2396,8 @@ sctp_disposition_t sctp_sf_eat_data_6_2(const sctp_endpoint_t *ep,
* processing the rest of the chunks in the packet.
*/
sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET,SCTP_NULL());
sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_NULL());
sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
SCTP_U32(SCTP_ERROR_NO_DATA));
SCTP_INC_STATS(SctpAborteds);
SCTP_DEC_STATS(SctpCurrEstab);
return SCTP_DISPOSITION_CONSUME;
......@@ -2596,7 +2612,8 @@ sctp_disposition_t sctp_sf_eat_data_fast_4_4(const sctp_endpoint_t *ep,
* processing the rest of the chunks in the packet.
*/
sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET,SCTP_NULL());
sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_NULL());
sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
SCTP_U32(SCTP_ERROR_NO_DATA));
SCTP_INC_STATS(SctpAborteds);
SCTP_DEC_STATS(SctpCurrEstab);
return SCTP_DISPOSITION_CONSUME;
......@@ -3547,7 +3564,8 @@ sctp_disposition_t sctp_sf_do_9_1_prm_abort(const sctp_endpoint_t *ep,
*/
/* Delete the established association. */
sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_NULL());
sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
SCTP_U32(SCTP_ERROR_USER_ABORT));
SCTP_INC_STATS(SctpAborteds);
SCTP_DEC_STATS(SctpCurrEstab);
......@@ -3686,7 +3704,8 @@ sctp_disposition_t sctp_sf_cookie_wait_prm_abort(const sctp_endpoint_t *ep,
*/
/* Delete the established association. */
sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, SCTP_NULL());
sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED,
SCTP_U32(SCTP_ERROR_USER_ABORT));
return retval;
}
......@@ -4012,7 +4031,8 @@ sctp_disposition_t sctp_sf_do_6_3_3_rtx(const sctp_endpoint_t *ep,
if (asoc->overall_error_count >= asoc->overall_error_threshold) {
/* CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */
sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_NULL());
sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
SCTP_U32(SCTP_ERROR_NO_ERROR));
SCTP_INC_STATS(SctpAborteds);
SCTP_DEC_STATS(SctpCurrEstab);
return SCTP_DISPOSITION_DELETE_TCB;
......@@ -4147,7 +4167,8 @@ sctp_disposition_t sctp_sf_t1_timer_expire(const sctp_endpoint_t *ep,
SCTP_TO(timer));
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl));
} else {
sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, SCTP_NULL());
sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED,
SCTP_U32(SCTP_ERROR_NO_ERROR));
return SCTP_DISPOSITION_DELETE_TCB;
}
......@@ -4181,7 +4202,8 @@ sctp_disposition_t sctp_sf_t2_timer_expire(const sctp_endpoint_t *ep,
SCTP_DEBUG_PRINTK("Timer T2 expired.\n");
if (asoc->overall_error_count >= asoc->overall_error_threshold) {
/* Note: CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */
sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_NULL());
sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
SCTP_U32(SCTP_ERROR_NO_ERROR));
SCTP_INC_STATS(SctpAborteds);
SCTP_DEC_STATS(SctpCurrEstab);
return SCTP_DISPOSITION_DELETE_TCB;
......@@ -4244,7 +4266,8 @@ sctp_disposition_t sctp_sf_t5_timer_expire(const sctp_endpoint_t *ep,
goto nomem;
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply));
sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_NULL());
sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
SCTP_U32(SCTP_ERROR_NO_ERROR));
return SCTP_DISPOSITION_DELETE_TCB;
nomem:
......
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