summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/inet/sctp/sctp_input.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/inet/sctp/sctp_input.c')
-rw-r--r--usr/src/uts/common/inet/sctp/sctp_input.c37
1 files changed, 25 insertions, 12 deletions
diff --git a/usr/src/uts/common/inet/sctp/sctp_input.c b/usr/src/uts/common/inet/sctp/sctp_input.c
index 431c7ccc08..e18bfeacdd 100644
--- a/usr/src/uts/common/inet/sctp/sctp_input.c
+++ b/usr/src/uts/common/inet/sctp/sctp_input.c
@@ -1248,8 +1248,14 @@ sctp_data_chunk(sctp_t *sctp, sctp_chunk_hdr_t *ch, mblk_t *mp, mblk_t **dups,
dlen = ntohs(dc->sdh_len) - sizeof (*dc);
- /* Check for buffer space */
- if (sctp->sctp_rwnd - sctp->sctp_rxqueued < dlen) {
+ /*
+ * Check for buffer space. Note if this is the next expected TSN
+ * we have to take it to avoid deadlock because we cannot deliver
+ * later queued TSNs and thus clear buffer space without it.
+ * We drop anything that is purely zero window probe data here.
+ */
+ if ((sctp->sctp_rwnd - sctp->sctp_rxqueued < dlen) &&
+ (tsn != sctp->sctp_ftsn || sctp->sctp_rwnd == 0)) {
/* Drop and SACK, but don't advance the cumulative TSN. */
sctp->sctp_force_sack = 1;
dprint(0, ("sctp_data_chunk: exceed rwnd %d rxqueued %d "
@@ -1404,9 +1410,8 @@ sctp_data_chunk(sctp_t *sctp, sctp_chunk_hdr_t *ch, mblk_t *mp, mblk_t **dups,
*/
dlen = dmp->b_wptr - (uchar_t *)dc - sizeof (*dc);
for (pmp = dmp->b_cont; pmp != NULL; pmp = pmp->b_cont)
- dlen += pmp->b_wptr - pmp->b_rptr;
+ dlen += MBLKL(pmp);
ASSERT(sctp->sctp_rxqueued >= dlen);
- ASSERT(sctp->sctp_rwnd >= dlen);
/* Deliver the message. */
sctp->sctp_rxqueued -= dlen;
@@ -1424,9 +1429,15 @@ sctp_data_chunk(sctp_t *sctp, sctp_chunk_hdr_t *ch, mblk_t *mp, mblk_t **dups,
dmp->b_flag = tpfinished ? 0 : SCTP_PARTIAL_DATA;
new_rwnd = sctp->sctp_ulp_recv(sctp->sctp_ulpd, dmp,
msgdsize(dmp), 0, &error, NULL);
- if (new_rwnd > sctp->sctp_rwnd) {
+ /*
+ * Since we always deliver the next TSN data chunk,
+ * we may buffer a little more than allowed. In
+ * that case, just mark the window as 0.
+ */
+ if (new_rwnd < 0)
+ sctp->sctp_rwnd = 0;
+ else if (new_rwnd > sctp->sctp_rwnd)
sctp->sctp_rwnd = new_rwnd;
- }
SCTP_ACK_IT(sctp, tsn);
} else {
/* Just free the message if we don't have memory. */
@@ -1488,10 +1499,9 @@ sctp_data_chunk(sctp_t *sctp, sctp_chunk_hdr_t *ch, mblk_t *mp, mblk_t **dups,
*/
dlen = dmp->b_wptr - dmp->b_rptr - sizeof (*dc);
for (pmp = dmp->b_cont; pmp; pmp = pmp->b_cont)
- dlen += pmp->b_wptr - pmp->b_rptr;
+ dlen += MBLKL(pmp);
ASSERT(sctp->sctp_rxqueued >= dlen);
- ASSERT(sctp->sctp_rwnd >= dlen);
sctp->sctp_rxqueued -= dlen;
if (can_deliver) {
@@ -1508,9 +1518,10 @@ sctp_data_chunk(sctp_t *sctp, sctp_chunk_hdr_t *ch, mblk_t *mp, mblk_t **dups,
0 : SCTP_PARTIAL_DATA;
new_rwnd = sctp->sctp_ulp_recv(sctp->sctp_ulpd,
dmp, msgdsize(dmp), 0, &error, NULL);
- if (new_rwnd > sctp->sctp_rwnd) {
+ if (new_rwnd < 0)
+ sctp->sctp_rwnd = 0;
+ else if (new_rwnd > sctp->sctp_rwnd)
sctp->sctp_rwnd = new_rwnd;
- }
SCTP_ACK_IT(sctp, tsn);
} else {
freemsg(dmp);
@@ -2150,7 +2161,7 @@ sctp_process_forward_tsn(sctp_t *sctp, sctp_chunk_hdr_t *ch, sctp_faddr_t *fp,
dlen = dmp->b_wptr - dmp->b_rptr - sizeof (*dc);
for (pmp = dmp->b_cont; pmp != NULL;
pmp = pmp->b_cont) {
- dlen += pmp->b_wptr - pmp->b_rptr;
+ dlen += MBLKL(pmp);
}
if (can_deliver) {
int32_t nrwnd;
@@ -2172,7 +2183,9 @@ sctp_process_forward_tsn(sctp_t *sctp, sctp_chunk_hdr_t *ch, sctp_faddr_t *fp,
nrwnd = sctp->sctp_ulp_recv(
sctp->sctp_ulpd, dmp, msgdsize(dmp),
0, &error, NULL);
- if (nrwnd > sctp->sctp_rwnd)
+ if (nrwnd < 0)
+ sctp->sctp_rwnd = 0;
+ else if (nrwnd > sctp->sctp_rwnd)
sctp->sctp_rwnd = nrwnd;
} else {
/*