summaryrefslogtreecommitdiffstats
path: root/sys/netinet
diff options
context:
space:
mode:
authorrrs <rrs@FreeBSD.org>2009-02-27 20:54:45 +0000
committerrrs <rrs@FreeBSD.org>2009-02-27 20:54:45 +0000
commit57c49bea03a4e37f49ea548a874cfa7d4cdfa73a (patch)
tree915dcbdc5a8a9a013baf1f350b5bf6e37cabb3df /sys/netinet
parent90499e3f8eb3ad46cecd13f6219e89c28e330a34 (diff)
downloadFreeBSD-src-57c49bea03a4e37f49ea548a874cfa7d4cdfa73a.zip
FreeBSD-src-57c49bea03a4e37f49ea548a874cfa7d4cdfa73a.tar.gz
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
Diffstat (limited to 'sys/netinet')
-rw-r--r--sys/netinet/sctp_crc32.c1
-rw-r--r--sys/netinet/sctp_input.c107
-rw-r--r--sys/netinet/sctp_output.c16
-rw-r--r--sys/netinet/sctp_output.h5
-rw-r--r--sys/netinet/sctp_usrreq.c53
5 files changed, 133 insertions, 49 deletions
diff --git a/sys/netinet/sctp_crc32.c b/sys/netinet/sctp_crc32.c
index b7add8d..92b1387 100644
--- a/sys/netinet/sctp_crc32.c
+++ b/sys/netinet/sctp_crc32.c
@@ -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>
diff --git a/sys/netinet/sctp_input.c b/sys/netinet/sctp_input.c
index a9f3dfc..494929b 100644
--- a/sys/netinet/sctp_input.c
+++ b/sys/netinet/sctp_input.c
@@ -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;
-
- /* 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 */
- 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;
+ 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;
+ }
+ /* 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);
}
- 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);
+
}
}
diff --git a/sys/netinet/sctp_output.c b/sys/netinet/sctp_output.c
index 4ebd0b4..00e178b 100644
--- a/sys/netinet/sctp_output.c
+++ b/sys/netinet/sctp_output.c
@@ -7445,22 +7445,26 @@ outof_here:
}
}
-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;
diff --git a/sys/netinet/sctp_output.h b/sys/netinet/sctp_output.h
index 8a8a064..ce76feb 100644
--- a/sys/netinet/sctp_output.h
+++ b/sys/netinet/sctp_output.h
@@ -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 *);
diff --git a/sys/netinet/sctp_usrreq.c b/sys/netinet/sctp_usrreq.c
index c64026b..b3bfea5 100644
--- a/sys/netinet/sctp_usrreq.c
+++ b/sys/netinet/sctp_usrreq.c
@@ -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);
OpenPOWER on IntegriCloud