summaryrefslogtreecommitdiffstats
path: root/sys/netinet/sctputil.c
diff options
context:
space:
mode:
authorrrs <rrs@FreeBSD.org>2009-03-14 13:42:13 +0000
committerrrs <rrs@FreeBSD.org>2009-03-14 13:42:13 +0000
commitf25b21ac98dbffc3d1acd140518821747cfa758e (patch)
tree17dfefc898960aceecf005749d75a25787d70736 /sys/netinet/sctputil.c
parent7cd47702cc3b729fa8002cb2c9c16a4d908c7636 (diff)
downloadFreeBSD-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.c205
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);
}
OpenPOWER on IntegriCloud