Fix the add stream feature of strm-reset to really work:

- Fix the copy, we can't do a blind copy but must transfer
   the data from the old to the new.
 - Fix the ACK processing so we properly stop retransmitting
   the thing.
 - Fix it so if we get a retran we will properly reply with
   the saved response without doing anything.

MFC after:	1 month
This commit is contained in:
Randall Stewart 2009-02-27 20:54:45 +00:00
parent c72ae1423b
commit 8aae94933f
5 changed files with 132 additions and 48 deletions

View File

@ -40,7 +40,6 @@ __FBSDID("$FreeBSD$");
#include <sys/uio.h>
#include <sys/libkern.h>
#include <netinet/sctp.h>
#include <netinet/sctp_os.h>
#include <netinet/sctp_crc32.h>
#include <netinet/sctp_pcb.h>

View File

@ -3442,6 +3442,8 @@ sctp_handle_stream_reset_response(struct sctp_tcb *stcb,
}
} else if (type == SCTP_STR_RESET_ADD_STREAMS) {
/* Ok we now may have more streams */
if (asoc->stream_reset_outstanding)
asoc->stream_reset_outstanding--;
if (action == SCTP_STREAM_RESET_PERFORMED) {
/* Put the new streams into effect */
stcb->asoc.streamoutcnt = stcb->asoc.strm_realoutsize;
@ -3730,50 +3732,79 @@ sctp_handle_str_reset_add_strm(struct sctp_tcb *stcb, struct sctp_tmit_chunk *ch
*/
uint16_t num_stream, i;
uint32_t seq;
struct sctp_association *asoc = &stcb->asoc;
struct sctp_queued_to_read *ctl;
/* Get the number. */
seq = ntohl(str_add->request_seq);
num_stream = ntohs(str_add->number_of_streams);
/* Now what would be the new total? */
num_stream += stcb->asoc.streamincnt;
if (num_stream > stcb->asoc.max_inbound_streams) {
/* We must reject it they ask for to many */
denied:
sctp_add_stream_reset_result(chk, seq, SCTP_STREAM_RESET_DENIED);
stcb->asoc.last_reset_action[1] = stcb->asoc.last_reset_action[0];
stcb->asoc.last_reset_action[0] = SCTP_STREAM_RESET_DENIED;
} else {
/* Ok, we can do that :-) */
struct sctp_stream_in *oldstrm;
if (asoc->str_reset_seq_in == seq) {
num_stream += stcb->asoc.streamincnt;
if (num_stream > stcb->asoc.max_inbound_streams) {
/* We must reject it they ask for to many */
denied:
sctp_add_stream_reset_result(chk, seq, SCTP_STREAM_RESET_DENIED);
stcb->asoc.last_reset_action[1] = stcb->asoc.last_reset_action[0];
stcb->asoc.last_reset_action[0] = SCTP_STREAM_RESET_DENIED;
} else {
/* Ok, we can do that :-) */
struct sctp_stream_in *oldstrm;
/* save off the old */
oldstrm = stcb->asoc.strmin;
SCTP_MALLOC(stcb->asoc.strmin, struct sctp_stream_in *,
(num_stream * sizeof(struct sctp_stream_in)),
SCTP_M_STRMI);
if (stcb->asoc.strmin == NULL) {
stcb->asoc.strmin = oldstrm;
goto denied;
/* save off the old */
oldstrm = stcb->asoc.strmin;
SCTP_MALLOC(stcb->asoc.strmin, struct sctp_stream_in *,
(num_stream * sizeof(struct sctp_stream_in)),
SCTP_M_STRMI);
if (stcb->asoc.strmin == NULL) {
stcb->asoc.strmin = oldstrm;
goto denied;
}
/* copy off the old data */
for (i = 0; i < stcb->asoc.streamincnt; i++) {
TAILQ_INIT(&stcb->asoc.strmin[i].inqueue);
stcb->asoc.strmin[i].stream_no = i;
stcb->asoc.strmin[i].last_sequence_delivered = oldstrm[i].last_sequence_delivered;
stcb->asoc.strmin[i].delivery_started = oldstrm[i].delivery_started;
/* now anything on those queues? */
while (TAILQ_EMPTY(&oldstrm[i].inqueue) == 0) {
ctl = TAILQ_FIRST(&oldstrm[i].inqueue);
TAILQ_REMOVE(&oldstrm[i].inqueue, ctl, next);
TAILQ_INSERT_TAIL(&stcb->asoc.strmin[i].inqueue, ctl, next);
}
}
/* Init the new streams */
for (i = stcb->asoc.streamincnt; i < num_stream; i++) {
TAILQ_INIT(&stcb->asoc.strmin[i].inqueue);
stcb->asoc.strmin[i].stream_no = i;
stcb->asoc.strmin[i].last_sequence_delivered = 0xffff;
stcb->asoc.strmin[i].delivery_started = 0;
}
SCTP_FREE(oldstrm, SCTP_M_STRMI);
/* update the size */
stcb->asoc.streamincnt = num_stream;
/* Send the ack */
sctp_add_stream_reset_result(chk, seq, SCTP_STREAM_RESET_PERFORMED);
stcb->asoc.last_reset_action[1] = stcb->asoc.last_reset_action[0];
stcb->asoc.last_reset_action[0] = SCTP_STREAM_RESET_PERFORMED;
sctp_ulp_notify(SCTP_NOTIFY_STR_RESET_INSTREAM_ADD_OK, stcb,
(uint32_t) stcb->asoc.streamincnt, NULL, SCTP_SO_NOT_LOCKED);
}
/* copy off the old data */
memcpy(stcb->asoc.strmin, oldstrm,
(stcb->asoc.streamincnt * sizeof(struct sctp_stream_in)));
/* Init the new streams */
for (i = stcb->asoc.streamincnt; i < num_stream; i++) {
TAILQ_INIT(&stcb->asoc.strmin[i].inqueue);
stcb->asoc.strmin[i].stream_no = i;
stcb->asoc.strmin[i].last_sequence_delivered = 0xffff;
stcb->asoc.strmin[i].delivery_started = 0;
}
SCTP_FREE(oldstrm, SCTP_M_STRMI);
/* update the size */
stcb->asoc.streamincnt = num_stream;
/* Send the ack */
sctp_add_stream_reset_result(chk, seq, SCTP_STREAM_RESET_PERFORMED);
stcb->asoc.last_reset_action[1] = stcb->asoc.last_reset_action[0];
stcb->asoc.last_reset_action[0] = SCTP_STREAM_RESET_PERFORMED;
sctp_ulp_notify(SCTP_NOTIFY_STR_RESET_INSTREAM_ADD_OK, stcb,
(uint32_t) stcb->asoc.streamincnt, NULL, SCTP_SO_NOT_LOCKED);
} else if ((asoc->str_reset_seq_in - 1) == seq) {
/*
* one seq back, just echo back last action since my
* response was lost.
*/
sctp_add_stream_reset_result(chk, seq, asoc->last_reset_action[0]);
} else if ((asoc->str_reset_seq_in - 2) == seq) {
/*
* two seq back, just echo back last action since my
* response was lost.
*/
sctp_add_stream_reset_result(chk, seq, asoc->last_reset_action[1]);
} else {
sctp_add_stream_reset_result(chk, seq, SCTP_STREAM_RESET_BAD_SEQNO);
}
}

View File

@ -7445,22 +7445,26 @@ sctp_insert_on_wheel(struct sctp_tcb *stcb,
}
}
static void
void
sctp_remove_from_wheel(struct sctp_tcb *stcb,
struct sctp_association *asoc,
struct sctp_stream_out *strq)
struct sctp_stream_out *strq,
int holds_lock)
{
/* take off and then setup so we know it is not on the wheel */
SCTP_TCB_SEND_LOCK(stcb);
if (holds_lock == 0)
SCTP_TCB_SEND_LOCK(stcb);
if (TAILQ_FIRST(&strq->outqueue)) {
/* more was added */
SCTP_TCB_SEND_UNLOCK(stcb);
if (holds_lock == 0)
SCTP_TCB_SEND_UNLOCK(stcb);
return;
}
TAILQ_REMOVE(&asoc->out_wheel, strq, next_spoke);
strq->next_spoke.tqe_next = NULL;
strq->next_spoke.tqe_prev = NULL;
SCTP_TCB_SEND_UNLOCK(stcb);
if (holds_lock == 0)
SCTP_TCB_SEND_UNLOCK(stcb);
}
static void
@ -9083,7 +9087,7 @@ sctp_fill_outqueue(struct sctp_tcb *stcb,
}
}
}
sctp_remove_from_wheel(stcb, asoc, strq);
sctp_remove_from_wheel(stcb, asoc, strq, 0);
}
if ((giveup) || bail) {
break;

View File

@ -101,6 +101,11 @@ void
sctp_send_heartbeat_ack(struct sctp_tcb *, struct mbuf *, int, int,
struct sctp_nets *);
void
sctp_remove_from_wheel(struct sctp_tcb *stcb,
struct sctp_association *asoc,
struct sctp_stream_out *strq, int holds_lock);
void sctp_send_shutdown(struct sctp_tcb *, struct sctp_nets *);

View File

@ -3326,6 +3326,8 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize,
if ((stcb->asoc.strm_realoutsize - stcb->asoc.streamoutcnt) < addstrmcnt) {
/* Need to allocate more */
struct sctp_stream_out *oldstream;
struct sctp_stream_queue_pending *sp;
int removed;
oldstream = stcb->asoc.strmout;
/* get some more */
@ -3342,20 +3344,63 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize,
* the old out stuff and
* initializing the new stuff.
*/
memcpy(stcb->asoc.strmout, oldstream,
(stcb->asoc.streamoutcnt * sizeof(struct sctp_stream_out)));
SCTP_TCB_SEND_LOCK(stcb);
for (i = 0; i < stcb->asoc.streamoutcnt; i++) {
TAILQ_INIT(&stcb->asoc.strmout[i].outqueue);
stcb->asoc.strmout[i].next_sequence_sent = oldstream[i].next_sequence_sent;
stcb->asoc.strmout[i].last_msg_incomplete = oldstream[i].last_msg_incomplete;
stcb->asoc.strmout[i].stream_no = i;
if (oldstream[i].next_spoke.tqe_next) {
sctp_remove_from_wheel(stcb, &stcb->asoc, &oldstream[i], 1);
stcb->asoc.strmout[i].next_spoke.tqe_next = NULL;
stcb->asoc.strmout[i].next_spoke.tqe_prev = NULL;
removed = 1;
} else {
/* not on out wheel */
stcb->asoc.strmout[i].next_spoke.tqe_next = NULL;
stcb->asoc.strmout[i].next_spoke.tqe_prev = NULL;
removed = 0;
}
/*
* now anything on those
* queues?
*/
while (TAILQ_EMPTY(&oldstream[i].outqueue) == 0) {
sp = TAILQ_FIRST(&oldstream[i].outqueue);
TAILQ_REMOVE(&oldstream[i].outqueue, sp, next);
TAILQ_INSERT_TAIL(&stcb->asoc.strmout[i].outqueue, sp, next);
}
/* Did we disrupt the wheel? */
if (removed) {
sctp_insert_on_wheel(stcb,
&stcb->asoc,
&stcb->asoc.strmout[i],
1);
}
/*
* Now move assoc pointers
* too
*/
if (stcb->asoc.last_out_stream == &oldstream[i]) {
stcb->asoc.last_out_stream = &stcb->asoc.strmout[i];
}
if (stcb->asoc.locked_on_sending == &oldstream[i]) {
stcb->asoc.locked_on_sending = &stcb->asoc.strmout[i];
}
}
/* now the new streams */
for (i = stcb->asoc.streamoutcnt; i < (stcb->asoc.streamoutcnt + addstrmcnt); i++) {
stcb->asoc.strmout[i].next_sequence_sent = 0x0;
TAILQ_INIT(&stcb->asoc.strmout[i].outqueue);
stcb->asoc.strmout[i].stream_no = i;
stcb->asoc.strmout[i].last_msg_incomplete = 0;
stcb->asoc.strmout[i].next_spoke.tqe_next = 0;
stcb->asoc.strmout[i].next_spoke.tqe_prev = 0;
stcb->asoc.strmout[i].next_spoke.tqe_next = NULL;
stcb->asoc.strmout[i].next_spoke.tqe_prev = NULL;
}
stcb->asoc.strm_realoutsize = stcb->asoc.streamoutcnt + addstrmcnt;
SCTP_FREE(oldstream, SCTP_M_STRMO);
}
SCTP_TCB_SEND_UNLOCK(stcb);
goto skip_stuff;
} else {
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);