diff options
author | George Shepherd <George.Shepherd@Sun.COM> | 2009-10-21 15:05:54 -0700 |
---|---|---|
committer | George Shepherd <George.Shepherd@Sun.COM> | 2009-10-21 15:05:54 -0700 |
commit | f0c3911fac870fd8926c517f55b39ea4489e5a97 (patch) | |
tree | 6eba3333b5910874c40a2528f5852926a7ebcce7 /usr/src | |
parent | bea257dfb5d90b6cfb67fb3bcc5546b0df268593 (diff) | |
download | illumos-gate-f0c3911fac870fd8926c517f55b39ea4489e5a97.tar.gz |
6846343 sctp_icmp_error() drops ICMP_FRAGMENTATION_NEEDED msg from ip, preventing sctp from sending mss.
6598652 Potential SCTP receive dead lock with zero window
Diffstat (limited to 'usr/src')
-rw-r--r-- | usr/src/uts/common/inet/ip/ip.c | 10 | ||||
-rw-r--r-- | usr/src/uts/common/inet/ip/ip6.c | 12 | ||||
-rw-r--r-- | usr/src/uts/common/inet/sctp/sctp.c | 13 | ||||
-rw-r--r-- | usr/src/uts/common/inet/sctp/sctp_impl.h | 1 | ||||
-rw-r--r-- | usr/src/uts/common/inet/sctp/sctp_input.c | 37 |
5 files changed, 51 insertions, 22 deletions
diff --git a/usr/src/uts/common/inet/ip/ip.c b/usr/src/uts/common/inet/ip/ip.c index 910cdfc39c..69382ec91f 100644 --- a/usr/src/uts/common/inet/ip/ip.c +++ b/usr/src/uts/common/inet/ip/ip.c @@ -2203,7 +2203,7 @@ icmp_inbound_too_big(icmph_t *icmph, ipha_t *ipha, ill_t *ill, return (B_FALSE); } /* - * Verify we have atleast ICMP_MIN_TP_HDR_LENGTH bytes of transport + * Verify we have at least ICMP_MIN_TP_HDR_LENGTH bytes of transport * header. */ if ((uchar_t *)ipha + hdr_length + ICMP_MIN_TP_HDR_LEN > @@ -2547,13 +2547,13 @@ icmp_inbound_error_fanout(queue_t *q, ill_t *ill, mblk_t *mp, case IPPROTO_SCTP: /* - * Verify we have at least ICMP_MIN_TP_HDR_LEN bytes of - * transport header. + * Verify we have at least ICMP_MIN_SCTP_HDR_LEN bytes of + * transport header, in the first mp. */ - if ((uchar_t *)ipha + hdr_length + ICMP_MIN_TP_HDR_LEN > + if ((uchar_t *)ipha + hdr_length + ICMP_MIN_SCTP_HDR_LEN > mp->b_wptr) { if (!pullupmsg(mp, (uchar_t *)ipha + hdr_length + - ICMP_MIN_TP_HDR_LEN - mp->b_rptr)) { + ICMP_MIN_SCTP_HDR_LEN - mp->b_rptr)) { goto discard_pkt; } icmph = (icmph_t *)&mp->b_rptr[iph_hdr_length]; diff --git a/usr/src/uts/common/inet/ip/ip6.c b/usr/src/uts/common/inet/ip/ip6.c index a967662399..b9e3e15b97 100644 --- a/usr/src/uts/common/inet/ip/ip6.c +++ b/usr/src/uts/common/inet/ip/ip6.c @@ -79,6 +79,7 @@ #include <inet/tcp.h> #include <inet/tcp_impl.h> #include <inet/udp_impl.h> +#include <inet/sctp/sctp_impl.h> #include <inet/ipp_common.h> #include <inet/ip_multi.h> @@ -860,12 +861,15 @@ icmp_inbound_error_fanout_v6(queue_t *q, mblk_t *mp, ip6_t *ip6h, } case IPPROTO_SCTP: /* - * Verify we have at least ICMP_MIN_TP_HDR_LEN bytes of - * the SCTP header to get the port information. + * Verify we have at least ICMP_MIN_SCTP_HDR_LEN bytes of + * transport header to get the port information. */ - if ((uchar_t *)ip6h + hdr_length + ICMP_MIN_TP_HDR_LEN > + if ((uchar_t *)ip6h + hdr_length + ICMP_MIN_SCTP_HDR_LEN > mp->b_wptr) { - break; + if (!pullupmsg(mp, (uchar_t *)ip6h + hdr_length + + ICMP_MIN_SCTP_HDR_LEN - mp->b_rptr)) { + goto drop_pkt; + } } up = (uint16_t *)((uchar_t *)ip6h + hdr_length); diff --git a/usr/src/uts/common/inet/sctp/sctp.c b/usr/src/uts/common/inet/sctp/sctp.c index 680d6dcb6f..5062b186be 100644 --- a/usr/src/uts/common/inet/sctp/sctp.c +++ b/usr/src/uts/common/inet/sctp/sctp.c @@ -1134,11 +1134,14 @@ sctp_icmp_error(sctp_t *sctp, mblk_t *mp) return; } + /* account for the ip hdr from the icmp message */ iph_hdr_length = IPH_HDR_LENGTH(ipha); icmph = (icmph_t *)&mp->b_rptr[iph_hdr_length]; + /* now the ip hdr of message resulting in this icmp */ ipha = (ipha_t *)&icmph[1]; iph_hdr_length = IPH_HDR_LENGTH(ipha); sctph = (sctp_hdr_t *)((char *)ipha + iph_hdr_length); + /* first_mp must expose the full sctp header. */ if ((uchar_t *)(sctph + 1) >= mp->b_wptr) { /* not enough data for SCTP header */ freemsg(first_mp); @@ -1185,7 +1188,15 @@ sctp_icmp_error(sctp_t *sctp, mblk_t *mp) fp->sfa_pmss = (new_mtu - sctp->sctp_hdr_len) & ~(SCTP_ALIGN - 1); fp->pmtu_discovered = 1; - + /* + * It is possible, even likely that a fast retransmit + * attempt has been dropped by ip as a result of this + * error, retransmission bundles as much as possible. + * A retransmit here prevents significant delays waiting + * on the timer. Analogous to behaviour of TCP after + * ICMP too big. + */ + sctp_rexmit(sctp, fp); break; case ICMP_PORT_UNREACHABLE: case ICMP_PROTOCOL_UNREACHABLE: diff --git a/usr/src/uts/common/inet/sctp/sctp_impl.h b/usr/src/uts/common/inet/sctp/sctp_impl.h index 8b1d62256c..fa4af0b6f3 100644 --- a/usr/src/uts/common/inet/sctp/sctp_impl.h +++ b/usr/src/uts/common/inet/sctp/sctp_impl.h @@ -191,6 +191,7 @@ typedef struct sctpparam_s { #define SCTP_MAX_COMBINED_HEADER_LENGTH (60 + 12) /* Maxed out ip + sctp */ #define SCTP_MAX_IP_OPTIONS_LENGTH (60 - IP_SIMPLE_HDR_LENGTH) #define SCTP_MAX_HDR_LENGTH 60 +#define ICMP_MIN_SCTP_HDR_LEN (ICMP_MIN_TP_HDR_LEN + sizeof (sctp_hdr_t)) #define SCTP_SECRET_LEN 16 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 { /* |