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:
parent
c72ae1423b
commit
8aae94933f
@ -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>
|
||||
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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 *);
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user