diff options
author | rrs <rrs@FreeBSD.org> | 2009-02-27 20:54:45 +0000 |
---|---|---|
committer | rrs <rrs@FreeBSD.org> | 2009-02-27 20:54:45 +0000 |
commit | 57c49bea03a4e37f49ea548a874cfa7d4cdfa73a (patch) | |
tree | 915dcbdc5a8a9a013baf1f350b5bf6e37cabb3df /sys/netinet | |
parent | 90499e3f8eb3ad46cecd13f6219e89c28e330a34 (diff) | |
download | FreeBSD-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.c | 1 | ||||
-rw-r--r-- | sys/netinet/sctp_input.c | 107 | ||||
-rw-r--r-- | sys/netinet/sctp_output.c | 16 | ||||
-rw-r--r-- | sys/netinet/sctp_output.h | 5 | ||||
-rw-r--r-- | sys/netinet/sctp_usrreq.c | 53 |
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); |