diff options
author | rrs <rrs@FreeBSD.org> | 2009-03-14 13:42:13 +0000 |
---|---|---|
committer | rrs <rrs@FreeBSD.org> | 2009-03-14 13:42:13 +0000 |
commit | f25b21ac98dbffc3d1acd140518821747cfa758e (patch) | |
tree | 17dfefc898960aceecf005749d75a25787d70736 /sys/netinet/sctputil.c | |
parent | 7cd47702cc3b729fa8002cb2c9c16a4d908c7636 (diff) | |
download | FreeBSD-src-f25b21ac98dbffc3d1acd140518821747cfa758e.zip FreeBSD-src-f25b21ac98dbffc3d1acd140518821747cfa758e.tar.gz |
Fixes several PR-SCTP releated bugs.
- When sending large PR-SCTP messages over a
lossy link we would incorrectly calculate the fwd-tsn
- When receiving large multipart pr-sctp packets we would
incorrectly send back a SACK that would renege improperly
on already received packets thus causing unneeded retransmissions.
Diffstat (limited to 'sys/netinet/sctputil.c')
-rw-r--r-- | sys/netinet/sctputil.c | 205 |
1 files changed, 156 insertions, 49 deletions
diff --git a/sys/netinet/sctputil.c b/sys/netinet/sctputil.c index 1eac98a..42a47ac 100644 --- a/sys/netinet/sctputil.c +++ b/sys/netinet/sctputil.c @@ -3686,10 +3686,10 @@ sctp_report_all_outbound(struct sctp_tcb *stcb, int holds_lock, int so_locked while (chk) { TAILQ_REMOVE(&asoc->sent_queue, chk, sctp_next); asoc->sent_queue_cnt--; - sctp_free_bufspace(stcb, asoc, chk, 1); - sctp_ulp_notify(SCTP_NOTIFY_DG_FAIL, stcb, - SCTP_NOTIFY_DATAGRAM_SENT, chk, so_locked); - if (chk->data) { + if (chk->data != NULL) { + sctp_free_bufspace(stcb, asoc, chk, 1); + sctp_ulp_notify(SCTP_NOTIFY_DG_FAIL, stcb, + SCTP_NOTIFY_DATAGRAM_SENT, chk, so_locked); sctp_m_freem(chk->data); chk->data = NULL; } @@ -3704,9 +3704,10 @@ sctp_report_all_outbound(struct sctp_tcb *stcb, int holds_lock, int so_locked while (chk) { TAILQ_REMOVE(&asoc->send_queue, chk, sctp_next); asoc->send_queue_cnt--; - sctp_free_bufspace(stcb, asoc, chk, 1); - sctp_ulp_notify(SCTP_NOTIFY_DG_FAIL, stcb, SCTP_NOTIFY_DATAGRAM_UNSENT, chk, so_locked); - if (chk->data) { + if (chk->data != NULL) { + sctp_free_bufspace(stcb, asoc, chk, 1); + sctp_ulp_notify(SCTP_NOTIFY_DG_FAIL, stcb, + SCTP_NOTIFY_DATAGRAM_UNSENT, chk, so_locked); sctp_m_freem(chk->data); chk->data = NULL; } @@ -4630,64 +4631,46 @@ sctp_free_bufspace(struct sctp_tcb *stcb, struct sctp_association *asoc, int sctp_release_pr_sctp_chunk(struct sctp_tcb *stcb, struct sctp_tmit_chunk *tp1, - int reason, struct sctpchunk_listhead *queue, int so_locked + int reason, int so_locked #if !defined(__APPLE__) && !defined(SCTP_SO_LOCK_TESTING) SCTP_UNUSED #endif ) { + struct sctp_stream_out *strq; + struct sctp_tmit_chunk *chk = NULL; + struct sctp_stream_queue_pending *sp; + uint16_t stream = 0, seq = 0; + uint8_t foundeom = 0; int ret_sz = 0; int notdone; - uint8_t foundeom = 0; + int do_wakeup_routine = 0; + stream = tp1->rec.data.stream_number; + seq = tp1->rec.data.stream_seq; do { ret_sz += tp1->book_size; tp1->sent = SCTP_FORWARD_TSN_SKIP; - if (tp1->data) { + if (tp1->data != NULL) { #if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) struct socket *so; #endif + printf("Release PR-SCTP chunk tsn:%u flags:%x\n", + tp1->rec.data.TSN_seq, + (unsigned int)tp1->rec.data.rcv_flags); sctp_free_bufspace(stcb, &stcb->asoc, tp1, 1); sctp_flight_size_decrease(tp1); sctp_total_flight_decrease(stcb, tp1); + stcb->asoc.peers_rwnd += tp1->send_size; + stcb->asoc.peers_rwnd += SCTP_BASE_SYSCTL(sctp_peer_chunk_oh); sctp_ulp_notify(SCTP_NOTIFY_DG_FAIL, stcb, reason, tp1, so_locked); sctp_m_freem(tp1->data); tp1->data = NULL; -#if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) - so = SCTP_INP_SO(stcb->sctp_ep); - if (!so_locked) { - atomic_add_int(&stcb->asoc.refcnt, 1); - SCTP_TCB_UNLOCK(stcb); - SCTP_SOCKET_LOCK(so, 1); - SCTP_TCB_LOCK(stcb); - atomic_subtract_int(&stcb->asoc.refcnt, 1); - if (stcb->asoc.state & SCTP_STATE_CLOSED_SOCKET) { - /* - * assoc was freed while we were - * unlocked - */ - SCTP_SOCKET_UNLOCK(so, 1); - return (ret_sz); - } - } -#endif - sctp_sowwakeup(stcb->sctp_ep, stcb->sctp_socket); -#if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) - if (!so_locked) { - SCTP_SOCKET_UNLOCK(so, 1); + do_wakeup_routine = 1; + if (PR_SCTP_BUF_ENABLED(tp1->flags)) { + stcb->asoc.sent_queue_cnt_removeable--; } -#endif - } - if (PR_SCTP_BUF_ENABLED(tp1->flags)) { - stcb->asoc.sent_queue_cnt_removeable--; - } - if (queue == &stcb->asoc.send_queue) { - TAILQ_REMOVE(&stcb->asoc.send_queue, tp1, sctp_next); - /* on to the sent queue */ - TAILQ_INSERT_TAIL(&stcb->asoc.sent_queue, tp1, - sctp_next); - stcb->asoc.sent_queue_cnt++; } if ((tp1->rec.data.rcv_flags & SCTP_DATA_NOT_FRAG) == SCTP_DATA_NOT_FRAG) { @@ -4707,23 +4690,147 @@ sctp_release_pr_sctp_chunk(struct sctp_tcb *stcb, struct sctp_tmit_chunk *tp1, tp1 = TAILQ_NEXT(tp1, sctp_next); } } while (tp1 && notdone); - if ((foundeom == 0) && (queue == &stcb->asoc.sent_queue)) { + if (foundeom == 0) { /* * The multi-part message was scattered across the send and * sent queue. */ +next_on_sent: tp1 = TAILQ_FIRST(&stcb->asoc.send_queue); /* * recurse throught the send_queue too, starting at the * beginning. */ - if (tp1) { - ret_sz += sctp_release_pr_sctp_chunk(stcb, tp1, reason, - &stcb->asoc.send_queue, so_locked); - } else { - SCTP_PRINTF("hmm, nothing on the send queue and no EOM?\n"); + if ((tp1) && + (tp1->rec.data.stream_number == stream) && + (tp1->rec.data.stream_seq == seq) + ) { + /* + * save to chk in case we have some on stream out + * queue. If so and we have an un-transmitted one we + * don't have to fudge the TSN. + */ + chk = tp1; + ret_sz += tp1->book_size; + sctp_ulp_notify(SCTP_NOTIFY_DG_FAIL, stcb, reason, tp1, so_locked); + sctp_free_bufspace(stcb, &stcb->asoc, tp1, 1); + sctp_m_freem(tp1->data); + tp1->data = NULL; + if (tp1->rec.data.rcv_flags & SCTP_DATA_LAST_FRAG) { + foundeom = 1; + } + do_wakeup_routine = 1; + tp1->sent = SCTP_FORWARD_TSN_SKIP; + TAILQ_REMOVE(&stcb->asoc.send_queue, tp1, sctp_next); + /* + * on to the sent queue so we can wait for it to be + * passed by. + */ + TAILQ_INSERT_TAIL(&stcb->asoc.sent_queue, tp1, + sctp_next); + stcb->asoc.send_queue_cnt--; + stcb->asoc.sent_queue_cnt++; + goto next_on_sent; } } + if (foundeom == 0) { + /* + * Still no eom found. That means there is stuff left on the + * stream out queue.. yuck. + */ + strq = &stcb->asoc.strmout[stream]; + SCTP_TCB_SEND_LOCK(stcb); + sp = TAILQ_FIRST(&strq->outqueue); + while (sp->strseq <= seq) { + /* Check if its our SEQ */ + if (sp->strseq == seq) { + sp->discard_rest = 1; + /* + * We may need to put a chunk on the queue + * that holds the TSN that would have been + * sent with the LAST bit. + */ + if (chk == NULL) { + /* Yep, we have to */ + sctp_alloc_a_chunk(stcb, chk); + if (chk == NULL) { + /* + * we are hosed. All we can + * do is nothing.. which + * will cause an abort if + * the peer is paying + * attention. + */ + goto oh_well; + } + memset(chk, 0, sizeof(*chk)); + chk->rec.data.rcv_flags = SCTP_DATA_LAST_FRAG; + chk->sent = SCTP_FORWARD_TSN_SKIP; + chk->asoc = &stcb->asoc; + chk->rec.data.stream_seq = sp->strseq; + chk->rec.data.stream_number = sp->stream; + chk->rec.data.payloadtype = sp->ppid; + chk->rec.data.context = sp->context; + chk->flags = sp->act_flags; + chk->addr_over = sp->addr_over; + chk->whoTo = sp->net; + atomic_add_int(&chk->whoTo->ref_count, 1); + chk->rec.data.TSN_seq = atomic_fetchadd_int(&stcb->asoc.sending_seq, 1); + stcb->asoc.pr_sctp_cnt++; + chk->pr_sctp_on = 1; + TAILQ_INSERT_TAIL(&stcb->asoc.sent_queue, chk, sctp_next); + stcb->asoc.sent_queue_cnt++; + } else { + chk->rec.data.rcv_flags |= SCTP_DATA_LAST_FRAG; + } + oh_well: + if (sp->data) { + /* + * Pull any data to free up the SB + * and allow sender to "add more" + * whilc we will throw away :-) + */ + sctp_free_spbufspace(stcb, &stcb->asoc, + sp); + ret_sz += sp->length; + do_wakeup_routine = 1; + sp->some_taken = 1; + sctp_m_freem(sp->data); + sp->length = 0; + sp->data = NULL; + sp->tail_mbuf = NULL; + } + break; + } else { + /* Next one please */ + sp = TAILQ_NEXT(sp, next); + } + } /* End while */ + SCTP_TCB_SEND_UNLOCK(stcb); + } + if (do_wakeup_routine) { +#if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) + so = SCTP_INP_SO(stcb->sctp_ep); + if (!so_locked) { + atomic_add_int(&stcb->asoc.refcnt, 1); + SCTP_TCB_UNLOCK(stcb); + SCTP_SOCKET_LOCK(so, 1); + SCTP_TCB_LOCK(stcb); + atomic_subtract_int(&stcb->asoc.refcnt, 1); + if (stcb->asoc.state & SCTP_STATE_CLOSED_SOCKET) { + /* assoc was freed while we were unlocked */ + SCTP_SOCKET_UNLOCK(so, 1); + return (ret_sz); + } + } +#endif + sctp_sowwakeup(stcb->sctp_ep, stcb->sctp_socket); +#if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) + if (!so_locked) { + SCTP_SOCKET_UNLOCK(so, 1); + } +#endif + } return (ret_sz); } |