xref: /titanic_51/usr/src/uts/common/inet/sctp/sctp_input.c (revision a215d4eb400e2ff52f7a17e0781964c37aabfc04)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
545916cd2Sjpk  * Common Development and Distribution License (the "License").
645916cd2Sjpk  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
2177c67f2fSkcpoon 
227c478bd9Sstevel@tonic-gate /*
23c3c17166SGeorge Shepherd  * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #include <sys/types.h>
277c478bd9Sstevel@tonic-gate #include <sys/systm.h>
287c478bd9Sstevel@tonic-gate #include <sys/stream.h>
297c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
307c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
317c478bd9Sstevel@tonic-gate #define	_SUN_TPI_VERSION 2
327c478bd9Sstevel@tonic-gate #include <sys/tihdr.h>
337c478bd9Sstevel@tonic-gate #include <sys/socket.h>
347c478bd9Sstevel@tonic-gate #include <sys/strsun.h>
357c478bd9Sstevel@tonic-gate #include <sys/strsubr.h>
367c478bd9Sstevel@tonic-gate 
377c478bd9Sstevel@tonic-gate #include <netinet/in.h>
387c478bd9Sstevel@tonic-gate #include <netinet/ip6.h>
397c478bd9Sstevel@tonic-gate #include <netinet/tcp_seq.h>
407c478bd9Sstevel@tonic-gate #include <netinet/sctp.h>
417c478bd9Sstevel@tonic-gate 
427c478bd9Sstevel@tonic-gate #include <inet/common.h>
437c478bd9Sstevel@tonic-gate #include <inet/ip.h>
44bd670b35SErik Nordmark #include <inet/ip_if.h>
457c478bd9Sstevel@tonic-gate #include <inet/ip6.h>
467c478bd9Sstevel@tonic-gate #include <inet/mib2.h>
477c478bd9Sstevel@tonic-gate #include <inet/ipclassifier.h>
487c478bd9Sstevel@tonic-gate #include <inet/ipp_common.h>
497c478bd9Sstevel@tonic-gate #include <inet/ipsec_impl.h>
507c478bd9Sstevel@tonic-gate #include <inet/sctp_ip.h>
517c478bd9Sstevel@tonic-gate 
527c478bd9Sstevel@tonic-gate #include "sctp_impl.h"
537c478bd9Sstevel@tonic-gate #include "sctp_asconf.h"
547c478bd9Sstevel@tonic-gate #include "sctp_addr.h"
557c478bd9Sstevel@tonic-gate 
567c478bd9Sstevel@tonic-gate static struct kmem_cache *sctp_kmem_set_cache;
577c478bd9Sstevel@tonic-gate 
587c478bd9Sstevel@tonic-gate /*
597c478bd9Sstevel@tonic-gate  * PR-SCTP comments.
607c478bd9Sstevel@tonic-gate  *
617c478bd9Sstevel@tonic-gate  * When we get a valid Forward TSN chunk, we check the fragment list for this
627c478bd9Sstevel@tonic-gate  * SSN and preceeding SSNs free all them. Further, if this Forward TSN causes
637c478bd9Sstevel@tonic-gate  * the next expected SSN to be present in the stream queue, we deliver any
647c478bd9Sstevel@tonic-gate  * such stranded messages upstream. We also update the SACK info. appropriately.
657c478bd9Sstevel@tonic-gate  * When checking for advancing the cumulative ack (in sctp_cumack()) we must
667c478bd9Sstevel@tonic-gate  * check for abandoned chunks and messages. While traversing the tramsmit
677c478bd9Sstevel@tonic-gate  * list if we come across an abandoned chunk, we can skip the message (i.e.
687c478bd9Sstevel@tonic-gate  * take it out of the (re)transmit list) since this message, and hence this
697c478bd9Sstevel@tonic-gate  * chunk, has been marked abandoned by sctp_rexmit(). If we come across an
707c478bd9Sstevel@tonic-gate  * unsent chunk for a message this now abandoned we need to check if a
717c478bd9Sstevel@tonic-gate  * Forward TSN needs to be sent, this could be a case where we deferred sending
727c478bd9Sstevel@tonic-gate  * a Forward TSN in sctp_get_msg_to_send(). Further, after processing a
737c478bd9Sstevel@tonic-gate  * SACK we check if the Advanced peer ack point can be moved ahead, i.e.
747c478bd9Sstevel@tonic-gate  * if we can send a Forward TSN via sctp_check_abandoned_data().
757c478bd9Sstevel@tonic-gate  */
767c478bd9Sstevel@tonic-gate void
777c478bd9Sstevel@tonic-gate sctp_free_set(sctp_set_t *s)
787c478bd9Sstevel@tonic-gate {
797c478bd9Sstevel@tonic-gate 	sctp_set_t *p;
807c478bd9Sstevel@tonic-gate 
817c478bd9Sstevel@tonic-gate 	while (s) {
827c478bd9Sstevel@tonic-gate 		p = s->next;
837c478bd9Sstevel@tonic-gate 		kmem_cache_free(sctp_kmem_set_cache, s);
847c478bd9Sstevel@tonic-gate 		s = p;
857c478bd9Sstevel@tonic-gate 	}
867c478bd9Sstevel@tonic-gate }
877c478bd9Sstevel@tonic-gate 
887c478bd9Sstevel@tonic-gate static void
897c478bd9Sstevel@tonic-gate sctp_ack_add(sctp_set_t **head, uint32_t tsn, int *num)
907c478bd9Sstevel@tonic-gate {
917c478bd9Sstevel@tonic-gate 	sctp_set_t *p, *t;
927c478bd9Sstevel@tonic-gate 
937c478bd9Sstevel@tonic-gate 	if (head == NULL || num == NULL)
947c478bd9Sstevel@tonic-gate 		return;
957c478bd9Sstevel@tonic-gate 
967c478bd9Sstevel@tonic-gate 	ASSERT(*num >= 0);
977c478bd9Sstevel@tonic-gate 	ASSERT((*num == 0 && *head == NULL) || (*num > 0 && *head != NULL));
987c478bd9Sstevel@tonic-gate 
997c478bd9Sstevel@tonic-gate 	if (*head == NULL) {
1007c478bd9Sstevel@tonic-gate 		*head = kmem_cache_alloc(sctp_kmem_set_cache, KM_NOSLEEP);
1017c478bd9Sstevel@tonic-gate 		if (*head == NULL)
1027c478bd9Sstevel@tonic-gate 			return;
1037c478bd9Sstevel@tonic-gate 		(*head)->prev = (*head)->next = NULL;
1047c478bd9Sstevel@tonic-gate 		(*head)->begin = tsn;
1057c478bd9Sstevel@tonic-gate 		(*head)->end = tsn;
1067c478bd9Sstevel@tonic-gate 		*num = 1;
1077c478bd9Sstevel@tonic-gate 		return;
1087c478bd9Sstevel@tonic-gate 	}
1097c478bd9Sstevel@tonic-gate 
1107c478bd9Sstevel@tonic-gate 	ASSERT((*head)->prev == NULL);
1117c478bd9Sstevel@tonic-gate 
1127c478bd9Sstevel@tonic-gate 	/*
1137c478bd9Sstevel@tonic-gate 	 * Handle this special case here so we don't have to check
1147c478bd9Sstevel@tonic-gate 	 * for it each time in the loop.
1157c478bd9Sstevel@tonic-gate 	 */
1167c478bd9Sstevel@tonic-gate 	if (SEQ_LT(tsn + 1, (*head)->begin)) {
1177c478bd9Sstevel@tonic-gate 		/* add a new set, and move the head pointer */
1187c478bd9Sstevel@tonic-gate 		t = kmem_cache_alloc(sctp_kmem_set_cache, KM_NOSLEEP);
1197c478bd9Sstevel@tonic-gate 		if (t == NULL)
1207c478bd9Sstevel@tonic-gate 			return;
1217c478bd9Sstevel@tonic-gate 		t->next = *head;
1227c478bd9Sstevel@tonic-gate 		t->prev = NULL;
1237c478bd9Sstevel@tonic-gate 		(*head)->prev = t;
1247c478bd9Sstevel@tonic-gate 		t->begin = tsn;
1257c478bd9Sstevel@tonic-gate 		t->end = tsn;
1267c478bd9Sstevel@tonic-gate 		(*num)++;
1277c478bd9Sstevel@tonic-gate 		*head = t;
1287c478bd9Sstevel@tonic-gate 		return;
1297c478bd9Sstevel@tonic-gate 	}
1307c478bd9Sstevel@tonic-gate 
1317c478bd9Sstevel@tonic-gate 	/*
1327c478bd9Sstevel@tonic-gate 	 * We need to handle the following cases, where p points to
1337c478bd9Sstevel@tonic-gate 	 * the current set (as we walk through the loop):
1347c478bd9Sstevel@tonic-gate 	 *
1357c478bd9Sstevel@tonic-gate 	 * 1. tsn is entirely less than p; create a new set before p.
1367c478bd9Sstevel@tonic-gate 	 * 2. tsn borders p from less; coalesce p with tsn.
1377c478bd9Sstevel@tonic-gate 	 * 3. tsn is withing p; do nothing.
1387c478bd9Sstevel@tonic-gate 	 * 4. tsn borders p from greater; coalesce p with tsn.
1397c478bd9Sstevel@tonic-gate 	 * 4a. p may now border p->next from less; if so, coalesce those
1407c478bd9Sstevel@tonic-gate 	 *    two sets.
1417c478bd9Sstevel@tonic-gate 	 * 5. tsn is entirely greater then all sets; add a new set at
1427c478bd9Sstevel@tonic-gate 	 *    the end.
1437c478bd9Sstevel@tonic-gate 	 */
1447c478bd9Sstevel@tonic-gate 	for (p = *head; ; p = p->next) {
1457c478bd9Sstevel@tonic-gate 		if (SEQ_LT(tsn + 1, p->begin)) {
1467c478bd9Sstevel@tonic-gate 			/* 1: add a new set before p. */
1477c478bd9Sstevel@tonic-gate 			t = kmem_cache_alloc(sctp_kmem_set_cache, KM_NOSLEEP);
1487c478bd9Sstevel@tonic-gate 			if (t == NULL)
1497c478bd9Sstevel@tonic-gate 				return;
1507c478bd9Sstevel@tonic-gate 			t->next = p;
1517c478bd9Sstevel@tonic-gate 			t->prev = NULL;
1527c478bd9Sstevel@tonic-gate 			t->begin = tsn;
1537c478bd9Sstevel@tonic-gate 			t->end = tsn;
1547c478bd9Sstevel@tonic-gate 			if (p->prev) {
1557c478bd9Sstevel@tonic-gate 				t->prev = p->prev;
1567c478bd9Sstevel@tonic-gate 				p->prev->next = t;
1577c478bd9Sstevel@tonic-gate 			}
1587c478bd9Sstevel@tonic-gate 			p->prev = t;
1597c478bd9Sstevel@tonic-gate 			(*num)++;
1607c478bd9Sstevel@tonic-gate 			return;
1617c478bd9Sstevel@tonic-gate 		}
1627c478bd9Sstevel@tonic-gate 
1637c478bd9Sstevel@tonic-gate 		if ((tsn + 1) == p->begin) {
1647c478bd9Sstevel@tonic-gate 			/* 2: adjust p->begin */
1657c478bd9Sstevel@tonic-gate 			p->begin = tsn;
1667c478bd9Sstevel@tonic-gate 			return;
1677c478bd9Sstevel@tonic-gate 		}
1687c478bd9Sstevel@tonic-gate 
1697c478bd9Sstevel@tonic-gate 		if (SEQ_GEQ(tsn, p->begin) && SEQ_LEQ(tsn, p->end)) {
1707c478bd9Sstevel@tonic-gate 			/* 3; do nothing */
1717c478bd9Sstevel@tonic-gate 			return;
1727c478bd9Sstevel@tonic-gate 		}
1737c478bd9Sstevel@tonic-gate 
1747c478bd9Sstevel@tonic-gate 		if ((p->end + 1) == tsn) {
1757c478bd9Sstevel@tonic-gate 			/* 4; adjust p->end */
1767c478bd9Sstevel@tonic-gate 			p->end = tsn;
1777c478bd9Sstevel@tonic-gate 
1787c478bd9Sstevel@tonic-gate 			if (p->next != NULL && (tsn + 1) == p->next->begin) {
1797c478bd9Sstevel@tonic-gate 				/* 4a: coalesce p and p->next */
1807c478bd9Sstevel@tonic-gate 				t = p->next;
1817c478bd9Sstevel@tonic-gate 				p->end = t->end;
1827c478bd9Sstevel@tonic-gate 				p->next = t->next;
1837c478bd9Sstevel@tonic-gate 				if (t->next != NULL)
1847c478bd9Sstevel@tonic-gate 					t->next->prev = p;
1857c478bd9Sstevel@tonic-gate 				kmem_cache_free(sctp_kmem_set_cache, t);
1867c478bd9Sstevel@tonic-gate 				(*num)--;
1877c478bd9Sstevel@tonic-gate 			}
1887c478bd9Sstevel@tonic-gate 			return;
1897c478bd9Sstevel@tonic-gate 		}
1907c478bd9Sstevel@tonic-gate 
1917c478bd9Sstevel@tonic-gate 		if (p->next == NULL) {
1927c478bd9Sstevel@tonic-gate 			/* 5: add new set at the end */
1937c478bd9Sstevel@tonic-gate 			t = kmem_cache_alloc(sctp_kmem_set_cache, KM_NOSLEEP);
1947c478bd9Sstevel@tonic-gate 			if (t == NULL)
1957c478bd9Sstevel@tonic-gate 				return;
1967c478bd9Sstevel@tonic-gate 			t->next = NULL;
1977c478bd9Sstevel@tonic-gate 			t->prev = p;
1987c478bd9Sstevel@tonic-gate 			t->begin = tsn;
1997c478bd9Sstevel@tonic-gate 			t->end = tsn;
2007c478bd9Sstevel@tonic-gate 			p->next = t;
2017c478bd9Sstevel@tonic-gate 			(*num)++;
2027c478bd9Sstevel@tonic-gate 			return;
2037c478bd9Sstevel@tonic-gate 		}
2047c478bd9Sstevel@tonic-gate 
2057c478bd9Sstevel@tonic-gate 		if (SEQ_GT(tsn, p->end + 1))
2067c478bd9Sstevel@tonic-gate 			continue;
2077c478bd9Sstevel@tonic-gate 	}
2087c478bd9Sstevel@tonic-gate }
2097c478bd9Sstevel@tonic-gate 
2107c478bd9Sstevel@tonic-gate static void
2117c478bd9Sstevel@tonic-gate sctp_ack_rem(sctp_set_t **head, uint32_t end, int *num)
2127c478bd9Sstevel@tonic-gate {
2137c478bd9Sstevel@tonic-gate 	sctp_set_t *p, *t;
2147c478bd9Sstevel@tonic-gate 
2157c478bd9Sstevel@tonic-gate 	if (head == NULL || *head == NULL || num == NULL)
2167c478bd9Sstevel@tonic-gate 		return;
2177c478bd9Sstevel@tonic-gate 
2187c478bd9Sstevel@tonic-gate 	/* Nothing to remove */
2197c478bd9Sstevel@tonic-gate 	if (SEQ_LT(end, (*head)->begin))
2207c478bd9Sstevel@tonic-gate 		return;
2217c478bd9Sstevel@tonic-gate 
2227c478bd9Sstevel@tonic-gate 	/* Find out where to start removing sets */
2237c478bd9Sstevel@tonic-gate 	for (p = *head; p->next; p = p->next) {
2247c478bd9Sstevel@tonic-gate 		if (SEQ_LEQ(end, p->end))
2257c478bd9Sstevel@tonic-gate 			break;
2267c478bd9Sstevel@tonic-gate 	}
2277c478bd9Sstevel@tonic-gate 
2287c478bd9Sstevel@tonic-gate 	if (SEQ_LT(end, p->end) && SEQ_GEQ(end, p->begin)) {
2297c478bd9Sstevel@tonic-gate 		/* adjust p */
2307c478bd9Sstevel@tonic-gate 		p->begin = end + 1;
2317c478bd9Sstevel@tonic-gate 		/* all done */
2327c478bd9Sstevel@tonic-gate 		if (p == *head)
2337c478bd9Sstevel@tonic-gate 			return;
2347c478bd9Sstevel@tonic-gate 	} else if (SEQ_GEQ(end, p->end)) {
2357c478bd9Sstevel@tonic-gate 		/* remove this set too */
2367c478bd9Sstevel@tonic-gate 		p = p->next;
2377c478bd9Sstevel@tonic-gate 	}
2387c478bd9Sstevel@tonic-gate 
2397c478bd9Sstevel@tonic-gate 	/* unlink everything before this set */
2407c478bd9Sstevel@tonic-gate 	t = *head;
2417c478bd9Sstevel@tonic-gate 	*head = p;
2427c478bd9Sstevel@tonic-gate 	if (p != NULL && p->prev != NULL) {
2437c478bd9Sstevel@tonic-gate 		p->prev->next = NULL;
2447c478bd9Sstevel@tonic-gate 		p->prev = NULL;
2457c478bd9Sstevel@tonic-gate 	}
2467c478bd9Sstevel@tonic-gate 
2477c478bd9Sstevel@tonic-gate 	sctp_free_set(t);
2487c478bd9Sstevel@tonic-gate 
2497c478bd9Sstevel@tonic-gate 	/* recount the number of sets */
2507c478bd9Sstevel@tonic-gate 	*num = 0;
2517c478bd9Sstevel@tonic-gate 
2527c478bd9Sstevel@tonic-gate 	for (p = *head; p != NULL; p = p->next)
2537c478bd9Sstevel@tonic-gate 		(*num)++;
2547c478bd9Sstevel@tonic-gate }
2557c478bd9Sstevel@tonic-gate 
2567c478bd9Sstevel@tonic-gate void
2577c478bd9Sstevel@tonic-gate sctp_sets_init()
2587c478bd9Sstevel@tonic-gate {
2597c478bd9Sstevel@tonic-gate 	sctp_kmem_set_cache = kmem_cache_create("sctp_set_cache",
2607c478bd9Sstevel@tonic-gate 	    sizeof (sctp_set_t), 0, NULL, NULL, NULL, NULL,
2617c478bd9Sstevel@tonic-gate 	    NULL, 0);
2627c478bd9Sstevel@tonic-gate }
2637c478bd9Sstevel@tonic-gate 
2647c478bd9Sstevel@tonic-gate void
2657c478bd9Sstevel@tonic-gate sctp_sets_fini()
2667c478bd9Sstevel@tonic-gate {
2677c478bd9Sstevel@tonic-gate 	kmem_cache_destroy(sctp_kmem_set_cache);
2687c478bd9Sstevel@tonic-gate }
2697c478bd9Sstevel@tonic-gate 
2707c478bd9Sstevel@tonic-gate sctp_chunk_hdr_t *
2717c478bd9Sstevel@tonic-gate sctp_first_chunk(uchar_t *rptr, ssize_t remaining)
2727c478bd9Sstevel@tonic-gate {
2737c478bd9Sstevel@tonic-gate 	sctp_chunk_hdr_t *ch;
2747c478bd9Sstevel@tonic-gate 	uint16_t ch_len;
2757c478bd9Sstevel@tonic-gate 
2767c478bd9Sstevel@tonic-gate 	if (remaining < sizeof (*ch)) {
2777c478bd9Sstevel@tonic-gate 		return (NULL);
2787c478bd9Sstevel@tonic-gate 	}
2797c478bd9Sstevel@tonic-gate 
2807c478bd9Sstevel@tonic-gate 	ch = (sctp_chunk_hdr_t *)rptr;
2817c478bd9Sstevel@tonic-gate 	ch_len = ntohs(ch->sch_len);
2827c478bd9Sstevel@tonic-gate 
2837c478bd9Sstevel@tonic-gate 	if (ch_len < sizeof (*ch) || remaining < ch_len) {
2847c478bd9Sstevel@tonic-gate 		return (NULL);
2857c478bd9Sstevel@tonic-gate 	}
2867c478bd9Sstevel@tonic-gate 
2877c478bd9Sstevel@tonic-gate 	return (ch);
2887c478bd9Sstevel@tonic-gate }
2897c478bd9Sstevel@tonic-gate 
2907c478bd9Sstevel@tonic-gate sctp_chunk_hdr_t *
2917c478bd9Sstevel@tonic-gate sctp_next_chunk(sctp_chunk_hdr_t *ch, ssize_t *remaining)
2927c478bd9Sstevel@tonic-gate {
2937c478bd9Sstevel@tonic-gate 	int pad;
2947c478bd9Sstevel@tonic-gate 	uint16_t ch_len;
2957c478bd9Sstevel@tonic-gate 
2967c478bd9Sstevel@tonic-gate 	if (!ch) {
2977c478bd9Sstevel@tonic-gate 		return (NULL);
2987c478bd9Sstevel@tonic-gate 	}
2997c478bd9Sstevel@tonic-gate 
3007c478bd9Sstevel@tonic-gate 	ch_len = ntohs(ch->sch_len);
3017c478bd9Sstevel@tonic-gate 
3027c478bd9Sstevel@tonic-gate 	if ((pad = ch_len & (SCTP_ALIGN - 1)) != 0) {
3037c478bd9Sstevel@tonic-gate 		pad = SCTP_ALIGN - pad;
3047c478bd9Sstevel@tonic-gate 	}
3057c478bd9Sstevel@tonic-gate 
3067c478bd9Sstevel@tonic-gate 	*remaining -= (ch_len + pad);
3077c478bd9Sstevel@tonic-gate 	ch = (sctp_chunk_hdr_t *)((char *)ch + ch_len + pad);
3087c478bd9Sstevel@tonic-gate 
3097c478bd9Sstevel@tonic-gate 	return (sctp_first_chunk((uchar_t *)ch, *remaining));
3107c478bd9Sstevel@tonic-gate }
3117c478bd9Sstevel@tonic-gate 
3127c478bd9Sstevel@tonic-gate /*
3137c478bd9Sstevel@tonic-gate  * Attach ancillary data to a received SCTP segments.
3147c478bd9Sstevel@tonic-gate  * If the source address (fp) is not the primary, send up a
3157c478bd9Sstevel@tonic-gate  * unitdata_ind so recvfrom() can populate the msg_name field.
3167c478bd9Sstevel@tonic-gate  * If ancillary data is also requested, we append it to the
3177c478bd9Sstevel@tonic-gate  * unitdata_req. Otherwise, we just send up an optdata_ind.
3187c478bd9Sstevel@tonic-gate  */
3197c478bd9Sstevel@tonic-gate static int
3207c478bd9Sstevel@tonic-gate sctp_input_add_ancillary(sctp_t *sctp, mblk_t **mp, sctp_data_hdr_t *dcp,
321bd670b35SErik Nordmark     sctp_faddr_t *fp, ip_pkt_t *ipp, ip_recv_attr_t *ira)
3227c478bd9Sstevel@tonic-gate {
3237c478bd9Sstevel@tonic-gate 	struct T_unitdata_ind	*tudi;
3247c478bd9Sstevel@tonic-gate 	int			optlen;
3257c478bd9Sstevel@tonic-gate 	int			hdrlen;
3267c478bd9Sstevel@tonic-gate 	uchar_t			*optptr;
3277c478bd9Sstevel@tonic-gate 	struct cmsghdr		*cmsg;
3287c478bd9Sstevel@tonic-gate 	mblk_t			*mp1;
3297c478bd9Sstevel@tonic-gate 	struct sockaddr_in6	sin_buf[1];
3307c478bd9Sstevel@tonic-gate 	struct sockaddr_in6	*sin6;
3317c478bd9Sstevel@tonic-gate 	struct sockaddr_in	*sin4;
332bd670b35SErik Nordmark 	crb_t			 addflag;	/* Which pieces to add */
333bd670b35SErik Nordmark 	conn_t			*connp = sctp->sctp_connp;
3347c478bd9Sstevel@tonic-gate 
3357c478bd9Sstevel@tonic-gate 	sin4 = NULL;
3367c478bd9Sstevel@tonic-gate 	sin6 = NULL;
3377c478bd9Sstevel@tonic-gate 
3387c478bd9Sstevel@tonic-gate 	optlen = hdrlen = 0;
339bd670b35SErik Nordmark 	addflag.crb_all = 0;
3407c478bd9Sstevel@tonic-gate 
3417c478bd9Sstevel@tonic-gate 	/* Figure out address size */
342bd670b35SErik Nordmark 	if (connp->conn_family == AF_INET) {
3437c478bd9Sstevel@tonic-gate 		sin4 = (struct sockaddr_in *)sin_buf;
3447c478bd9Sstevel@tonic-gate 		sin4->sin_family = AF_INET;
345bd670b35SErik Nordmark 		sin4->sin_port = connp->conn_fport;
3466be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		IN6_V4MAPPED_TO_IPADDR(&fp->sf_faddr, sin4->sin_addr.s_addr);
3477c478bd9Sstevel@tonic-gate 		hdrlen = sizeof (*tudi) + sizeof (*sin4);
3487c478bd9Sstevel@tonic-gate 	} else {
3497c478bd9Sstevel@tonic-gate 		sin6 = sin_buf;
3507c478bd9Sstevel@tonic-gate 		sin6->sin6_family = AF_INET6;
351bd670b35SErik Nordmark 		sin6->sin6_port = connp->conn_fport;
3526be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		sin6->sin6_addr = fp->sf_faddr;
3537c478bd9Sstevel@tonic-gate 		hdrlen = sizeof (*tudi) + sizeof (*sin6);
3547c478bd9Sstevel@tonic-gate 	}
3557c478bd9Sstevel@tonic-gate 	/* If app asked to receive send / recv info */
356bd670b35SErik Nordmark 	if (sctp->sctp_recvsndrcvinfo)
3577c478bd9Sstevel@tonic-gate 		optlen += sizeof (*cmsg) + sizeof (struct sctp_sndrcvinfo);
3587c478bd9Sstevel@tonic-gate 
359bd670b35SErik Nordmark 	if (connp->conn_recv_ancillary.crb_all == 0)
3607c478bd9Sstevel@tonic-gate 		goto noancillary;
3617c478bd9Sstevel@tonic-gate 
362bd670b35SErik Nordmark 	if (connp->conn_recv_ancillary.crb_ip_recvpktinfo &&
363bd670b35SErik Nordmark 	    ira->ira_ruifindex != sctp->sctp_recvifindex) {
3647c478bd9Sstevel@tonic-gate 		optlen += sizeof (*cmsg) + sizeof (struct in6_pktinfo);
3657c478bd9Sstevel@tonic-gate 		if (hdrlen == 0)
3667c478bd9Sstevel@tonic-gate 			hdrlen = sizeof (struct T_unitdata_ind);
367bd670b35SErik Nordmark 		addflag.crb_ip_recvpktinfo = 1;
3687c478bd9Sstevel@tonic-gate 	}
3697c478bd9Sstevel@tonic-gate 	/* If app asked for hoplimit and it has changed ... */
370bd670b35SErik Nordmark 	if (connp->conn_recv_ancillary.crb_ipv6_recvhoplimit &&
371bd670b35SErik Nordmark 	    ipp->ipp_hoplimit != sctp->sctp_recvhops) {
3727c478bd9Sstevel@tonic-gate 		optlen += sizeof (*cmsg) + sizeof (uint_t);
3737c478bd9Sstevel@tonic-gate 		if (hdrlen == 0)
3747c478bd9Sstevel@tonic-gate 			hdrlen = sizeof (struct T_unitdata_ind);
375bd670b35SErik Nordmark 		addflag.crb_ipv6_recvhoplimit = 1;
376bd670b35SErik Nordmark 	}
377bd670b35SErik Nordmark 	/* If app asked for tclass and it has changed ... */
378bd670b35SErik Nordmark 	if (connp->conn_recv_ancillary.crb_ipv6_recvtclass &&
379bd670b35SErik Nordmark 	    ipp->ipp_tclass != sctp->sctp_recvtclass) {
380bd670b35SErik Nordmark 		optlen += sizeof (struct T_opthdr) + sizeof (uint_t);
381bd670b35SErik Nordmark 		if (hdrlen == 0)
382bd670b35SErik Nordmark 			hdrlen = sizeof (struct T_unitdata_ind);
383bd670b35SErik Nordmark 		addflag.crb_ipv6_recvtclass = 1;
3847c478bd9Sstevel@tonic-gate 	}
3857c478bd9Sstevel@tonic-gate 	/* If app asked for hopbyhop headers and it has changed ... */
386bd670b35SErik Nordmark 	if (connp->conn_recv_ancillary.crb_ipv6_recvhopopts &&
38745916cd2Sjpk 	    ip_cmpbuf(sctp->sctp_hopopts, sctp->sctp_hopoptslen,
3887c478bd9Sstevel@tonic-gate 	    (ipp->ipp_fields & IPPF_HOPOPTS),
3897c478bd9Sstevel@tonic-gate 	    ipp->ipp_hopopts, ipp->ipp_hopoptslen)) {
39045916cd2Sjpk 		optlen += sizeof (*cmsg) + ipp->ipp_hopoptslen -
39145916cd2Sjpk 		    sctp->sctp_v6label_len;
3927c478bd9Sstevel@tonic-gate 		if (hdrlen == 0)
3937c478bd9Sstevel@tonic-gate 			hdrlen = sizeof (struct T_unitdata_ind);
394bd670b35SErik Nordmark 		addflag.crb_ipv6_recvhopopts = 1;
39545916cd2Sjpk 		if (!ip_allocbuf((void **)&sctp->sctp_hopopts,
3967c478bd9Sstevel@tonic-gate 		    &sctp->sctp_hopoptslen,
3977c478bd9Sstevel@tonic-gate 		    (ipp->ipp_fields & IPPF_HOPOPTS),
3987c478bd9Sstevel@tonic-gate 		    ipp->ipp_hopopts, ipp->ipp_hopoptslen))
3997c478bd9Sstevel@tonic-gate 			return (-1);
4007c478bd9Sstevel@tonic-gate 	}
4017c478bd9Sstevel@tonic-gate 	/* If app asked for dst headers before routing headers ... */
402bd670b35SErik Nordmark 	if (connp->conn_recv_ancillary.crb_ipv6_recvrthdrdstopts &&
403bd670b35SErik Nordmark 	    ip_cmpbuf(sctp->sctp_rthdrdstopts, sctp->sctp_rthdrdstoptslen,
404bd670b35SErik Nordmark 	    (ipp->ipp_fields & IPPF_RTHDRDSTOPTS),
405bd670b35SErik Nordmark 	    ipp->ipp_rthdrdstopts, ipp->ipp_rthdrdstoptslen)) {
406bd670b35SErik Nordmark 		optlen += sizeof (*cmsg) + ipp->ipp_rthdrdstoptslen;
4077c478bd9Sstevel@tonic-gate 		if (hdrlen == 0)
4087c478bd9Sstevel@tonic-gate 			hdrlen = sizeof (struct T_unitdata_ind);
409bd670b35SErik Nordmark 		addflag.crb_ipv6_recvrthdrdstopts = 1;
410bd670b35SErik Nordmark 		if (!ip_allocbuf((void **)&sctp->sctp_rthdrdstopts,
411bd670b35SErik Nordmark 		    &sctp->sctp_rthdrdstoptslen,
412bd670b35SErik Nordmark 		    (ipp->ipp_fields & IPPF_RTHDRDSTOPTS),
413bd670b35SErik Nordmark 		    ipp->ipp_rthdrdstopts, ipp->ipp_rthdrdstoptslen))
4147c478bd9Sstevel@tonic-gate 			return (-1);
4157c478bd9Sstevel@tonic-gate 	}
4167c478bd9Sstevel@tonic-gate 	/* If app asked for routing headers and it has changed ... */
417bd670b35SErik Nordmark 	if (connp->conn_recv_ancillary.crb_ipv6_recvrthdr &&
418bd670b35SErik Nordmark 	    ip_cmpbuf(sctp->sctp_rthdr, sctp->sctp_rthdrlen,
4197c478bd9Sstevel@tonic-gate 	    (ipp->ipp_fields & IPPF_RTHDR),
4207c478bd9Sstevel@tonic-gate 	    ipp->ipp_rthdr, ipp->ipp_rthdrlen)) {
4217c478bd9Sstevel@tonic-gate 		optlen += sizeof (*cmsg) + ipp->ipp_rthdrlen;
4227c478bd9Sstevel@tonic-gate 		if (hdrlen == 0)
4237c478bd9Sstevel@tonic-gate 			hdrlen = sizeof (struct T_unitdata_ind);
424bd670b35SErik Nordmark 		addflag.crb_ipv6_recvrthdr = 1;
42545916cd2Sjpk 		if (!ip_allocbuf((void **)&sctp->sctp_rthdr,
4267c478bd9Sstevel@tonic-gate 		    &sctp->sctp_rthdrlen,
4277c478bd9Sstevel@tonic-gate 		    (ipp->ipp_fields & IPPF_RTHDR),
4287c478bd9Sstevel@tonic-gate 		    ipp->ipp_rthdr, ipp->ipp_rthdrlen))
4297c478bd9Sstevel@tonic-gate 			return (-1);
4307c478bd9Sstevel@tonic-gate 	}
4317c478bd9Sstevel@tonic-gate 	/* If app asked for dest headers and it has changed ... */
432bd670b35SErik Nordmark 	if (connp->conn_recv_ancillary.crb_ipv6_recvdstopts &&
43345916cd2Sjpk 	    ip_cmpbuf(sctp->sctp_dstopts, sctp->sctp_dstoptslen,
4347c478bd9Sstevel@tonic-gate 	    (ipp->ipp_fields & IPPF_DSTOPTS),
4357c478bd9Sstevel@tonic-gate 	    ipp->ipp_dstopts, ipp->ipp_dstoptslen)) {
4367c478bd9Sstevel@tonic-gate 		optlen += sizeof (*cmsg) + ipp->ipp_dstoptslen;
4377c478bd9Sstevel@tonic-gate 		if (hdrlen == 0)
4387c478bd9Sstevel@tonic-gate 			hdrlen = sizeof (struct T_unitdata_ind);
439bd670b35SErik Nordmark 		addflag.crb_ipv6_recvdstopts = 1;
44045916cd2Sjpk 		if (!ip_allocbuf((void **)&sctp->sctp_dstopts,
4417c478bd9Sstevel@tonic-gate 		    &sctp->sctp_dstoptslen,
4427c478bd9Sstevel@tonic-gate 		    (ipp->ipp_fields & IPPF_DSTOPTS),
4437c478bd9Sstevel@tonic-gate 		    ipp->ipp_dstopts, ipp->ipp_dstoptslen))
4447c478bd9Sstevel@tonic-gate 			return (-1);
4457c478bd9Sstevel@tonic-gate 	}
4467c478bd9Sstevel@tonic-gate noancillary:
4477c478bd9Sstevel@tonic-gate 	/* Nothing to add */
4487c478bd9Sstevel@tonic-gate 	if (hdrlen == 0)
4497c478bd9Sstevel@tonic-gate 		return (-1);
4507c478bd9Sstevel@tonic-gate 
4517c478bd9Sstevel@tonic-gate 	mp1 = allocb(hdrlen + optlen + sizeof (void *), BPRI_MED);
4527c478bd9Sstevel@tonic-gate 	if (mp1 == NULL)
4537c478bd9Sstevel@tonic-gate 		return (-1);
4547c478bd9Sstevel@tonic-gate 	mp1->b_cont = *mp;
4557c478bd9Sstevel@tonic-gate 	*mp = mp1;
4567c478bd9Sstevel@tonic-gate 	mp1->b_rptr += sizeof (void *);  /* pointer worth of padding */
4577c478bd9Sstevel@tonic-gate 	mp1->b_wptr = mp1->b_rptr + hdrlen + optlen;
4587c478bd9Sstevel@tonic-gate 	DB_TYPE(mp1) = M_PROTO;
4597c478bd9Sstevel@tonic-gate 	tudi = (struct T_unitdata_ind *)mp1->b_rptr;
4607c478bd9Sstevel@tonic-gate 	tudi->PRIM_type = T_UNITDATA_IND;
4617c478bd9Sstevel@tonic-gate 	tudi->SRC_length = sin4 ? sizeof (*sin4) : sizeof (*sin6);
4627c478bd9Sstevel@tonic-gate 	tudi->SRC_offset = sizeof (*tudi);
4637c478bd9Sstevel@tonic-gate 	tudi->OPT_offset = sizeof (*tudi) + tudi->SRC_length;
4647c478bd9Sstevel@tonic-gate 	tudi->OPT_length = optlen;
4657c478bd9Sstevel@tonic-gate 	if (sin4) {
4667c478bd9Sstevel@tonic-gate 		bcopy(sin4, tudi + 1, sizeof (*sin4));
4677c478bd9Sstevel@tonic-gate 	} else {
4687c478bd9Sstevel@tonic-gate 		bcopy(sin6, tudi + 1, sizeof (*sin6));
4697c478bd9Sstevel@tonic-gate 	}
4707c478bd9Sstevel@tonic-gate 	optptr = (uchar_t *)tudi + tudi->OPT_offset;
4717c478bd9Sstevel@tonic-gate 
4727c478bd9Sstevel@tonic-gate 	if (sctp->sctp_recvsndrcvinfo) {
4737c478bd9Sstevel@tonic-gate 		/* XXX need backout method if memory allocation fails. */
4747c478bd9Sstevel@tonic-gate 		struct sctp_sndrcvinfo *sri;
4757c478bd9Sstevel@tonic-gate 
4767c478bd9Sstevel@tonic-gate 		cmsg = (struct cmsghdr *)optptr;
4777c478bd9Sstevel@tonic-gate 		cmsg->cmsg_level = IPPROTO_SCTP;
4787c478bd9Sstevel@tonic-gate 		cmsg->cmsg_type = SCTP_SNDRCV;
4797c478bd9Sstevel@tonic-gate 		cmsg->cmsg_len = sizeof (*cmsg) + sizeof (*sri);
4807c478bd9Sstevel@tonic-gate 		optptr += sizeof (*cmsg);
4817c478bd9Sstevel@tonic-gate 
4827c478bd9Sstevel@tonic-gate 		sri = (struct sctp_sndrcvinfo *)(cmsg + 1);
4837c478bd9Sstevel@tonic-gate 		ASSERT(OK_32PTR(sri));
4847c478bd9Sstevel@tonic-gate 		sri->sinfo_stream = ntohs(dcp->sdh_sid);
4857c478bd9Sstevel@tonic-gate 		sri->sinfo_ssn = ntohs(dcp->sdh_ssn);
4867c478bd9Sstevel@tonic-gate 		if (SCTP_DATA_GET_UBIT(dcp)) {
4877c478bd9Sstevel@tonic-gate 			sri->sinfo_flags = MSG_UNORDERED;
4887c478bd9Sstevel@tonic-gate 		} else {
4897c478bd9Sstevel@tonic-gate 			sri->sinfo_flags = 0;
4907c478bd9Sstevel@tonic-gate 		}
4917c478bd9Sstevel@tonic-gate 		sri->sinfo_ppid = dcp->sdh_payload_id;
4927c478bd9Sstevel@tonic-gate 		sri->sinfo_context = 0;
4937c478bd9Sstevel@tonic-gate 		sri->sinfo_timetolive = 0;
4947c478bd9Sstevel@tonic-gate 		sri->sinfo_tsn = ntohl(dcp->sdh_tsn);
4957c478bd9Sstevel@tonic-gate 		sri->sinfo_cumtsn = sctp->sctp_ftsn;
4967c478bd9Sstevel@tonic-gate 		sri->sinfo_assoc_id = 0;
4977c478bd9Sstevel@tonic-gate 
4987c478bd9Sstevel@tonic-gate 		optptr += sizeof (*sri);
4997c478bd9Sstevel@tonic-gate 	}
5007c478bd9Sstevel@tonic-gate 
5017c478bd9Sstevel@tonic-gate 	/*
5027c478bd9Sstevel@tonic-gate 	 * If app asked for pktinfo and the index has changed ...
5037c478bd9Sstevel@tonic-gate 	 * Note that the local address never changes for the connection.
5047c478bd9Sstevel@tonic-gate 	 */
505bd670b35SErik Nordmark 	if (addflag.crb_ip_recvpktinfo) {
5067c478bd9Sstevel@tonic-gate 		struct in6_pktinfo *pkti;
507bd670b35SErik Nordmark 		uint_t ifindex;
5087c478bd9Sstevel@tonic-gate 
509bd670b35SErik Nordmark 		ifindex = ira->ira_ruifindex;
5107c478bd9Sstevel@tonic-gate 		cmsg = (struct cmsghdr *)optptr;
5117c478bd9Sstevel@tonic-gate 		cmsg->cmsg_level = IPPROTO_IPV6;
5127c478bd9Sstevel@tonic-gate 		cmsg->cmsg_type = IPV6_PKTINFO;
5137c478bd9Sstevel@tonic-gate 		cmsg->cmsg_len = sizeof (*cmsg) + sizeof (*pkti);
5147c478bd9Sstevel@tonic-gate 		optptr += sizeof (*cmsg);
5157c478bd9Sstevel@tonic-gate 
5167c478bd9Sstevel@tonic-gate 		pkti = (struct in6_pktinfo *)optptr;
517bd670b35SErik Nordmark 		if (connp->conn_family == AF_INET6)
5187c478bd9Sstevel@tonic-gate 			pkti->ipi6_addr = sctp->sctp_ip6h->ip6_src;
5197c478bd9Sstevel@tonic-gate 		else
5207c478bd9Sstevel@tonic-gate 			IN6_IPADDR_TO_V4MAPPED(sctp->sctp_ipha->ipha_src,
5217c478bd9Sstevel@tonic-gate 			    &pkti->ipi6_addr);
522bd670b35SErik Nordmark 
523bd670b35SErik Nordmark 		pkti->ipi6_ifindex = ifindex;
5247c478bd9Sstevel@tonic-gate 		optptr += sizeof (*pkti);
5257c478bd9Sstevel@tonic-gate 		ASSERT(OK_32PTR(optptr));
5267c478bd9Sstevel@tonic-gate 		/* Save as "last" value */
527bd670b35SErik Nordmark 		sctp->sctp_recvifindex = ifindex;
5287c478bd9Sstevel@tonic-gate 	}
5297c478bd9Sstevel@tonic-gate 	/* If app asked for hoplimit and it has changed ... */
530bd670b35SErik Nordmark 	if (addflag.crb_ipv6_recvhoplimit) {
5317c478bd9Sstevel@tonic-gate 		cmsg = (struct cmsghdr *)optptr;
5327c478bd9Sstevel@tonic-gate 		cmsg->cmsg_level = IPPROTO_IPV6;
5337c478bd9Sstevel@tonic-gate 		cmsg->cmsg_type = IPV6_HOPLIMIT;
5347c478bd9Sstevel@tonic-gate 		cmsg->cmsg_len = sizeof (*cmsg) + sizeof (uint_t);
5357c478bd9Sstevel@tonic-gate 		optptr += sizeof (*cmsg);
5367c478bd9Sstevel@tonic-gate 
5377c478bd9Sstevel@tonic-gate 		*(uint_t *)optptr = ipp->ipp_hoplimit;
5387c478bd9Sstevel@tonic-gate 		optptr += sizeof (uint_t);
5397c478bd9Sstevel@tonic-gate 		ASSERT(OK_32PTR(optptr));
5407c478bd9Sstevel@tonic-gate 		/* Save as "last" value */
5417c478bd9Sstevel@tonic-gate 		sctp->sctp_recvhops = ipp->ipp_hoplimit;
5427c478bd9Sstevel@tonic-gate 	}
543bd670b35SErik Nordmark 	/* If app asked for tclass and it has changed ... */
544bd670b35SErik Nordmark 	if (addflag.crb_ipv6_recvtclass) {
545bd670b35SErik Nordmark 		cmsg = (struct cmsghdr *)optptr;
546bd670b35SErik Nordmark 		cmsg->cmsg_level = IPPROTO_IPV6;
547bd670b35SErik Nordmark 		cmsg->cmsg_type = IPV6_TCLASS;
548bd670b35SErik Nordmark 		cmsg->cmsg_len = sizeof (*cmsg) + sizeof (uint_t);
549bd670b35SErik Nordmark 		optptr += sizeof (*cmsg);
550bd670b35SErik Nordmark 
551bd670b35SErik Nordmark 		*(uint_t *)optptr = ipp->ipp_tclass;
552bd670b35SErik Nordmark 		optptr += sizeof (uint_t);
553bd670b35SErik Nordmark 		ASSERT(OK_32PTR(optptr));
554bd670b35SErik Nordmark 		/* Save as "last" value */
555bd670b35SErik Nordmark 		sctp->sctp_recvtclass = ipp->ipp_tclass;
556bd670b35SErik Nordmark 	}
557bd670b35SErik Nordmark 	if (addflag.crb_ipv6_recvhopopts) {
5587c478bd9Sstevel@tonic-gate 		cmsg = (struct cmsghdr *)optptr;
5597c478bd9Sstevel@tonic-gate 		cmsg->cmsg_level = IPPROTO_IPV6;
5607c478bd9Sstevel@tonic-gate 		cmsg->cmsg_type = IPV6_HOPOPTS;
5617c478bd9Sstevel@tonic-gate 		cmsg->cmsg_len = sizeof (*cmsg) + ipp->ipp_hopoptslen;
5627c478bd9Sstevel@tonic-gate 		optptr += sizeof (*cmsg);
5637c478bd9Sstevel@tonic-gate 
5647c478bd9Sstevel@tonic-gate 		bcopy(ipp->ipp_hopopts, optptr, ipp->ipp_hopoptslen);
5657c478bd9Sstevel@tonic-gate 		optptr += ipp->ipp_hopoptslen;
5667c478bd9Sstevel@tonic-gate 		ASSERT(OK_32PTR(optptr));
5677c478bd9Sstevel@tonic-gate 		/* Save as last value */
56845916cd2Sjpk 		ip_savebuf((void **)&sctp->sctp_hopopts,
5697c478bd9Sstevel@tonic-gate 		    &sctp->sctp_hopoptslen,
5707c478bd9Sstevel@tonic-gate 		    (ipp->ipp_fields & IPPF_HOPOPTS),
5717c478bd9Sstevel@tonic-gate 		    ipp->ipp_hopopts, ipp->ipp_hopoptslen);
5727c478bd9Sstevel@tonic-gate 	}
573bd670b35SErik Nordmark 	if (addflag.crb_ipv6_recvrthdrdstopts) {
5747c478bd9Sstevel@tonic-gate 		cmsg = (struct cmsghdr *)optptr;
5757c478bd9Sstevel@tonic-gate 		cmsg->cmsg_level = IPPROTO_IPV6;
5767c478bd9Sstevel@tonic-gate 		cmsg->cmsg_type = IPV6_RTHDRDSTOPTS;
577bd670b35SErik Nordmark 		cmsg->cmsg_len = sizeof (*cmsg) + ipp->ipp_rthdrdstoptslen;
5787c478bd9Sstevel@tonic-gate 		optptr += sizeof (*cmsg);
5797c478bd9Sstevel@tonic-gate 
580bd670b35SErik Nordmark 		bcopy(ipp->ipp_rthdrdstopts, optptr, ipp->ipp_rthdrdstoptslen);
581bd670b35SErik Nordmark 		optptr += ipp->ipp_rthdrdstoptslen;
5827c478bd9Sstevel@tonic-gate 		ASSERT(OK_32PTR(optptr));
5837c478bd9Sstevel@tonic-gate 		/* Save as last value */
584bd670b35SErik Nordmark 		ip_savebuf((void **)&sctp->sctp_rthdrdstopts,
585bd670b35SErik Nordmark 		    &sctp->sctp_rthdrdstoptslen,
586bd670b35SErik Nordmark 		    (ipp->ipp_fields & IPPF_RTHDRDSTOPTS),
587bd670b35SErik Nordmark 		    ipp->ipp_rthdrdstopts, ipp->ipp_rthdrdstoptslen);
5887c478bd9Sstevel@tonic-gate 	}
589bd670b35SErik Nordmark 	if (addflag.crb_ipv6_recvrthdr) {
5907c478bd9Sstevel@tonic-gate 		cmsg = (struct cmsghdr *)optptr;
5917c478bd9Sstevel@tonic-gate 		cmsg->cmsg_level = IPPROTO_IPV6;
5927c478bd9Sstevel@tonic-gate 		cmsg->cmsg_type = IPV6_RTHDR;
5937c478bd9Sstevel@tonic-gate 		cmsg->cmsg_len = sizeof (*cmsg) + ipp->ipp_rthdrlen;
5947c478bd9Sstevel@tonic-gate 		optptr += sizeof (*cmsg);
5957c478bd9Sstevel@tonic-gate 
5967c478bd9Sstevel@tonic-gate 		bcopy(ipp->ipp_rthdr, optptr, ipp->ipp_rthdrlen);
5977c478bd9Sstevel@tonic-gate 		optptr += ipp->ipp_rthdrlen;
5987c478bd9Sstevel@tonic-gate 		ASSERT(OK_32PTR(optptr));
5997c478bd9Sstevel@tonic-gate 		/* Save as last value */
60045916cd2Sjpk 		ip_savebuf((void **)&sctp->sctp_rthdr,
6017c478bd9Sstevel@tonic-gate 		    &sctp->sctp_rthdrlen,
6027c478bd9Sstevel@tonic-gate 		    (ipp->ipp_fields & IPPF_RTHDR),
6037c478bd9Sstevel@tonic-gate 		    ipp->ipp_rthdr, ipp->ipp_rthdrlen);
6047c478bd9Sstevel@tonic-gate 	}
605bd670b35SErik Nordmark 	if (addflag.crb_ipv6_recvdstopts) {
6067c478bd9Sstevel@tonic-gate 		cmsg = (struct cmsghdr *)optptr;
6077c478bd9Sstevel@tonic-gate 		cmsg->cmsg_level = IPPROTO_IPV6;
6087c478bd9Sstevel@tonic-gate 		cmsg->cmsg_type = IPV6_DSTOPTS;
6097c478bd9Sstevel@tonic-gate 		cmsg->cmsg_len = sizeof (*cmsg) + ipp->ipp_dstoptslen;
6107c478bd9Sstevel@tonic-gate 		optptr += sizeof (*cmsg);
6117c478bd9Sstevel@tonic-gate 
6127c478bd9Sstevel@tonic-gate 		bcopy(ipp->ipp_dstopts, optptr, ipp->ipp_dstoptslen);
6137c478bd9Sstevel@tonic-gate 		optptr += ipp->ipp_dstoptslen;
6147c478bd9Sstevel@tonic-gate 		ASSERT(OK_32PTR(optptr));
6157c478bd9Sstevel@tonic-gate 		/* Save as last value */
61645916cd2Sjpk 		ip_savebuf((void **)&sctp->sctp_dstopts,
6177c478bd9Sstevel@tonic-gate 		    &sctp->sctp_dstoptslen,
6187c478bd9Sstevel@tonic-gate 		    (ipp->ipp_fields & IPPF_DSTOPTS),
6197c478bd9Sstevel@tonic-gate 		    ipp->ipp_dstopts, ipp->ipp_dstoptslen);
6207c478bd9Sstevel@tonic-gate 	}
6217c478bd9Sstevel@tonic-gate 
6227c478bd9Sstevel@tonic-gate 	ASSERT(optptr == mp1->b_wptr);
6237c478bd9Sstevel@tonic-gate 
6247c478bd9Sstevel@tonic-gate 	return (0);
6257c478bd9Sstevel@tonic-gate }
6267c478bd9Sstevel@tonic-gate 
6277c478bd9Sstevel@tonic-gate void
6287c478bd9Sstevel@tonic-gate sctp_free_reass(sctp_instr_t *sip)
6297c478bd9Sstevel@tonic-gate {
6307c478bd9Sstevel@tonic-gate 	mblk_t *mp, *mpnext, *mctl;
631c3c17166SGeorge Shepherd #ifdef	DEBUG
632c3c17166SGeorge Shepherd 	sctp_reass_t	*srp;
633c3c17166SGeorge Shepherd #endif
6347c478bd9Sstevel@tonic-gate 
6357c478bd9Sstevel@tonic-gate 	for (mp = sip->istr_reass; mp != NULL; mp = mpnext) {
6367c478bd9Sstevel@tonic-gate 		mpnext = mp->b_next;
6377c478bd9Sstevel@tonic-gate 		mp->b_next = NULL;
6387c478bd9Sstevel@tonic-gate 		mp->b_prev = NULL;
6397c478bd9Sstevel@tonic-gate 		if (DB_TYPE(mp) == M_CTL) {
6407c478bd9Sstevel@tonic-gate 			mctl = mp;
641c3c17166SGeorge Shepherd #ifdef	DEBUG
642c3c17166SGeorge Shepherd 			srp = (sctp_reass_t *)DB_BASE(mctl);
643c3c17166SGeorge Shepherd 			/* Partial delivery can leave empty srp */
6446be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 			ASSERT(mp->b_cont != NULL || srp->sr_got == 0);
645c3c17166SGeorge Shepherd #endif
6467c478bd9Sstevel@tonic-gate 			mp = mp->b_cont;
6477c478bd9Sstevel@tonic-gate 			mctl->b_cont = NULL;
6487c478bd9Sstevel@tonic-gate 			freeb(mctl);
6497c478bd9Sstevel@tonic-gate 		}
6507c478bd9Sstevel@tonic-gate 		freemsg(mp);
6517c478bd9Sstevel@tonic-gate 	}
65292baa190SGeorge Shepherd 	sip->istr_reass = NULL;
6537c478bd9Sstevel@tonic-gate }
6547c478bd9Sstevel@tonic-gate 
6557c478bd9Sstevel@tonic-gate /*
6567c478bd9Sstevel@tonic-gate  * If the series of data fragments of which dmp is a part is successfully
6577c478bd9Sstevel@tonic-gate  * reassembled, the first mblk in the series is returned. dc is adjusted
6587c478bd9Sstevel@tonic-gate  * to point at the data chunk in the lead mblk, and b_rptr also points to
6597c478bd9Sstevel@tonic-gate  * the data chunk; the following mblk's b_rptr's point at the actual payload.
6607c478bd9Sstevel@tonic-gate  *
6617c478bd9Sstevel@tonic-gate  * If the series is not yet reassembled, NULL is returned. dc is not changed.
6627c478bd9Sstevel@tonic-gate  * XXX should probably move this up into the state machine.
6637c478bd9Sstevel@tonic-gate  */
6647c478bd9Sstevel@tonic-gate 
6657c478bd9Sstevel@tonic-gate /* Fragment list for un-ordered messages. Partial delivery is not supported */
6667c478bd9Sstevel@tonic-gate static mblk_t *
6677c478bd9Sstevel@tonic-gate sctp_uodata_frag(sctp_t *sctp, mblk_t *dmp, sctp_data_hdr_t **dc)
6687c478bd9Sstevel@tonic-gate {
6697c478bd9Sstevel@tonic-gate 	mblk_t		*hmp;
6707c478bd9Sstevel@tonic-gate 	mblk_t		*begin = NULL;
6717c478bd9Sstevel@tonic-gate 	mblk_t		*end = NULL;
6727c478bd9Sstevel@tonic-gate 	sctp_data_hdr_t	*qdc;
6737c478bd9Sstevel@tonic-gate 	uint32_t	ntsn;
6747c478bd9Sstevel@tonic-gate 	uint32_t	tsn = ntohl((*dc)->sdh_tsn);
6757c478bd9Sstevel@tonic-gate #ifdef	DEBUG
6767c478bd9Sstevel@tonic-gate 	mblk_t		*mp1;
6777c478bd9Sstevel@tonic-gate #endif
6787c478bd9Sstevel@tonic-gate 
6797c478bd9Sstevel@tonic-gate 	/* First frag. */
6807c478bd9Sstevel@tonic-gate 	if (sctp->sctp_uo_frags == NULL) {
6817c478bd9Sstevel@tonic-gate 		sctp->sctp_uo_frags = dmp;
6827c478bd9Sstevel@tonic-gate 		return (NULL);
6837c478bd9Sstevel@tonic-gate 	}
6847c478bd9Sstevel@tonic-gate 	hmp = sctp->sctp_uo_frags;
6857c478bd9Sstevel@tonic-gate 	/*
6867c478bd9Sstevel@tonic-gate 	 * Insert the segment according to the TSN, fragmented unordered
6877c478bd9Sstevel@tonic-gate 	 * chunks are sequenced by TSN.
6887c478bd9Sstevel@tonic-gate 	 */
6897c478bd9Sstevel@tonic-gate 	while (hmp != NULL) {
6907c478bd9Sstevel@tonic-gate 		qdc = (sctp_data_hdr_t *)hmp->b_rptr;
6917c478bd9Sstevel@tonic-gate 		ntsn = ntohl(qdc->sdh_tsn);
6927c478bd9Sstevel@tonic-gate 		if (SEQ_GT(ntsn, tsn)) {
6937c478bd9Sstevel@tonic-gate 			if (hmp->b_prev == NULL) {
6947c478bd9Sstevel@tonic-gate 				dmp->b_next = hmp;
6957c478bd9Sstevel@tonic-gate 				hmp->b_prev = dmp;
6967c478bd9Sstevel@tonic-gate 				sctp->sctp_uo_frags = dmp;
6977c478bd9Sstevel@tonic-gate 			} else {
6987c478bd9Sstevel@tonic-gate 				dmp->b_next = hmp;
6997c478bd9Sstevel@tonic-gate 				dmp->b_prev = hmp->b_prev;
7007c478bd9Sstevel@tonic-gate 				hmp->b_prev->b_next = dmp;
7017c478bd9Sstevel@tonic-gate 				hmp->b_prev = dmp;
7027c478bd9Sstevel@tonic-gate 			}
7037c478bd9Sstevel@tonic-gate 			break;
7047c478bd9Sstevel@tonic-gate 		}
7057c478bd9Sstevel@tonic-gate 		if (hmp->b_next == NULL) {
7067c478bd9Sstevel@tonic-gate 			hmp->b_next = dmp;
7077c478bd9Sstevel@tonic-gate 			dmp->b_prev = hmp;
7087c478bd9Sstevel@tonic-gate 			break;
7097c478bd9Sstevel@tonic-gate 		}
7107c478bd9Sstevel@tonic-gate 		hmp = hmp->b_next;
7117c478bd9Sstevel@tonic-gate 	}
7127c478bd9Sstevel@tonic-gate 	/* check if we completed a msg */
7137c478bd9Sstevel@tonic-gate 	if (SCTP_DATA_GET_BBIT(*dc)) {
7147c478bd9Sstevel@tonic-gate 		begin = dmp;
7157c478bd9Sstevel@tonic-gate 	} else if (SCTP_DATA_GET_EBIT(*dc)) {
7167c478bd9Sstevel@tonic-gate 		end = dmp;
7177c478bd9Sstevel@tonic-gate 	}
7187c478bd9Sstevel@tonic-gate 	/*
7197c478bd9Sstevel@tonic-gate 	 * We walk consecutive TSNs backwards till we get a seg. with
7207c478bd9Sstevel@tonic-gate 	 * the B bit
7217c478bd9Sstevel@tonic-gate 	 */
7227c478bd9Sstevel@tonic-gate 	if (begin == NULL) {
7237c478bd9Sstevel@tonic-gate 		for (hmp = dmp->b_prev; hmp != NULL; hmp = hmp->b_prev) {
7247c478bd9Sstevel@tonic-gate 			qdc = (sctp_data_hdr_t *)hmp->b_rptr;
7257c478bd9Sstevel@tonic-gate 			ntsn = ntohl(qdc->sdh_tsn);
7267c478bd9Sstevel@tonic-gate 			if ((int32_t)(tsn - ntsn) > 1) {
7277c478bd9Sstevel@tonic-gate 				return (NULL);
7287c478bd9Sstevel@tonic-gate 			}
7297c478bd9Sstevel@tonic-gate 			if (SCTP_DATA_GET_BBIT(qdc)) {
7307c478bd9Sstevel@tonic-gate 				begin = hmp;
7317c478bd9Sstevel@tonic-gate 				break;
7327c478bd9Sstevel@tonic-gate 			}
7337c478bd9Sstevel@tonic-gate 			tsn = ntsn;
7347c478bd9Sstevel@tonic-gate 		}
7357c478bd9Sstevel@tonic-gate 	}
7367c478bd9Sstevel@tonic-gate 	tsn = ntohl((*dc)->sdh_tsn);
7377c478bd9Sstevel@tonic-gate 	/*
7387c478bd9Sstevel@tonic-gate 	 * We walk consecutive TSNs till we get a seg. with the E bit
7397c478bd9Sstevel@tonic-gate 	 */
7407c478bd9Sstevel@tonic-gate 	if (end == NULL) {
7417c478bd9Sstevel@tonic-gate 		for (hmp = dmp->b_next; hmp != NULL; hmp = hmp->b_next) {
7427c478bd9Sstevel@tonic-gate 			qdc = (sctp_data_hdr_t *)hmp->b_rptr;
7437c478bd9Sstevel@tonic-gate 			ntsn = ntohl(qdc->sdh_tsn);
7447c478bd9Sstevel@tonic-gate 			if ((int32_t)(ntsn - tsn) > 1) {
7457c478bd9Sstevel@tonic-gate 				return (NULL);
7467c478bd9Sstevel@tonic-gate 			}
7477c478bd9Sstevel@tonic-gate 			if (SCTP_DATA_GET_EBIT(qdc)) {
7487c478bd9Sstevel@tonic-gate 				end = hmp;
7497c478bd9Sstevel@tonic-gate 				break;
7507c478bd9Sstevel@tonic-gate 			}
7517c478bd9Sstevel@tonic-gate 			tsn = ntsn;
7527c478bd9Sstevel@tonic-gate 		}
7537c478bd9Sstevel@tonic-gate 	}
7547c478bd9Sstevel@tonic-gate 	if (begin == NULL || end == NULL) {
7557c478bd9Sstevel@tonic-gate 		return (NULL);
7567c478bd9Sstevel@tonic-gate 	}
7577c478bd9Sstevel@tonic-gate 	/* Got one!, Remove the msg from the list */
7587c478bd9Sstevel@tonic-gate 	if (sctp->sctp_uo_frags == begin) {
7597c478bd9Sstevel@tonic-gate 		ASSERT(begin->b_prev == NULL);
7607c478bd9Sstevel@tonic-gate 		sctp->sctp_uo_frags = end->b_next;
7617c478bd9Sstevel@tonic-gate 		if (end->b_next != NULL)
7627c478bd9Sstevel@tonic-gate 			end->b_next->b_prev = NULL;
7637c478bd9Sstevel@tonic-gate 	} else {
7647c478bd9Sstevel@tonic-gate 		begin->b_prev->b_next = end->b_next;
7657c478bd9Sstevel@tonic-gate 		if (end->b_next != NULL)
7667c478bd9Sstevel@tonic-gate 			end->b_next->b_prev = begin->b_prev;
7677c478bd9Sstevel@tonic-gate 	}
7687c478bd9Sstevel@tonic-gate 	begin->b_prev = NULL;
7697c478bd9Sstevel@tonic-gate 	end->b_next = NULL;
7707c478bd9Sstevel@tonic-gate 
7717c478bd9Sstevel@tonic-gate 	/*
7727c478bd9Sstevel@tonic-gate 	 * Null out b_next and b_prev and chain using b_cont.
7737c478bd9Sstevel@tonic-gate 	 */
7747c478bd9Sstevel@tonic-gate 	dmp = end = begin;
7757c478bd9Sstevel@tonic-gate 	hmp = begin->b_next;
7767c478bd9Sstevel@tonic-gate 	*dc = (sctp_data_hdr_t *)begin->b_rptr;
7777c478bd9Sstevel@tonic-gate 	begin->b_next = NULL;
7787c478bd9Sstevel@tonic-gate 	while (hmp != NULL) {
7797c478bd9Sstevel@tonic-gate 		qdc = (sctp_data_hdr_t *)hmp->b_rptr;
7807c478bd9Sstevel@tonic-gate 		hmp->b_rptr = (uchar_t *)(qdc + 1);
7817c478bd9Sstevel@tonic-gate 		end = hmp->b_next;
7827c478bd9Sstevel@tonic-gate 		dmp->b_cont = hmp;
7837c478bd9Sstevel@tonic-gate 		dmp = hmp;
7847c478bd9Sstevel@tonic-gate 
7857c478bd9Sstevel@tonic-gate 		if (end != NULL)
7867c478bd9Sstevel@tonic-gate 			hmp->b_next = NULL;
7877c478bd9Sstevel@tonic-gate 		hmp->b_prev = NULL;
7887c478bd9Sstevel@tonic-gate 		hmp = end;
7897c478bd9Sstevel@tonic-gate 	}
7907c478bd9Sstevel@tonic-gate 	BUMP_LOCAL(sctp->sctp_reassmsgs);
7917c478bd9Sstevel@tonic-gate #ifdef	DEBUG
7927c478bd9Sstevel@tonic-gate 	mp1 = begin;
7937c478bd9Sstevel@tonic-gate 	while (mp1 != NULL) {
7947c478bd9Sstevel@tonic-gate 		ASSERT(mp1->b_next == NULL);
7957c478bd9Sstevel@tonic-gate 		ASSERT(mp1->b_prev == NULL);
7967c478bd9Sstevel@tonic-gate 		mp1 = mp1->b_cont;
7977c478bd9Sstevel@tonic-gate 	}
7987c478bd9Sstevel@tonic-gate #endif
7997c478bd9Sstevel@tonic-gate 	return (begin);
8007c478bd9Sstevel@tonic-gate }
8017d546a59Svi117747 
8027d546a59Svi117747 /*
8037d546a59Svi117747  * Try partial delivery.
8047d546a59Svi117747  */
8057d546a59Svi117747 static mblk_t *
8067d546a59Svi117747 sctp_try_partial_delivery(sctp_t *sctp, mblk_t *hmp, sctp_reass_t *srp,
8077d546a59Svi117747     sctp_data_hdr_t **dc)
8087d546a59Svi117747 {
8097d546a59Svi117747 	mblk_t		*mp;
8107d546a59Svi117747 	mblk_t		*dmp;
8117d546a59Svi117747 	mblk_t		*qmp;
8127d546a59Svi117747 	mblk_t		*prev;
8137d546a59Svi117747 	sctp_data_hdr_t	*qdc;
8147d546a59Svi117747 	uint32_t	tsn;
8157d546a59Svi117747 
8167d546a59Svi117747 	ASSERT(DB_TYPE(hmp) == M_CTL);
8177d546a59Svi117747 
8187d546a59Svi117747 	dprint(4, ("trypartial: got=%d, needed=%d\n",
8196be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 	    (int)(srp->sr_got), (int)(srp->sr_needed)));
8207d546a59Svi117747 
821bd670b35SErik Nordmark 	mp = hmp->b_cont;
8227d546a59Svi117747 	qdc = (sctp_data_hdr_t *)mp->b_rptr;
8237d546a59Svi117747 
8246be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 	ASSERT(SCTP_DATA_GET_BBIT(qdc) && srp->sr_hasBchunk);
8257d546a59Svi117747 
8267d546a59Svi117747 	tsn = ntohl(qdc->sdh_tsn) + 1;
8277d546a59Svi117747 
8287d546a59Svi117747 	/*
8297d546a59Svi117747 	 * This loop has two exit conditions: the
8307d546a59Svi117747 	 * end of received chunks has been reached, or
8317d546a59Svi117747 	 * there is a break in the sequence. We want
8327d546a59Svi117747 	 * to chop the reassembly list as follows (the
8337d546a59Svi117747 	 * numbers are TSNs):
8347d546a59Svi117747 	 *   10 -> 11 -> 	(end of chunks)
8357d546a59Svi117747 	 *   10 -> 11 -> | 13   (break in sequence)
8367d546a59Svi117747 	 */
8377d546a59Svi117747 	prev = mp;
8387d546a59Svi117747 	mp = mp->b_cont;
8397d546a59Svi117747 	while (mp != NULL) {
8407d546a59Svi117747 		qdc = (sctp_data_hdr_t *)mp->b_rptr;
8417d546a59Svi117747 		if (ntohl(qdc->sdh_tsn) != tsn)
8427d546a59Svi117747 			break;
8437d546a59Svi117747 		prev = mp;
8447d546a59Svi117747 		mp = mp->b_cont;
8457d546a59Svi117747 		tsn++;
8467d546a59Svi117747 	}
8477d546a59Svi117747 	/*
8487d546a59Svi117747 	 * We are sending all the fragments upstream, we have to retain
8497d546a59Svi117747 	 * the srp info for further fragments.
8507d546a59Svi117747 	 */
8517d546a59Svi117747 	if (mp == NULL) {
8527d546a59Svi117747 		dmp = hmp->b_cont;
8537d546a59Svi117747 		hmp->b_cont = NULL;
8546be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		srp->sr_nexttsn = tsn;
8556be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		srp->sr_msglen = 0;
8566be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		srp->sr_needed = 0;
8576be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		srp->sr_got = 0;
8586be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		srp->sr_tail = NULL;
8597d546a59Svi117747 	} else {
860c3c17166SGeorge Shepherd 		/*
861c3c17166SGeorge Shepherd 		 * There is a gap then some ordered frags which are not
862c3c17166SGeorge Shepherd 		 * the next deliverable tsn. When the next deliverable
863c3c17166SGeorge Shepherd 		 * frag arrives it will be set as the new list head in
864c3c17166SGeorge Shepherd 		 * sctp_data_frag() by setting the B bit.
865c3c17166SGeorge Shepherd 		 */
8667d546a59Svi117747 		dmp = hmp->b_cont;
8677d546a59Svi117747 		hmp->b_cont = mp;
8687d546a59Svi117747 	}
8696be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 	srp->sr_hasBchunk = B_FALSE;
8707d546a59Svi117747 	/*
8717d546a59Svi117747 	 * mp now points at the last chunk in the sequence,
8727d546a59Svi117747 	 * and prev points to mp's previous in the list.
873c3c17166SGeorge Shepherd 	 * We chop the list at prev. Subsequent fragment
874c3c17166SGeorge Shepherd 	 * deliveries will follow the normal reassembly
875c3c17166SGeorge Shepherd 	 * path unless they too exceed the sctp_pd_point.
8767d546a59Svi117747 	 */
8777d546a59Svi117747 	prev->b_cont = NULL;
8786be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 	srp->sr_partial_delivered = B_TRUE;
8797d546a59Svi117747 
8807d546a59Svi117747 	dprint(4, ("trypartial: got some, got=%d, needed=%d\n",
8816be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 	    (int)(srp->sr_got), (int)(srp->sr_needed)));
8827d546a59Svi117747 
8837d546a59Svi117747 	/*
8847d546a59Svi117747 	 * Adjust all mblk's except the lead so their rptr's point to the
8857d546a59Svi117747 	 * payload. sctp_data_chunk() will need to process the lead's
8867d546a59Svi117747 	 * data chunk section, so leave it's rptr pointing at the data chunk.
8877d546a59Svi117747 	 */
8887d546a59Svi117747 	*dc = (sctp_data_hdr_t *)dmp->b_rptr;
8896be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 	if (srp->sr_tail != NULL) {
8906be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		srp->sr_got--;
8916be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		ASSERT(srp->sr_got != 0);
8926be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		if (srp->sr_needed != 0) {
8936be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 			srp->sr_needed--;
8946be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 			ASSERT(srp->sr_needed != 0);
8957d546a59Svi117747 		}
8966be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		srp->sr_msglen -= ntohs((*dc)->sdh_len);
8977d546a59Svi117747 	}
8987d546a59Svi117747 	for (qmp = dmp->b_cont; qmp != NULL; qmp = qmp->b_cont) {
8997d546a59Svi117747 		qdc = (sctp_data_hdr_t *)qmp->b_rptr;
9007d546a59Svi117747 		qmp->b_rptr = (uchar_t *)(qdc + 1);
9017d546a59Svi117747 
9027d546a59Svi117747 		/*
9037d546a59Svi117747 		 * Deduct the balance from got and needed here, now that
9047d546a59Svi117747 		 * we know we are actually delivering these data.
9057d546a59Svi117747 		 */
9066be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		if (srp->sr_tail != NULL) {
9076be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 			srp->sr_got--;
9086be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 			ASSERT(srp->sr_got != 0);
9096be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 			if (srp->sr_needed != 0) {
9106be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 				srp->sr_needed--;
9116be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 				ASSERT(srp->sr_needed != 0);
9127d546a59Svi117747 			}
9136be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 			srp->sr_msglen -= ntohs(qdc->sdh_len);
9147d546a59Svi117747 		}
9157d546a59Svi117747 	}
9166be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 	ASSERT(srp->sr_msglen == 0);
9177d546a59Svi117747 	BUMP_LOCAL(sctp->sctp_reassmsgs);
9187d546a59Svi117747 
9197d546a59Svi117747 	return (dmp);
9207d546a59Svi117747 }
9217d546a59Svi117747 
9227c478bd9Sstevel@tonic-gate /*
923c3c17166SGeorge Shepherd  * Handle received fragments for ordered delivery to upper layer protocol.
924c3c17166SGeorge Shepherd  * Manage the per message reassembly queue and if this fragment completes
925c3c17166SGeorge Shepherd  * reassembly of the message, or qualifies the already reassembled data
926c3c17166SGeorge Shepherd  * for partial delivery, prepare the message for delivery upstream.
927c3c17166SGeorge Shepherd  *
928c3c17166SGeorge Shepherd  * tpfinished in the caller remains set only when the incoming fragment
929c3c17166SGeorge Shepherd  * has completed the reassembly of the message associated with its ssn.
9307c478bd9Sstevel@tonic-gate  */
9317c478bd9Sstevel@tonic-gate static mblk_t *
9327c478bd9Sstevel@tonic-gate sctp_data_frag(sctp_t *sctp, mblk_t *dmp, sctp_data_hdr_t **dc, int *error,
9337d546a59Svi117747     sctp_instr_t *sip, boolean_t *tpfinished)
9347c478bd9Sstevel@tonic-gate {
935c3c17166SGeorge Shepherd 	mblk_t		*reassq_curr, *reassq_next, *reassq_prev;
936c3c17166SGeorge Shepherd 	mblk_t		*new_reassq;
9377c478bd9Sstevel@tonic-gate 	mblk_t		*qmp;
9387c478bd9Sstevel@tonic-gate 	mblk_t		*first_mp;
9397c478bd9Sstevel@tonic-gate 	sctp_reass_t	*srp;
9407c478bd9Sstevel@tonic-gate 	sctp_data_hdr_t	*qdc;
9417c478bd9Sstevel@tonic-gate 	sctp_data_hdr_t	*bdc;
9427c478bd9Sstevel@tonic-gate 	sctp_data_hdr_t	*edc;
9437c478bd9Sstevel@tonic-gate 	uint32_t	tsn;
9447d546a59Svi117747 	uint16_t	fraglen = 0;
9457c478bd9Sstevel@tonic-gate 
9467c478bd9Sstevel@tonic-gate 	*error = 0;
9477c478bd9Sstevel@tonic-gate 
948c3c17166SGeorge Shepherd 	/*
949c3c17166SGeorge Shepherd 	 * Find the reassembly queue for this data chunk, if none
950c3c17166SGeorge Shepherd 	 * yet exists, a new per message queue will be created and
951c3c17166SGeorge Shepherd 	 * appended to the end of the list of per message queues.
952c3c17166SGeorge Shepherd 	 *
953c3c17166SGeorge Shepherd 	 * sip points on sctp_instr_t representing instream messages
954c3c17166SGeorge Shepherd 	 * as yet undelivered for this stream (sid) of the association.
955c3c17166SGeorge Shepherd 	 */
956c3c17166SGeorge Shepherd 	reassq_next = reassq_prev = sip->istr_reass;
957c3c17166SGeorge Shepherd 	for (; reassq_next != NULL; reassq_next = reassq_next->b_next) {
958c3c17166SGeorge Shepherd 		srp = (sctp_reass_t *)DB_BASE(reassq_next);
9596be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		if (ntohs((*dc)->sdh_ssn) == srp->sr_ssn) {
960c3c17166SGeorge Shepherd 			reassq_curr = reassq_next;
9617c478bd9Sstevel@tonic-gate 			goto foundit;
9626be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		} else if (SSN_GT(srp->sr_ssn, ntohs((*dc)->sdh_ssn)))
9637c478bd9Sstevel@tonic-gate 			break;
964c3c17166SGeorge Shepherd 		reassq_prev = reassq_next;
9657c478bd9Sstevel@tonic-gate 	}
9667c478bd9Sstevel@tonic-gate 
9677d546a59Svi117747 	/*
968c3c17166SGeorge Shepherd 	 * First fragment of this message received, allocate a M_CTL that
969c3c17166SGeorge Shepherd 	 * will head the reassembly queue for this message. The message
970c3c17166SGeorge Shepherd 	 * and all its fragments are identified by having the same ssn.
971c3c17166SGeorge Shepherd 	 *
972c3c17166SGeorge Shepherd 	 * Arriving fragments will be inserted in tsn order on the
973c3c17166SGeorge Shepherd 	 * reassembly queue for this message (ssn), linked by b_cont.
9747d546a59Svi117747 	 */
975c3c17166SGeorge Shepherd 	if ((new_reassq = allocb(sizeof (*srp), BPRI_MED)) == NULL) {
976c3c17166SGeorge Shepherd 		*error = ENOMEM;
9777d546a59Svi117747 		return (NULL);
9787d546a59Svi117747 	}
979c3c17166SGeorge Shepherd 	DB_TYPE(new_reassq) = M_CTL;
980c3c17166SGeorge Shepherd 	srp = (sctp_reass_t *)DB_BASE(new_reassq);
981c3c17166SGeorge Shepherd 	new_reassq->b_cont = dmp;
9827c478bd9Sstevel@tonic-gate 
983c3c17166SGeorge Shepherd 	/*
984c3c17166SGeorge Shepherd 	 * All per ssn reassembly queues, (one for each message) on
985c3c17166SGeorge Shepherd 	 * this stream are doubly linked by b_next/b_prev back to the
986c3c17166SGeorge Shepherd 	 * instr_reass of the instream structure associated with this
987c3c17166SGeorge Shepherd 	 * stream id, (sip is initialized as sctp->sctp_instr[sid]).
988c3c17166SGeorge Shepherd 	 * Insert the new reassembly queue in the correct (ssn) order.
989c3c17166SGeorge Shepherd 	 */
990c3c17166SGeorge Shepherd 	if (reassq_next != NULL) {
991c3c17166SGeorge Shepherd 		if (sip->istr_reass == reassq_next) {
992c3c17166SGeorge Shepherd 			/* head insertion */
993c3c17166SGeorge Shepherd 			sip->istr_reass = new_reassq;
994c3c17166SGeorge Shepherd 			new_reassq->b_next = reassq_next;
995c3c17166SGeorge Shepherd 			new_reassq->b_prev = NULL;
996c3c17166SGeorge Shepherd 			reassq_next->b_prev = new_reassq;
9977c478bd9Sstevel@tonic-gate 		} else {
998c3c17166SGeorge Shepherd 			/* mid queue insertion */
999c3c17166SGeorge Shepherd 			reassq_prev->b_next = new_reassq;
1000c3c17166SGeorge Shepherd 			new_reassq->b_prev = reassq_prev;
1001c3c17166SGeorge Shepherd 			new_reassq->b_next = reassq_next;
1002c3c17166SGeorge Shepherd 			reassq_next->b_prev = new_reassq;
10037c478bd9Sstevel@tonic-gate 		}
10047c478bd9Sstevel@tonic-gate 	} else {
1005c3c17166SGeorge Shepherd 		/* place new reassembly queue at the end */
10067c478bd9Sstevel@tonic-gate 		if (sip->istr_reass == NULL) {
1007c3c17166SGeorge Shepherd 			sip->istr_reass = new_reassq;
1008c3c17166SGeorge Shepherd 			new_reassq->b_prev = NULL;
10097c478bd9Sstevel@tonic-gate 		} else {
1010c3c17166SGeorge Shepherd 			reassq_prev->b_next = new_reassq;
1011c3c17166SGeorge Shepherd 			new_reassq->b_prev = reassq_prev;
10127c478bd9Sstevel@tonic-gate 		}
1013c3c17166SGeorge Shepherd 		new_reassq->b_next = NULL;
10147c478bd9Sstevel@tonic-gate 	}
10156be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 	srp->sr_partial_delivered = B_FALSE;
10166be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 	srp->sr_ssn = ntohs((*dc)->sdh_ssn);
10176be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 	srp->sr_hasBchunk = B_FALSE;
10187d546a59Svi117747 empty_srp:
10196be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 	srp->sr_needed = 0;
10206be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 	srp->sr_got = 1;
1021c3c17166SGeorge Shepherd 	/* tail always the highest tsn on the reassembly queue for this ssn */
10226be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 	srp->sr_tail = dmp;
10237d546a59Svi117747 	if (SCTP_DATA_GET_BBIT(*dc)) {
1024c3c17166SGeorge Shepherd 		/* Incoming frag is flagged as the beginning of message */
10256be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		srp->sr_msglen = ntohs((*dc)->sdh_len);
10266be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		srp->sr_nexttsn = ntohl((*dc)->sdh_tsn) + 1;
10276be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		srp->sr_hasBchunk = B_TRUE;
10286be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 	} else if (srp->sr_partial_delivered &&
10296be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 	    srp->sr_nexttsn == ntohl((*dc)->sdh_tsn)) {
1030c3c17166SGeorge Shepherd 		/*
1031c3c17166SGeorge Shepherd 		 * The real beginning fragment of the message was already
1032c3c17166SGeorge Shepherd 		 * delivered upward, so this is the earliest frag expected.
1033c3c17166SGeorge Shepherd 		 * Fake the B-bit then see if this frag also completes the
1034c3c17166SGeorge Shepherd 		 * message.
1035c3c17166SGeorge Shepherd 		 */
10367d546a59Svi117747 		SCTP_DATA_SET_BBIT(*dc);
10376be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		srp->sr_hasBchunk = B_TRUE;
10386be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		srp->sr_msglen = ntohs((*dc)->sdh_len);
1039c3c17166SGeorge Shepherd 		if (SCTP_DATA_GET_EBIT(*dc)) {
1040c3c17166SGeorge Shepherd 			/* This frag is marked as the end of message */
10416be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 			srp->sr_needed = 1;
1042c3c17166SGeorge Shepherd 			/* Got all fragments of this message now */
1043c3c17166SGeorge Shepherd 			goto frag_done;
1044c3c17166SGeorge Shepherd 		}
10456be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		srp->sr_nexttsn++;
10467d546a59Svi117747 	}
1047c3c17166SGeorge Shepherd 
1048c3c17166SGeorge Shepherd 	/* The only fragment of this message currently queued */
1049c3c17166SGeorge Shepherd 	*tpfinished = B_FALSE;
10507c478bd9Sstevel@tonic-gate 	return (NULL);
10517c478bd9Sstevel@tonic-gate foundit:
10527c478bd9Sstevel@tonic-gate 	/*
1053c3c17166SGeorge Shepherd 	 * This message already has a reassembly queue. Insert the new frag
1054c3c17166SGeorge Shepherd 	 * in the reassembly queue. Try the tail first, on the assumption
1055c3c17166SGeorge Shepherd 	 * that the fragments are arriving in order.
10567c478bd9Sstevel@tonic-gate 	 */
10576be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 	qmp = srp->sr_tail;
10587d546a59Svi117747 
10597d546a59Svi117747 	/*
1060c3c17166SGeorge Shepherd 	 * A NULL tail means all existing fragments of the message have
1061c3c17166SGeorge Shepherd 	 * been entirely consumed during a partially delivery.
10627d546a59Svi117747 	 */
10637d546a59Svi117747 	if (qmp == NULL) {
10646be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		ASSERT(srp->sr_got == 0 && srp->sr_needed == 0 &&
10656be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		    srp->sr_partial_delivered);
1066c3c17166SGeorge Shepherd 		ASSERT(reassq_curr->b_cont == NULL);
1067c3c17166SGeorge Shepherd 		reassq_curr->b_cont = dmp;
10687d546a59Svi117747 		goto empty_srp;
1069c3c17166SGeorge Shepherd 	} else {
1070c3c17166SGeorge Shepherd 		/*
1071c3c17166SGeorge Shepherd 		 * If partial delivery did take place but the next arriving
1072c3c17166SGeorge Shepherd 		 * fragment was not the next to be delivered, or partial
1073c3c17166SGeorge Shepherd 		 * delivery broke off due to a gap, fragments remain on the
1074c3c17166SGeorge Shepherd 		 * tail. The next fragment due to be delivered still has to
1075c3c17166SGeorge Shepherd 		 * be set as the new head of list upon arrival. Fake B-bit
1076c3c17166SGeorge Shepherd 		 * on that frag then see if it also completes the message.
1077c3c17166SGeorge Shepherd 		 */
10786be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		if (srp->sr_partial_delivered &&
10796be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		    srp->sr_nexttsn == ntohl((*dc)->sdh_tsn)) {
1080c3c17166SGeorge Shepherd 			SCTP_DATA_SET_BBIT(*dc);
10816be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 			srp->sr_hasBchunk = B_TRUE;
1082c3c17166SGeorge Shepherd 			if (SCTP_DATA_GET_EBIT(*dc)) {
1083c3c17166SGeorge Shepherd 				/* Got all fragments of this message now */
1084c3c17166SGeorge Shepherd 				goto frag_done;
10857d546a59Svi117747 			}
1086c3c17166SGeorge Shepherd 		}
1087c3c17166SGeorge Shepherd 	}
1088c3c17166SGeorge Shepherd 
1089c3c17166SGeorge Shepherd 	/* grab the frag header of already queued tail frag for comparison */
10907c478bd9Sstevel@tonic-gate 	qdc = (sctp_data_hdr_t *)qmp->b_rptr;
10917c478bd9Sstevel@tonic-gate 	ASSERT(qmp->b_cont == NULL);
10927c478bd9Sstevel@tonic-gate 
1093c3c17166SGeorge Shepherd 	/* check if the frag goes on the tail in order */
10947c478bd9Sstevel@tonic-gate 	if (SEQ_GT(ntohl((*dc)->sdh_tsn), ntohl(qdc->sdh_tsn))) {
10957c478bd9Sstevel@tonic-gate 		qmp->b_cont = dmp;
10966be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		srp->sr_tail = dmp;
10977c478bd9Sstevel@tonic-gate 		dmp->b_cont = NULL;
10986be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		if (srp->sr_hasBchunk && srp->sr_nexttsn ==
10996be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		    ntohl((*dc)->sdh_tsn)) {
11006be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 			srp->sr_msglen += ntohs((*dc)->sdh_len);
11016be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 			srp->sr_nexttsn++;
11027d546a59Svi117747 		}
11037c478bd9Sstevel@tonic-gate 		goto inserted;
11047c478bd9Sstevel@tonic-gate 	}
11057c478bd9Sstevel@tonic-gate 
1106c3c17166SGeorge Shepherd 	/* Next check if we should insert this frag at the beginning */
1107c3c17166SGeorge Shepherd 	qmp = reassq_curr->b_cont;
11087c478bd9Sstevel@tonic-gate 	qdc = (sctp_data_hdr_t *)qmp->b_rptr;
11097c478bd9Sstevel@tonic-gate 	if (SEQ_LT(ntohl((*dc)->sdh_tsn), ntohl(qdc->sdh_tsn))) {
11107c478bd9Sstevel@tonic-gate 		dmp->b_cont = qmp;
1111c3c17166SGeorge Shepherd 		reassq_curr->b_cont = dmp;
11127d546a59Svi117747 		if (SCTP_DATA_GET_BBIT(*dc)) {
11136be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 			srp->sr_hasBchunk = B_TRUE;
11146be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 			srp->sr_nexttsn = ntohl((*dc)->sdh_tsn);
11157c478bd9Sstevel@tonic-gate 		}
11167d546a59Svi117747 		goto preinserted;
11177c478bd9Sstevel@tonic-gate 	}
11187c478bd9Sstevel@tonic-gate 
1119c3c17166SGeorge Shepherd 	/* Insert this frag in it's correct order in the middle */
11207c478bd9Sstevel@tonic-gate 	for (;;) {
11217c478bd9Sstevel@tonic-gate 		/* Tail check above should have caught this */
11227c478bd9Sstevel@tonic-gate 		ASSERT(qmp->b_cont != NULL);
11237c478bd9Sstevel@tonic-gate 
11247c478bd9Sstevel@tonic-gate 		qdc = (sctp_data_hdr_t *)qmp->b_cont->b_rptr;
11257c478bd9Sstevel@tonic-gate 		if (SEQ_LT(ntohl((*dc)->sdh_tsn), ntohl(qdc->sdh_tsn))) {
11267c478bd9Sstevel@tonic-gate 			/* insert here */
11277c478bd9Sstevel@tonic-gate 			dmp->b_cont = qmp->b_cont;
11287c478bd9Sstevel@tonic-gate 			qmp->b_cont = dmp;
11297c478bd9Sstevel@tonic-gate 			break;
11307c478bd9Sstevel@tonic-gate 		}
11317c478bd9Sstevel@tonic-gate 		qmp = qmp->b_cont;
11327c478bd9Sstevel@tonic-gate 	}
11337d546a59Svi117747 preinserted:
1134c3c17166SGeorge Shepherd 	/*
1135c3c17166SGeorge Shepherd 	 * Need head of message and to be due to deliver, otherwise skip
1136c3c17166SGeorge Shepherd 	 * the recalculation of the message length below.
1137c3c17166SGeorge Shepherd 	 */
11386be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 	if (!srp->sr_hasBchunk || ntohl((*dc)->sdh_tsn) != srp->sr_nexttsn)
11397d546a59Svi117747 		goto inserted;
11407d546a59Svi117747 	/*
11417d546a59Svi117747 	 * fraglen contains the length of consecutive chunks of fragments.
1142c3c17166SGeorge Shepherd 	 * starting from the chunk we just inserted.
11437d546a59Svi117747 	 */
11446be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 	tsn = srp->sr_nexttsn;
11457d546a59Svi117747 	for (qmp = dmp; qmp != NULL; qmp = qmp->b_cont) {
11467d546a59Svi117747 		qdc = (sctp_data_hdr_t *)qmp->b_rptr;
11477d546a59Svi117747 		if (tsn != ntohl(qdc->sdh_tsn))
11487d546a59Svi117747 			break;
11497d546a59Svi117747 		fraglen += ntohs(qdc->sdh_len);
11507d546a59Svi117747 		tsn++;
11517d546a59Svi117747 	}
11526be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 	srp->sr_nexttsn = tsn;
11536be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 	srp->sr_msglen += fraglen;
11547c478bd9Sstevel@tonic-gate inserted:
11556be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 	srp->sr_got++;
1156c3c17166SGeorge Shepherd 	first_mp = reassq_curr->b_cont;
1157c3c17166SGeorge Shepherd 	/* Prior to this frag either the beginning or end frag was missing */
11586be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 	if (srp->sr_needed == 0) {
1159c3c17166SGeorge Shepherd 		/* used to check if we have the first and last fragments */
11607c478bd9Sstevel@tonic-gate 		bdc = (sctp_data_hdr_t *)first_mp->b_rptr;
11616be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		edc = (sctp_data_hdr_t *)srp->sr_tail->b_rptr;
11627c478bd9Sstevel@tonic-gate 
1163c3c17166SGeorge Shepherd 		/*
1164c3c17166SGeorge Shepherd 		 * If we now have both the beginning and the end of the message,
1165c3c17166SGeorge Shepherd 		 * calculate how many fragments in the complete message.
1166c3c17166SGeorge Shepherd 		 */
11677d546a59Svi117747 		if (SCTP_DATA_GET_BBIT(bdc) && SCTP_DATA_GET_EBIT(edc)) {
11686be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 			srp->sr_needed = ntohl(edc->sdh_tsn) -
11697c478bd9Sstevel@tonic-gate 			    ntohl(bdc->sdh_tsn) + 1;
11707c478bd9Sstevel@tonic-gate 		}
11717d546a59Svi117747 	}
11727c478bd9Sstevel@tonic-gate 
11737d546a59Svi117747 	/*
11747d546a59Svi117747 	 * Try partial delivery if the message length has exceeded the
11757d546a59Svi117747 	 * partial delivery point. Only do this if we can immediately
11767d546a59Svi117747 	 * deliver the partially assembled message, and only partially
11777d546a59Svi117747 	 * deliver one message at a time (i.e. messages cannot be
1178c3c17166SGeorge Shepherd 	 * intermixed arriving at the upper layer).
1179c3c17166SGeorge Shepherd 	 * sctp_try_partial_delivery() will return a message consisting
1180c3c17166SGeorge Shepherd 	 * of only consecutive fragments.
11817d546a59Svi117747 	 */
11826be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 	if (srp->sr_needed != srp->sr_got) {
1183c3c17166SGeorge Shepherd 		/* we don't have the full message yet */
11847d546a59Svi117747 		dmp = NULL;
1185c3c17166SGeorge Shepherd 		if (ntohl((*dc)->sdh_tsn) <= sctp->sctp_ftsn &&
11866be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		    srp->sr_msglen >= sctp->sctp_pd_point &&
11876be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		    srp->sr_ssn == sip->nextseq) {
1188c3c17166SGeorge Shepherd 			dmp = sctp_try_partial_delivery(sctp, reassq_curr,
1189c3c17166SGeorge Shepherd 			    srp, dc);
11907c478bd9Sstevel@tonic-gate 		}
1191c3c17166SGeorge Shepherd 		*tpfinished = B_FALSE;
1192c3c17166SGeorge Shepherd 		/*
1193c3c17166SGeorge Shepherd 		 * NULL unless a segment of the message now qualified for
1194c3c17166SGeorge Shepherd 		 * partial_delivery and has been prepared for delivery by
1195c3c17166SGeorge Shepherd 		 * sctp_try_partial_delivery().
1196c3c17166SGeorge Shepherd 		 */
11977d546a59Svi117747 		return (dmp);
11987c478bd9Sstevel@tonic-gate 	}
11997d546a59Svi117747 frag_done:
12007c478bd9Sstevel@tonic-gate 	/*
1201c3c17166SGeorge Shepherd 	 * Reassembly complete for this message, prepare the data for delivery.
1202c3c17166SGeorge Shepherd 	 * First unlink the reassembly queue for this ssn from the list of
1203c3c17166SGeorge Shepherd 	 * messages in reassembly.
12047c478bd9Sstevel@tonic-gate 	 */
1205c3c17166SGeorge Shepherd 	if (sip->istr_reass == reassq_curr) {
1206c3c17166SGeorge Shepherd 		sip->istr_reass = reassq_curr->b_next;
1207c3c17166SGeorge Shepherd 		if (reassq_curr->b_next)
1208c3c17166SGeorge Shepherd 			reassq_curr->b_next->b_prev = NULL;
12097c478bd9Sstevel@tonic-gate 	} else {
1210c3c17166SGeorge Shepherd 		ASSERT(reassq_curr->b_prev != NULL);
1211c3c17166SGeorge Shepherd 		reassq_curr->b_prev->b_next = reassq_curr->b_next;
1212c3c17166SGeorge Shepherd 		if (reassq_curr->b_next)
1213c3c17166SGeorge Shepherd 			reassq_curr->b_next->b_prev = reassq_curr->b_prev;
12147c478bd9Sstevel@tonic-gate 	}
12157c478bd9Sstevel@tonic-gate 
12167c478bd9Sstevel@tonic-gate 	/*
1217c3c17166SGeorge Shepherd 	 * Need to clean up b_prev and b_next as freeb() will
1218c3c17166SGeorge Shepherd 	 * ASSERT that they are unused.
12197c478bd9Sstevel@tonic-gate 	 */
1220c3c17166SGeorge Shepherd 	reassq_curr->b_next = NULL;
1221c3c17166SGeorge Shepherd 	reassq_curr->b_prev = NULL;
1222c3c17166SGeorge Shepherd 
1223c3c17166SGeorge Shepherd 	dmp = reassq_curr;
1224c3c17166SGeorge Shepherd 	/* point to the head of the reassembled data message */
12257c478bd9Sstevel@tonic-gate 	dmp = dmp->b_cont;
1226c3c17166SGeorge Shepherd 	reassq_curr->b_cont = NULL;
1227c3c17166SGeorge Shepherd 	freeb(reassq_curr);
1228c3c17166SGeorge Shepherd 	/* Tell our caller that we are returning a complete message. */
12297d546a59Svi117747 	*tpfinished = B_TRUE;
12307c478bd9Sstevel@tonic-gate 
12317c478bd9Sstevel@tonic-gate 	/*
12327c478bd9Sstevel@tonic-gate 	 * Adjust all mblk's except the lead so their rptr's point to the
1233c3c17166SGeorge Shepherd 	 * payload. sctp_data_chunk() will need to process the lead's data
1234c3c17166SGeorge Shepherd 	 * data chunk section, so leave its rptr pointing at the data chunk
1235c3c17166SGeorge Shepherd 	 * header.
12367c478bd9Sstevel@tonic-gate 	 */
12377c478bd9Sstevel@tonic-gate 	*dc = (sctp_data_hdr_t *)dmp->b_rptr;
12387d546a59Svi117747 	for (qmp = dmp->b_cont; qmp != NULL; qmp = qmp->b_cont) {
12397c478bd9Sstevel@tonic-gate 		qdc = (sctp_data_hdr_t *)qmp->b_rptr;
12407c478bd9Sstevel@tonic-gate 		qmp->b_rptr = (uchar_t *)(qdc + 1);
12417c478bd9Sstevel@tonic-gate 	}
12427c478bd9Sstevel@tonic-gate 	BUMP_LOCAL(sctp->sctp_reassmsgs);
12437c478bd9Sstevel@tonic-gate 
12447c478bd9Sstevel@tonic-gate 	return (dmp);
12457c478bd9Sstevel@tonic-gate }
1246c3c17166SGeorge Shepherd 
12477c478bd9Sstevel@tonic-gate static void
12487c478bd9Sstevel@tonic-gate sctp_add_dup(uint32_t tsn, mblk_t **dups)
12497c478bd9Sstevel@tonic-gate {
12507c478bd9Sstevel@tonic-gate 	mblk_t *mp;
12517c478bd9Sstevel@tonic-gate 	size_t bsize = SCTP_DUP_MBLK_SZ * sizeof (tsn);
12527c478bd9Sstevel@tonic-gate 
12537c478bd9Sstevel@tonic-gate 	if (dups == NULL) {
12547c478bd9Sstevel@tonic-gate 		return;
12557c478bd9Sstevel@tonic-gate 	}
12567c478bd9Sstevel@tonic-gate 
12577c478bd9Sstevel@tonic-gate 	/* first time? */
12587c478bd9Sstevel@tonic-gate 	if (*dups == NULL) {
12597c478bd9Sstevel@tonic-gate 		*dups = allocb(bsize, BPRI_MED);
12607c478bd9Sstevel@tonic-gate 		if (*dups == NULL) {
12617c478bd9Sstevel@tonic-gate 			return;
12627c478bd9Sstevel@tonic-gate 		}
12637c478bd9Sstevel@tonic-gate 	}
12647c478bd9Sstevel@tonic-gate 
12657c478bd9Sstevel@tonic-gate 	mp = *dups;
12667c478bd9Sstevel@tonic-gate 	if ((mp->b_wptr - mp->b_rptr) >= bsize) {
12677c478bd9Sstevel@tonic-gate 		/* maximum reached */
12687c478bd9Sstevel@tonic-gate 		return;
12697c478bd9Sstevel@tonic-gate 	}
12707c478bd9Sstevel@tonic-gate 
12717c478bd9Sstevel@tonic-gate 	/* add the duplicate tsn */
12727c478bd9Sstevel@tonic-gate 	bcopy(&tsn, mp->b_wptr, sizeof (tsn));
12737c478bd9Sstevel@tonic-gate 	mp->b_wptr += sizeof (tsn);
12747c478bd9Sstevel@tonic-gate 	ASSERT((mp->b_wptr - mp->b_rptr) <= bsize);
12757c478bd9Sstevel@tonic-gate }
12767c478bd9Sstevel@tonic-gate 
1277c3c17166SGeorge Shepherd /*
1278c3c17166SGeorge Shepherd  * All incoming sctp data, complete messages and fragments are handled by
1279c3c17166SGeorge Shepherd  * this function. Unless the U-bit is set in the data chunk it will be
1280c3c17166SGeorge Shepherd  * delivered in order or queued until an in-order delivery can be made.
1281c3c17166SGeorge Shepherd  */
12827c478bd9Sstevel@tonic-gate static void
12837c478bd9Sstevel@tonic-gate sctp_data_chunk(sctp_t *sctp, sctp_chunk_hdr_t *ch, mblk_t *mp, mblk_t **dups,
1284bd670b35SErik Nordmark     sctp_faddr_t *fp, ip_pkt_t *ipp, ip_recv_attr_t *ira)
12857c478bd9Sstevel@tonic-gate {
12867c478bd9Sstevel@tonic-gate 	sctp_data_hdr_t *dc;
12877c478bd9Sstevel@tonic-gate 	mblk_t *dmp, *pmp;
12887c478bd9Sstevel@tonic-gate 	sctp_instr_t *instr;
12897c478bd9Sstevel@tonic-gate 	int ubit;
1290c3c17166SGeorge Shepherd 	int sid;
12917c478bd9Sstevel@tonic-gate 	int isfrag;
12927c478bd9Sstevel@tonic-gate 	uint16_t ssn;
12937c478bd9Sstevel@tonic-gate 	uint32_t oftsn;
12947c478bd9Sstevel@tonic-gate 	boolean_t can_deliver = B_TRUE;
12957c478bd9Sstevel@tonic-gate 	uint32_t tsn;
12967c478bd9Sstevel@tonic-gate 	int dlen;
12977d546a59Svi117747 	boolean_t tpfinished = B_TRUE;
1298f4b3ec61Sdh155122 	sctp_stack_t	*sctps = sctp->sctp_sctps;
12990f1702c5SYu Xiangning 	int	error;
13007c478bd9Sstevel@tonic-gate 
13017c478bd9Sstevel@tonic-gate 	/* The following are used multiple times, so we inline them */
13027c478bd9Sstevel@tonic-gate #define	SCTP_ACK_IT(sctp, tsn)						\
13037c478bd9Sstevel@tonic-gate 	if (tsn == sctp->sctp_ftsn) {					\
13047c478bd9Sstevel@tonic-gate 		dprint(2, ("data_chunk: acking next %x\n", tsn));	\
1305769b977dSvi117747 		(sctp)->sctp_ftsn++;					\
1306769b977dSvi117747 		if ((sctp)->sctp_sack_gaps > 0)				\
1307769b977dSvi117747 			(sctp)->sctp_force_sack = 1;			\
13087c478bd9Sstevel@tonic-gate 	} else if (SEQ_GT(tsn, sctp->sctp_ftsn)) {			\
13097c478bd9Sstevel@tonic-gate 		/* Got a gap; record it */				\
13109f13099eSGeorge Shepherd 		BUMP_LOCAL(sctp->sctp_outseqtsns);			\
13117c478bd9Sstevel@tonic-gate 		dprint(2, ("data_chunk: acking gap %x\n", tsn));	\
1312769b977dSvi117747 		sctp_ack_add(&sctp->sctp_sack_info, tsn,		\
13137c478bd9Sstevel@tonic-gate 		    &sctp->sctp_sack_gaps);				\
13147c478bd9Sstevel@tonic-gate 		sctp->sctp_force_sack = 1;				\
13157c478bd9Sstevel@tonic-gate 	}
13167c478bd9Sstevel@tonic-gate 
13177c478bd9Sstevel@tonic-gate 	dmp = NULL;
13187c478bd9Sstevel@tonic-gate 
13197c478bd9Sstevel@tonic-gate 	dc = (sctp_data_hdr_t *)ch;
13207c478bd9Sstevel@tonic-gate 	tsn = ntohl(dc->sdh_tsn);
13217c478bd9Sstevel@tonic-gate 
132245916cd2Sjpk 	dprint(3, ("sctp_data_chunk: mp=%p tsn=%x\n", (void *)mp, tsn));
13237c478bd9Sstevel@tonic-gate 
13247c478bd9Sstevel@tonic-gate 	/* Check for duplicates */
13257c478bd9Sstevel@tonic-gate 	if (SEQ_LT(tsn, sctp->sctp_ftsn)) {
13267c478bd9Sstevel@tonic-gate 		dprint(4, ("sctp_data_chunk: dropping duplicate\n"));
13279f13099eSGeorge Shepherd 		BUMP_LOCAL(sctp->sctp_idupchunks);
13287c478bd9Sstevel@tonic-gate 		sctp->sctp_force_sack = 1;
13297c478bd9Sstevel@tonic-gate 		sctp_add_dup(dc->sdh_tsn, dups);
13307c478bd9Sstevel@tonic-gate 		return;
13317c478bd9Sstevel@tonic-gate 	}
13327c478bd9Sstevel@tonic-gate 
1333c3c17166SGeorge Shepherd 	/* Check for dups of sack'ed data */
13347c478bd9Sstevel@tonic-gate 	if (sctp->sctp_sack_info != NULL) {
13357c478bd9Sstevel@tonic-gate 		sctp_set_t *sp;
13367c478bd9Sstevel@tonic-gate 
13377c478bd9Sstevel@tonic-gate 		for (sp = sctp->sctp_sack_info; sp; sp = sp->next) {
13387c478bd9Sstevel@tonic-gate 			if (SEQ_GEQ(tsn, sp->begin) && SEQ_LEQ(tsn, sp->end)) {
13397c478bd9Sstevel@tonic-gate 				dprint(4,
13407f093707Skcpoon 				    ("sctp_data_chunk: dropping dup > "
13417f093707Skcpoon 				    "cumtsn\n"));
13429f13099eSGeorge Shepherd 				BUMP_LOCAL(sctp->sctp_idupchunks);
13437c478bd9Sstevel@tonic-gate 				sctp->sctp_force_sack = 1;
13447c478bd9Sstevel@tonic-gate 				sctp_add_dup(dc->sdh_tsn, dups);
13457c478bd9Sstevel@tonic-gate 				return;
13467c478bd9Sstevel@tonic-gate 			}
13477c478bd9Sstevel@tonic-gate 		}
13487c478bd9Sstevel@tonic-gate 	}
13497c478bd9Sstevel@tonic-gate 
1350c3c17166SGeorge Shepherd 	/* We can no longer deliver anything up, but still need to handle it. */
13517c478bd9Sstevel@tonic-gate 	if (SCTP_IS_DETACHED(sctp)) {
13525dd46ab5SKacheong Poon 		SCTPS_BUMP_MIB(sctps, sctpInClosed);
13537c478bd9Sstevel@tonic-gate 		can_deliver = B_FALSE;
13547c478bd9Sstevel@tonic-gate 	}
13557c478bd9Sstevel@tonic-gate 
13567c478bd9Sstevel@tonic-gate 	dlen = ntohs(dc->sdh_len) - sizeof (*dc);
13577c478bd9Sstevel@tonic-gate 
1358f0c3911fSGeorge Shepherd 	/*
1359f0c3911fSGeorge Shepherd 	 * Check for buffer space. Note if this is the next expected TSN
1360f0c3911fSGeorge Shepherd 	 * we have to take it to avoid deadlock because we cannot deliver
1361f0c3911fSGeorge Shepherd 	 * later queued TSNs and thus clear buffer space without it.
1362f0c3911fSGeorge Shepherd 	 * We drop anything that is purely zero window probe data here.
1363f0c3911fSGeorge Shepherd 	 */
1364f0c3911fSGeorge Shepherd 	if ((sctp->sctp_rwnd - sctp->sctp_rxqueued < dlen) &&
1365f0c3911fSGeorge Shepherd 	    (tsn != sctp->sctp_ftsn || sctp->sctp_rwnd == 0)) {
13667c478bd9Sstevel@tonic-gate 		/* Drop and SACK, but don't advance the cumulative TSN. */
13677c478bd9Sstevel@tonic-gate 		sctp->sctp_force_sack = 1;
13687c478bd9Sstevel@tonic-gate 		dprint(0, ("sctp_data_chunk: exceed rwnd %d rxqueued %d "
136912f47623Skcpoon 		    "dlen %d ssn %d tsn %x\n", sctp->sctp_rwnd,
137012f47623Skcpoon 		    sctp->sctp_rxqueued, dlen, ntohs(dc->sdh_ssn),
137112f47623Skcpoon 		    ntohl(dc->sdh_tsn)));
13727c478bd9Sstevel@tonic-gate 		return;
13737c478bd9Sstevel@tonic-gate 	}
13747c478bd9Sstevel@tonic-gate 
1375c3c17166SGeorge Shepherd 	sid = ntohs(dc->sdh_sid);
1376c3c17166SGeorge Shepherd 
1377c3c17166SGeorge Shepherd 	/* Data received for a stream not negotiated for this association */
1378c3c17166SGeorge Shepherd 	if (sid >= sctp->sctp_num_istr) {
137947b33325SGeorge Shepherd 		sctp_bsc_t	inval_parm;
13807c478bd9Sstevel@tonic-gate 
138147b33325SGeorge Shepherd 		/* Will populate the CAUSE block in the ERROR chunk. */
138247b33325SGeorge Shepherd 		inval_parm.bsc_sid = dc->sdh_sid;
138347b33325SGeorge Shepherd 		/* RESERVED, ignored at the receiving end */
138447b33325SGeorge Shepherd 		inval_parm.bsc_pad = 0;
138547b33325SGeorge Shepherd 
13867c478bd9Sstevel@tonic-gate 		/* ack and drop it */
138747b33325SGeorge Shepherd 		sctp_add_err(sctp, SCTP_ERR_BAD_SID, (void *)&inval_parm,
138847b33325SGeorge Shepherd 		    sizeof (sctp_bsc_t), fp);
13897c478bd9Sstevel@tonic-gate 		SCTP_ACK_IT(sctp, tsn);
13907c478bd9Sstevel@tonic-gate 		return;
13917c478bd9Sstevel@tonic-gate 	}
13927c478bd9Sstevel@tonic-gate 
1393c3c17166SGeorge Shepherd 	/* unordered delivery OK for this data if ubit set */
13947c478bd9Sstevel@tonic-gate 	ubit = SCTP_DATA_GET_UBIT(dc);
13957c478bd9Sstevel@tonic-gate 	ASSERT(sctp->sctp_instr != NULL);
1396c3c17166SGeorge Shepherd 
1397c3c17166SGeorge Shepherd 	/* select per stream structure for this stream from the array */
1398c3c17166SGeorge Shepherd 	instr = &sctp->sctp_instr[sid];
13997c478bd9Sstevel@tonic-gate 	/* Initialize the stream, if not yet used */
14007c478bd9Sstevel@tonic-gate 	if (instr->sctp == NULL)
14017c478bd9Sstevel@tonic-gate 		instr->sctp = sctp;
14027d546a59Svi117747 
1403c3c17166SGeorge Shepherd 	/* Begin and End bit set would mean a complete message */
14047c478bd9Sstevel@tonic-gate 	isfrag = !(SCTP_DATA_GET_BBIT(dc) && SCTP_DATA_GET_EBIT(dc));
1405c3c17166SGeorge Shepherd 
1406c3c17166SGeorge Shepherd 	/* The ssn of this sctp message and of any fragments in it */
14077c478bd9Sstevel@tonic-gate 	ssn = ntohs(dc->sdh_ssn);
14087c478bd9Sstevel@tonic-gate 
14097c478bd9Sstevel@tonic-gate 	dmp = dupb(mp);
14107c478bd9Sstevel@tonic-gate 	if (dmp == NULL) {
1411c3c17166SGeorge Shepherd 		/* drop it and don't ack, let the peer retransmit */
14127c478bd9Sstevel@tonic-gate 		return;
14137c478bd9Sstevel@tonic-gate 	}
1414c3c17166SGeorge Shepherd 	/*
1415c3c17166SGeorge Shepherd 	 * Past header and payload, note: the underlying buffer may
1416c3c17166SGeorge Shepherd 	 * contain further chunks from the same incoming IP packet,
1417c3c17166SGeorge Shepherd 	 * if so db_ref will be greater than one.
1418c3c17166SGeorge Shepherd 	 */
14197c478bd9Sstevel@tonic-gate 	dmp->b_wptr = (uchar_t *)ch + ntohs(ch->sch_len);
14207c478bd9Sstevel@tonic-gate 
14217c478bd9Sstevel@tonic-gate 	sctp->sctp_rxqueued += dlen;
14227c478bd9Sstevel@tonic-gate 
14237c478bd9Sstevel@tonic-gate 	oftsn = sctp->sctp_ftsn;
14247c478bd9Sstevel@tonic-gate 
14257c478bd9Sstevel@tonic-gate 	if (isfrag) {
14267c478bd9Sstevel@tonic-gate 
14270f1702c5SYu Xiangning 		error = 0;
14287c478bd9Sstevel@tonic-gate 		/* fragmented data chunk */
14297c478bd9Sstevel@tonic-gate 		dmp->b_rptr = (uchar_t *)dc;
14307c478bd9Sstevel@tonic-gate 		if (ubit) {
1431c3c17166SGeorge Shepherd 			/* prepare data for unordered delivery */
14327c478bd9Sstevel@tonic-gate 			dmp = sctp_uodata_frag(sctp, dmp, &dc);
14337c478bd9Sstevel@tonic-gate #if	DEBUG
14347c478bd9Sstevel@tonic-gate 			if (dmp != NULL) {
14357c478bd9Sstevel@tonic-gate 				ASSERT(instr ==
1436c3c17166SGeorge Shepherd 				    &sctp->sctp_instr[sid]);
14377c478bd9Sstevel@tonic-gate 			}
14387c478bd9Sstevel@tonic-gate #endif
14397c478bd9Sstevel@tonic-gate 		} else {
1440c3c17166SGeorge Shepherd 			/*
1441c3c17166SGeorge Shepherd 			 * Assemble fragments and queue for ordered delivery,
1442c3c17166SGeorge Shepherd 			 * dmp returned is NULL or the head of a complete or
1443c3c17166SGeorge Shepherd 			 * "partial delivery" message. Any returned message
1444c3c17166SGeorge Shepherd 			 * and all its fragments will have the same ssn as the
1445c3c17166SGeorge Shepherd 			 * input fragment currently being handled.
1446c3c17166SGeorge Shepherd 			 */
14477c478bd9Sstevel@tonic-gate 			dmp = sctp_data_frag(sctp, dmp, &dc, &error, instr,
14487d546a59Svi117747 			    &tpfinished);
14497c478bd9Sstevel@tonic-gate 		}
1450c3c17166SGeorge Shepherd 		if (error == ENOMEM) {
1451c3c17166SGeorge Shepherd 			/* back out the adjustment made earlier */
14527c478bd9Sstevel@tonic-gate 			sctp->sctp_rxqueued -= dlen;
14537c478bd9Sstevel@tonic-gate 			/*
1454c3c17166SGeorge Shepherd 			 * Don't ack the segment,
1455c3c17166SGeorge Shepherd 			 * the peer will retransmit.
14567c478bd9Sstevel@tonic-gate 			 */
14577c478bd9Sstevel@tonic-gate 			return;
14587c478bd9Sstevel@tonic-gate 		}
14597c478bd9Sstevel@tonic-gate 
14607c478bd9Sstevel@tonic-gate 		if (dmp == NULL) {
14617c478bd9Sstevel@tonic-gate 			/*
1462c3c17166SGeorge Shepherd 			 * The frag has been queued for later in-order delivery,
1463c3c17166SGeorge Shepherd 			 * but the cumulative TSN may need to advance, so also
1464c3c17166SGeorge Shepherd 			 * need to perform the gap ack checks at the done label.
14657c478bd9Sstevel@tonic-gate 			 */
14667c478bd9Sstevel@tonic-gate 			SCTP_ACK_IT(sctp, tsn);
1467c3c17166SGeorge Shepherd 			DTRACE_PROBE4(sctp_data_frag_queued, sctp_t *, sctp,
1468c3c17166SGeorge Shepherd 			    int, sid, int, tsn, uint16_t, ssn);
14697c478bd9Sstevel@tonic-gate 			goto done;
14707c478bd9Sstevel@tonic-gate 		}
14717c478bd9Sstevel@tonic-gate 	}
14727c478bd9Sstevel@tonic-gate 
14738042ac43Sgeorges 	/*
1474c3c17166SGeorge Shepherd 	 * Unless message is the next for delivery to the ulp, queue complete
1475c3c17166SGeorge Shepherd 	 * message in the correct order for ordered delivery.
1476c3c17166SGeorge Shepherd 	 * Note: tpfinished is true when the incoming chunk contains a complete
14778042ac43Sgeorges 	 * message or is the final missing fragment which completed a message.
14788042ac43Sgeorges 	 */
14798042ac43Sgeorges 	if (!ubit && tpfinished && ssn != instr->nextseq) {
14807c478bd9Sstevel@tonic-gate 		/* Adjust rptr to point at the data chunk for compares */
14817c478bd9Sstevel@tonic-gate 		dmp->b_rptr = (uchar_t *)dc;
14827c478bd9Sstevel@tonic-gate 
14837c478bd9Sstevel@tonic-gate 		dprint(2,
14847c478bd9Sstevel@tonic-gate 		    ("data_chunk: inserted %x in pq (ssn %d expected %d)\n",
14857c478bd9Sstevel@tonic-gate 		    ntohl(dc->sdh_tsn), (int)(ssn), (int)(instr->nextseq)));
14867c478bd9Sstevel@tonic-gate 
14877c478bd9Sstevel@tonic-gate 		if (instr->istr_msgs == NULL) {
14887c478bd9Sstevel@tonic-gate 			instr->istr_msgs = dmp;
14897c478bd9Sstevel@tonic-gate 			ASSERT(dmp->b_prev == NULL && dmp->b_next == NULL);
14907c478bd9Sstevel@tonic-gate 		} else {
14917c478bd9Sstevel@tonic-gate 			mblk_t			*imblk = instr->istr_msgs;
14927c478bd9Sstevel@tonic-gate 			sctp_data_hdr_t		*idc;
14937c478bd9Sstevel@tonic-gate 
14947c478bd9Sstevel@tonic-gate 			/*
14957c478bd9Sstevel@tonic-gate 			 * XXXNeed to take sequence wraps into account,
14967c478bd9Sstevel@tonic-gate 			 * ... and a more efficient insertion algo.
14977c478bd9Sstevel@tonic-gate 			 */
14987c478bd9Sstevel@tonic-gate 			for (;;) {
14997c478bd9Sstevel@tonic-gate 				idc = (sctp_data_hdr_t *)imblk->b_rptr;
15007c478bd9Sstevel@tonic-gate 				if (SSN_GT(ntohs(idc->sdh_ssn),
15017c478bd9Sstevel@tonic-gate 				    ntohs(dc->sdh_ssn))) {
15027c478bd9Sstevel@tonic-gate 					if (instr->istr_msgs == imblk) {
15037c478bd9Sstevel@tonic-gate 						instr->istr_msgs = dmp;
15047c478bd9Sstevel@tonic-gate 						dmp->b_next = imblk;
15057c478bd9Sstevel@tonic-gate 						imblk->b_prev = dmp;
15067c478bd9Sstevel@tonic-gate 					} else {
15077c478bd9Sstevel@tonic-gate 						ASSERT(imblk->b_prev != NULL);
15087c478bd9Sstevel@tonic-gate 						imblk->b_prev->b_next = dmp;
15097c478bd9Sstevel@tonic-gate 						dmp->b_prev = imblk->b_prev;
15107c478bd9Sstevel@tonic-gate 						imblk->b_prev = dmp;
15117c478bd9Sstevel@tonic-gate 						dmp->b_next = imblk;
15127c478bd9Sstevel@tonic-gate 					}
15137c478bd9Sstevel@tonic-gate 					break;
15147c478bd9Sstevel@tonic-gate 				}
15157c478bd9Sstevel@tonic-gate 				if (imblk->b_next == NULL) {
15167c478bd9Sstevel@tonic-gate 					imblk->b_next = dmp;
15177c478bd9Sstevel@tonic-gate 					dmp->b_prev = imblk;
15187c478bd9Sstevel@tonic-gate 					break;
15197c478bd9Sstevel@tonic-gate 				}
15207c478bd9Sstevel@tonic-gate 				imblk = imblk->b_next;
15217c478bd9Sstevel@tonic-gate 			}
15227c478bd9Sstevel@tonic-gate 		}
15237c478bd9Sstevel@tonic-gate 		(instr->istr_nmsgs)++;
15247c478bd9Sstevel@tonic-gate 		(sctp->sctp_istr_nmsgs)++;
15257c478bd9Sstevel@tonic-gate 		SCTP_ACK_IT(sctp, tsn);
1526c3c17166SGeorge Shepherd 		DTRACE_PROBE4(sctp_pqueue_completemsg, sctp_t *, sctp,
1527c3c17166SGeorge Shepherd 		    int, sid, int, tsn, uint16_t, ssn);
15287c478bd9Sstevel@tonic-gate 		return;
15297c478bd9Sstevel@tonic-gate 	}
15307c478bd9Sstevel@tonic-gate 
15317c478bd9Sstevel@tonic-gate 	/*
1532c3c17166SGeorge Shepherd 	 * Deliver the data directly. Recalculate dlen now since
1533c3c17166SGeorge Shepherd 	 * we may have just reassembled this data.
15347c478bd9Sstevel@tonic-gate 	 */
15357c478bd9Sstevel@tonic-gate 	dlen = dmp->b_wptr - (uchar_t *)dc - sizeof (*dc);
15367c478bd9Sstevel@tonic-gate 	for (pmp = dmp->b_cont; pmp != NULL; pmp = pmp->b_cont)
1537f0c3911fSGeorge Shepherd 		dlen += MBLKL(pmp);
15387c478bd9Sstevel@tonic-gate 	ASSERT(sctp->sctp_rxqueued >= dlen);
15397c478bd9Sstevel@tonic-gate 
15407c478bd9Sstevel@tonic-gate 	/* Deliver the message. */
15417c478bd9Sstevel@tonic-gate 	sctp->sctp_rxqueued -= dlen;
15427c478bd9Sstevel@tonic-gate 
15437c478bd9Sstevel@tonic-gate 	if (can_deliver) {
1544c3c17166SGeorge Shepherd 		/* step past header to the payload */
15457c478bd9Sstevel@tonic-gate 		dmp->b_rptr = (uchar_t *)(dc + 1);
1546bd670b35SErik Nordmark 		if (sctp_input_add_ancillary(sctp, &dmp, dc, fp,
1547bd670b35SErik Nordmark 		    ipp, ira) == 0) {
15487c478bd9Sstevel@tonic-gate 			dprint(1, ("sctp_data_chunk: delivering %lu bytes\n",
15497c478bd9Sstevel@tonic-gate 			    msgdsize(dmp)));
15500f1702c5SYu Xiangning 			/*
1551c3c17166SGeorge Shepherd 			 * We overload the meaning of b_flag for SCTP sockfs
1552c3c17166SGeorge Shepherd 			 * internal use, to advise sockfs of partial delivery
1553c3c17166SGeorge Shepherd 			 * semantics.
15540f1702c5SYu Xiangning 			 */
15550f1702c5SYu Xiangning 			dmp->b_flag = tpfinished ? 0 : SCTP_PARTIAL_DATA;
1556*a215d4ebSKacheong Poon 			if (sctp->sctp_flowctrld) {
1557*a215d4ebSKacheong Poon 				sctp->sctp_rwnd -= dlen;
1558*a215d4ebSKacheong Poon 				if (sctp->sctp_rwnd < 0)
1559f0c3911fSGeorge Shepherd 					sctp->sctp_rwnd = 0;
1560*a215d4ebSKacheong Poon 			}
1561*a215d4ebSKacheong Poon 			if (sctp->sctp_ulp_recv(sctp->sctp_ulpd, dmp,
1562*a215d4ebSKacheong Poon 			    msgdsize(dmp), 0, &error, NULL) <= 0) {
1563*a215d4ebSKacheong Poon 				sctp->sctp_flowctrld = B_TRUE;
1564*a215d4ebSKacheong Poon 			}
15657c478bd9Sstevel@tonic-gate 			SCTP_ACK_IT(sctp, tsn);
15667c478bd9Sstevel@tonic-gate 		} else {
1567c3c17166SGeorge Shepherd 			/* No memory don't ack, the peer will retransmit. */
15687c478bd9Sstevel@tonic-gate 			freemsg(dmp);
15697c478bd9Sstevel@tonic-gate 			return;
15707c478bd9Sstevel@tonic-gate 		}
15717c478bd9Sstevel@tonic-gate 	} else {
1572c3c17166SGeorge Shepherd 		/* Closed above, ack to peer and free the data */
15737c478bd9Sstevel@tonic-gate 		freemsg(dmp);
15747c478bd9Sstevel@tonic-gate 		SCTP_ACK_IT(sctp, tsn);
15757c478bd9Sstevel@tonic-gate 	}
15767c478bd9Sstevel@tonic-gate 
15777c478bd9Sstevel@tonic-gate 	/*
1578c3c17166SGeorge Shepherd 	 * Data now enqueued, may already have been processed and free'd
15797c478bd9Sstevel@tonic-gate 	 * by the ULP (or we may have just freed it above, if we could not
1580c3c17166SGeorge Shepherd 	 * deliver), so we must not reference it (this is why we saved the
1581c3c17166SGeorge Shepherd 	 * ssn and ubit earlier).
15827c478bd9Sstevel@tonic-gate 	 */
15837c478bd9Sstevel@tonic-gate 	if (ubit != 0) {
15847c478bd9Sstevel@tonic-gate 		BUMP_LOCAL(sctp->sctp_iudchunks);
15857c478bd9Sstevel@tonic-gate 		goto done;
15867c478bd9Sstevel@tonic-gate 	}
15877c478bd9Sstevel@tonic-gate 	BUMP_LOCAL(sctp->sctp_idchunks);
15887c478bd9Sstevel@tonic-gate 
15897c478bd9Sstevel@tonic-gate 	/*
1590c3c17166SGeorge Shepherd 	 * There was a partial delivery and it has not finished,
1591c3c17166SGeorge Shepherd 	 * don't pull anything from the pqueues or increment the
1592c3c17166SGeorge Shepherd 	 * nextseq. This msg must complete before starting on
1593c3c17166SGeorge Shepherd 	 * the next ssn and the partial message must have the
1594c3c17166SGeorge Shepherd 	 * same ssn as the next expected message..
15957c478bd9Sstevel@tonic-gate 	 */
15967c478bd9Sstevel@tonic-gate 	if (!tpfinished) {
1597c3c17166SGeorge Shepherd 		DTRACE_PROBE4(sctp_partial_delivery, sctp_t *, sctp,
1598c3c17166SGeorge Shepherd 		    int, sid, int, tsn, uint16_t, ssn);
1599c3c17166SGeorge Shepherd 		/*
1600c3c17166SGeorge Shepherd 		 * Verify the partial delivery is part of the
1601c3c17166SGeorge Shepherd 		 * message expected for ordered delivery.
1602c3c17166SGeorge Shepherd 		 */
1603c3c17166SGeorge Shepherd 		if (ssn != instr->nextseq) {
1604c3c17166SGeorge Shepherd 			DTRACE_PROBE4(sctp_partial_delivery_error,
1605c3c17166SGeorge Shepherd 			    sctp_t *, sctp, int, sid, int, tsn,
1606c3c17166SGeorge Shepherd 			    uint16_t, ssn);
1607c3c17166SGeorge Shepherd 			cmn_err(CE_WARN, "sctp partial"
1608c3c17166SGeorge Shepherd 			    " delivery error, sctp 0x%p"
1609c3c17166SGeorge Shepherd 			    " sid = 0x%x ssn != nextseq"
1610c3c17166SGeorge Shepherd 			    " tsn 0x%x ftsn 0x%x"
1611c3c17166SGeorge Shepherd 			    " ssn 0x%x nextseq 0x%x",
1612c3c17166SGeorge Shepherd 			    (void *)sctp, sid,
1613c3c17166SGeorge Shepherd 			    tsn, sctp->sctp_ftsn, ssn,
1614c3c17166SGeorge Shepherd 			    instr->nextseq);
1615c3c17166SGeorge Shepherd 		}
1616c3c17166SGeorge Shepherd 
1617c3c17166SGeorge Shepherd 		ASSERT(ssn == instr->nextseq);
16187c478bd9Sstevel@tonic-gate 		goto done;
16197c478bd9Sstevel@tonic-gate 	}
16207c478bd9Sstevel@tonic-gate 
1621c3c17166SGeorge Shepherd 	if (ssn != instr->nextseq) {
1622c3c17166SGeorge Shepherd 		DTRACE_PROBE4(sctp_inorder_delivery_error,
1623c3c17166SGeorge Shepherd 		    sctp_t *, sctp, int, sid, int, tsn,
1624c3c17166SGeorge Shepherd 		    uint16_t, ssn);
1625c3c17166SGeorge Shepherd 		cmn_err(CE_WARN, "sctp in-order delivery error, sctp 0x%p "
1626c3c17166SGeorge Shepherd 		    "sid = 0x%x ssn != nextseq ssn 0x%x nextseq 0x%x",
1627c3c17166SGeorge Shepherd 		    (void *)sctp, sid, ssn, instr->nextseq);
1628c3c17166SGeorge Shepherd 	}
1629c3c17166SGeorge Shepherd 
1630c3c17166SGeorge Shepherd 	ASSERT(ssn == instr->nextseq);
1631c3c17166SGeorge Shepherd 
1632c3c17166SGeorge Shepherd 	DTRACE_PROBE4(sctp_deliver_completemsg, sctp_t *, sctp, int, sid,
1633c3c17166SGeorge Shepherd 	    int, tsn, uint16_t, ssn);
1634c3c17166SGeorge Shepherd 
16357c478bd9Sstevel@tonic-gate 	instr->nextseq = ssn + 1;
1636c3c17166SGeorge Shepherd 
1637c3c17166SGeorge Shepherd 	/*
1638c3c17166SGeorge Shepherd 	 * Deliver any successive data chunks waiting in the instr pqueue
1639c3c17166SGeorge Shepherd 	 * for the data just sent up.
1640c3c17166SGeorge Shepherd 	 */
16417c478bd9Sstevel@tonic-gate 	while (instr->istr_nmsgs > 0) {
16427c478bd9Sstevel@tonic-gate 		dmp = (mblk_t *)instr->istr_msgs;
16437c478bd9Sstevel@tonic-gate 		dc = (sctp_data_hdr_t *)dmp->b_rptr;
16447c478bd9Sstevel@tonic-gate 		ssn = ntohs(dc->sdh_ssn);
1645c3c17166SGeorge Shepherd 		tsn = ntohl(dc->sdh_tsn);
1646c3c17166SGeorge Shepherd 		/* Stop at the first gap in the sequence */
16477c478bd9Sstevel@tonic-gate 		if (ssn != instr->nextseq)
16487c478bd9Sstevel@tonic-gate 			break;
16497c478bd9Sstevel@tonic-gate 
1650c3c17166SGeorge Shepherd 		DTRACE_PROBE4(sctp_deliver_pqueuedmsg, sctp_t *, sctp,
1651c3c17166SGeorge Shepherd 		    int, sid, int, tsn, uint16_t, ssn);
1652c3c17166SGeorge Shepherd 		/*
1653c3c17166SGeorge Shepherd 		 * Ready to deliver all data before the gap
1654c3c17166SGeorge Shepherd 		 * to the upper layer.
1655c3c17166SGeorge Shepherd 		 */
16567c478bd9Sstevel@tonic-gate 		(instr->istr_nmsgs)--;
16577c478bd9Sstevel@tonic-gate 		(instr->nextseq)++;
16587c478bd9Sstevel@tonic-gate 		(sctp->sctp_istr_nmsgs)--;
16597c478bd9Sstevel@tonic-gate 
16607c478bd9Sstevel@tonic-gate 		instr->istr_msgs = instr->istr_msgs->b_next;
16617c478bd9Sstevel@tonic-gate 		if (instr->istr_msgs != NULL)
16627c478bd9Sstevel@tonic-gate 			instr->istr_msgs->b_prev = NULL;
16637c478bd9Sstevel@tonic-gate 		dmp->b_next = dmp->b_prev = NULL;
16647c478bd9Sstevel@tonic-gate 
16657c478bd9Sstevel@tonic-gate 		dprint(2, ("data_chunk: pulling %x from pq (ssn %d)\n",
16667c478bd9Sstevel@tonic-gate 		    ntohl(dc->sdh_tsn), (int)ssn));
16677c478bd9Sstevel@tonic-gate 
16687c478bd9Sstevel@tonic-gate 		/*
1669c3c17166SGeorge Shepherd 		 * Composite messages indicate this chunk was reassembled,
1670c3c17166SGeorge Shepherd 		 * each b_cont represents another TSN; Follow the chain to
1671c3c17166SGeorge Shepherd 		 * reach the frag with the last tsn in order to advance ftsn
1672c3c17166SGeorge Shepherd 		 * shortly by calling SCTP_ACK_IT().
16737c478bd9Sstevel@tonic-gate 		 */
16747c478bd9Sstevel@tonic-gate 		dlen = dmp->b_wptr - dmp->b_rptr - sizeof (*dc);
16757c478bd9Sstevel@tonic-gate 		for (pmp = dmp->b_cont; pmp; pmp = pmp->b_cont)
1676f0c3911fSGeorge Shepherd 			dlen += MBLKL(pmp);
16777c478bd9Sstevel@tonic-gate 
16787c478bd9Sstevel@tonic-gate 		ASSERT(sctp->sctp_rxqueued >= dlen);
16797c478bd9Sstevel@tonic-gate 
16807c478bd9Sstevel@tonic-gate 		sctp->sctp_rxqueued -= dlen;
16817c478bd9Sstevel@tonic-gate 		if (can_deliver) {
16827c478bd9Sstevel@tonic-gate 			dmp->b_rptr = (uchar_t *)(dc + 1);
16837c478bd9Sstevel@tonic-gate 			if (sctp_input_add_ancillary(sctp, &dmp, dc, fp,
1684bd670b35SErik Nordmark 			    ipp, ira) == 0) {
16857c478bd9Sstevel@tonic-gate 				dprint(1, ("sctp_data_chunk: delivering %lu "
16867c478bd9Sstevel@tonic-gate 				    "bytes\n", msgdsize(dmp)));
16870f1702c5SYu Xiangning 				/*
1688c3c17166SGeorge Shepherd 				 * Meaning of b_flag overloaded for SCTP sockfs
1689c3c17166SGeorge Shepherd 				 * internal use, advise sockfs of partial
1690c3c17166SGeorge Shepherd 				 * delivery semantics.
16910f1702c5SYu Xiangning 				 */
16920f1702c5SYu Xiangning 				dmp->b_flag = tpfinished ?
16930f1702c5SYu Xiangning 				    0 : SCTP_PARTIAL_DATA;
1694*a215d4ebSKacheong Poon 				if (sctp->sctp_flowctrld) {
1695*a215d4ebSKacheong Poon 					sctp->sctp_rwnd -= dlen;
1696*a215d4ebSKacheong Poon 					if (sctp->sctp_rwnd < 0)
1697f0c3911fSGeorge Shepherd 						sctp->sctp_rwnd = 0;
1698*a215d4ebSKacheong Poon 				}
1699*a215d4ebSKacheong Poon 				if (sctp->sctp_ulp_recv(sctp->sctp_ulpd, dmp,
1700*a215d4ebSKacheong Poon 				    msgdsize(dmp), 0, &error, NULL) <= 0) {
1701*a215d4ebSKacheong Poon 					sctp->sctp_flowctrld = B_TRUE;
1702*a215d4ebSKacheong Poon 				}
17037c478bd9Sstevel@tonic-gate 				SCTP_ACK_IT(sctp, tsn);
17047c478bd9Sstevel@tonic-gate 			} else {
1705c3c17166SGeorge Shepherd 				/* don't ack, the peer will retransmit */
17067c478bd9Sstevel@tonic-gate 				freemsg(dmp);
17077c478bd9Sstevel@tonic-gate 				return;
17087c478bd9Sstevel@tonic-gate 			}
17097c478bd9Sstevel@tonic-gate 		} else {
1710c3c17166SGeorge Shepherd 			/* Closed above, ack and free the data */
17117c478bd9Sstevel@tonic-gate 			freemsg(dmp);
17127c478bd9Sstevel@tonic-gate 			SCTP_ACK_IT(sctp, tsn);
17137c478bd9Sstevel@tonic-gate 		}
17147c478bd9Sstevel@tonic-gate 	}
17157c478bd9Sstevel@tonic-gate 
17167c478bd9Sstevel@tonic-gate done:
17177c478bd9Sstevel@tonic-gate 
17187c478bd9Sstevel@tonic-gate 	/*
17197c478bd9Sstevel@tonic-gate 	 * If there are gap reports pending, check if advancing
17207c478bd9Sstevel@tonic-gate 	 * the ftsn here closes a gap. If so, we can advance
17217c478bd9Sstevel@tonic-gate 	 * ftsn to the end of the set.
17227c478bd9Sstevel@tonic-gate 	 */
17237c478bd9Sstevel@tonic-gate 	if (sctp->sctp_sack_info != NULL &&
17247c478bd9Sstevel@tonic-gate 	    sctp->sctp_ftsn == sctp->sctp_sack_info->begin) {
17257c478bd9Sstevel@tonic-gate 		sctp->sctp_ftsn = sctp->sctp_sack_info->end + 1;
17267c478bd9Sstevel@tonic-gate 	}
17277c478bd9Sstevel@tonic-gate 	/*
17287c478bd9Sstevel@tonic-gate 	 * If ftsn has moved forward, maybe we can remove gap reports.
17297c478bd9Sstevel@tonic-gate 	 * NB: dmp may now be NULL, so don't dereference it here.
17307c478bd9Sstevel@tonic-gate 	 */
17317c478bd9Sstevel@tonic-gate 	if (oftsn != sctp->sctp_ftsn && sctp->sctp_sack_info != NULL) {
17327c478bd9Sstevel@tonic-gate 		sctp_ack_rem(&sctp->sctp_sack_info, sctp->sctp_ftsn - 1,
17337c478bd9Sstevel@tonic-gate 		    &sctp->sctp_sack_gaps);
17347c478bd9Sstevel@tonic-gate 		dprint(2, ("data_chunk: removed acks before %x (num=%d)\n",
17357c478bd9Sstevel@tonic-gate 		    sctp->sctp_ftsn - 1, sctp->sctp_sack_gaps));
17367c478bd9Sstevel@tonic-gate 	}
17377c478bd9Sstevel@tonic-gate 
17387c478bd9Sstevel@tonic-gate #ifdef	DEBUG
17397c478bd9Sstevel@tonic-gate 	if (sctp->sctp_sack_info != NULL) {
17407c478bd9Sstevel@tonic-gate 		ASSERT(sctp->sctp_ftsn != sctp->sctp_sack_info->begin);
17417c478bd9Sstevel@tonic-gate 	}
17427c478bd9Sstevel@tonic-gate #endif
17437c478bd9Sstevel@tonic-gate 
17447c478bd9Sstevel@tonic-gate #undef	SCTP_ACK_IT
17457c478bd9Sstevel@tonic-gate }
17467c478bd9Sstevel@tonic-gate 
17477c478bd9Sstevel@tonic-gate void
17487c478bd9Sstevel@tonic-gate sctp_fill_sack(sctp_t *sctp, unsigned char *dst, int sacklen)
17497c478bd9Sstevel@tonic-gate {
17507c478bd9Sstevel@tonic-gate 	sctp_chunk_hdr_t *sch;
17517c478bd9Sstevel@tonic-gate 	sctp_sack_chunk_t *sc;
17527c478bd9Sstevel@tonic-gate 	sctp_sack_frag_t *sf;
17537c478bd9Sstevel@tonic-gate 	uint16_t num_gaps = sctp->sctp_sack_gaps;
17547c478bd9Sstevel@tonic-gate 	sctp_set_t *sp;
17557c478bd9Sstevel@tonic-gate 
17567c478bd9Sstevel@tonic-gate 	/* Chunk hdr */
17577c478bd9Sstevel@tonic-gate 	sch = (sctp_chunk_hdr_t *)dst;
17587c478bd9Sstevel@tonic-gate 	sch->sch_id = CHUNK_SACK;
17597c478bd9Sstevel@tonic-gate 	sch->sch_flags = 0;
17607c478bd9Sstevel@tonic-gate 	sch->sch_len = htons(sacklen);
17617c478bd9Sstevel@tonic-gate 
17627c478bd9Sstevel@tonic-gate 	/* SACK chunk */
17637c478bd9Sstevel@tonic-gate 	sctp->sctp_lastacked = sctp->sctp_ftsn - 1;
17647c478bd9Sstevel@tonic-gate 
17657c478bd9Sstevel@tonic-gate 	sc = (sctp_sack_chunk_t *)(sch + 1);
17667c478bd9Sstevel@tonic-gate 	sc->ssc_cumtsn = htonl(sctp->sctp_lastacked);
17677c478bd9Sstevel@tonic-gate 	if (sctp->sctp_rxqueued < sctp->sctp_rwnd) {
17687c478bd9Sstevel@tonic-gate 		sc->ssc_a_rwnd = htonl(sctp->sctp_rwnd - sctp->sctp_rxqueued);
17697c478bd9Sstevel@tonic-gate 	} else {
17707c478bd9Sstevel@tonic-gate 		sc->ssc_a_rwnd = 0;
17717c478bd9Sstevel@tonic-gate 	}
1772*a215d4ebSKacheong Poon 	/* Remember the last window sent to peer. */
1773*a215d4ebSKacheong Poon 	sctp->sctp_arwnd = sc->ssc_a_rwnd;
17747c478bd9Sstevel@tonic-gate 	sc->ssc_numfrags = htons(num_gaps);
17757c478bd9Sstevel@tonic-gate 	sc->ssc_numdups = 0;
17767c478bd9Sstevel@tonic-gate 
17777c478bd9Sstevel@tonic-gate 	/* lay in gap reports */
17787c478bd9Sstevel@tonic-gate 	sf = (sctp_sack_frag_t *)(sc + 1);
17797c478bd9Sstevel@tonic-gate 	for (sp = sctp->sctp_sack_info; sp; sp = sp->next) {
17807c478bd9Sstevel@tonic-gate 		uint16_t offset;
17817c478bd9Sstevel@tonic-gate 
17827c478bd9Sstevel@tonic-gate 		/* start */
17837c478bd9Sstevel@tonic-gate 		if (sp->begin > sctp->sctp_lastacked) {
17847c478bd9Sstevel@tonic-gate 			offset = (uint16_t)(sp->begin - sctp->sctp_lastacked);
17857c478bd9Sstevel@tonic-gate 		} else {
17867c478bd9Sstevel@tonic-gate 			/* sequence number wrap */
17877c478bd9Sstevel@tonic-gate 			offset = (uint16_t)(UINT32_MAX - sctp->sctp_lastacked +
17887c478bd9Sstevel@tonic-gate 			    sp->begin);
17897c478bd9Sstevel@tonic-gate 		}
17907c478bd9Sstevel@tonic-gate 		sf->ssf_start = htons(offset);
17917c478bd9Sstevel@tonic-gate 
17927c478bd9Sstevel@tonic-gate 		/* end */
17937c478bd9Sstevel@tonic-gate 		if (sp->end >= sp->begin) {
17947c478bd9Sstevel@tonic-gate 			offset += (uint16_t)(sp->end - sp->begin);
17957c478bd9Sstevel@tonic-gate 		} else {
17967c478bd9Sstevel@tonic-gate 			/* sequence number wrap */
17977c478bd9Sstevel@tonic-gate 			offset += (uint16_t)(UINT32_MAX - sp->begin + sp->end);
17987c478bd9Sstevel@tonic-gate 		}
17997c478bd9Sstevel@tonic-gate 		sf->ssf_end = htons(offset);
18007c478bd9Sstevel@tonic-gate 
18017c478bd9Sstevel@tonic-gate 		sf++;
18027c478bd9Sstevel@tonic-gate 		/* This is just for debugging (a la the following assertion) */
18037c478bd9Sstevel@tonic-gate 		num_gaps--;
18047c478bd9Sstevel@tonic-gate 	}
18057c478bd9Sstevel@tonic-gate 
18067c478bd9Sstevel@tonic-gate 	ASSERT(num_gaps == 0);
18077c478bd9Sstevel@tonic-gate 
18087c478bd9Sstevel@tonic-gate 	/* If the SACK timer is running, stop it */
18097c478bd9Sstevel@tonic-gate 	if (sctp->sctp_ack_timer_running) {
18107c478bd9Sstevel@tonic-gate 		sctp_timer_stop(sctp->sctp_ack_mp);
18117c478bd9Sstevel@tonic-gate 		sctp->sctp_ack_timer_running = B_FALSE;
18127c478bd9Sstevel@tonic-gate 	}
18137c478bd9Sstevel@tonic-gate 
18147c478bd9Sstevel@tonic-gate 	BUMP_LOCAL(sctp->sctp_obchunks);
18159f13099eSGeorge Shepherd 	BUMP_LOCAL(sctp->sctp_osacks);
18167c478bd9Sstevel@tonic-gate }
18177c478bd9Sstevel@tonic-gate 
18187c478bd9Sstevel@tonic-gate mblk_t *
18197c478bd9Sstevel@tonic-gate sctp_make_sack(sctp_t *sctp, sctp_faddr_t *sendto, mblk_t *dups)
18207c478bd9Sstevel@tonic-gate {
18217c478bd9Sstevel@tonic-gate 	mblk_t *smp;
18227c478bd9Sstevel@tonic-gate 	size_t slen;
18237c478bd9Sstevel@tonic-gate 	sctp_chunk_hdr_t *sch;
18247c478bd9Sstevel@tonic-gate 	sctp_sack_chunk_t *sc;
18251c25cdbdSkcpoon 	int32_t acks_max;
1826f4b3ec61Sdh155122 	sctp_stack_t	*sctps = sctp->sctp_sctps;
18277f093707Skcpoon 	uint32_t	dups_len;
18287f093707Skcpoon 	sctp_faddr_t	*fp;
18297c478bd9Sstevel@tonic-gate 
1830bd670b35SErik Nordmark 	ASSERT(sendto != NULL);
1831bd670b35SErik Nordmark 
18327c478bd9Sstevel@tonic-gate 	if (sctp->sctp_force_sack) {
18337c478bd9Sstevel@tonic-gate 		sctp->sctp_force_sack = 0;
18347c478bd9Sstevel@tonic-gate 		goto checks_done;
18357c478bd9Sstevel@tonic-gate 	}
18367c478bd9Sstevel@tonic-gate 
1837f4b3ec61Sdh155122 	acks_max = sctps->sctps_deferred_acks_max;
18387c478bd9Sstevel@tonic-gate 	if (sctp->sctp_state == SCTPS_ESTABLISHED) {
18391c25cdbdSkcpoon 		if (sctp->sctp_sack_toggle < acks_max) {
18407c478bd9Sstevel@tonic-gate 			/* no need to SACK right now */
18417c478bd9Sstevel@tonic-gate 			dprint(2, ("sctp_make_sack: %p no sack (toggle)\n",
184245916cd2Sjpk 			    (void *)sctp));
18437c478bd9Sstevel@tonic-gate 			return (NULL);
18441c25cdbdSkcpoon 		} else if (sctp->sctp_sack_toggle >= acks_max) {
18457c478bd9Sstevel@tonic-gate 			sctp->sctp_sack_toggle = 0;
18467c478bd9Sstevel@tonic-gate 		}
18477c478bd9Sstevel@tonic-gate 	}
18487c478bd9Sstevel@tonic-gate 
18497c478bd9Sstevel@tonic-gate 	if (sctp->sctp_ftsn == sctp->sctp_lastacked + 1) {
185045916cd2Sjpk 		dprint(2, ("sctp_make_sack: %p no sack (already)\n",
185145916cd2Sjpk 		    (void *)sctp));
18527c478bd9Sstevel@tonic-gate 		return (NULL);
18537c478bd9Sstevel@tonic-gate 	}
18547c478bd9Sstevel@tonic-gate 
18557c478bd9Sstevel@tonic-gate checks_done:
18567c478bd9Sstevel@tonic-gate 	dprint(2, ("sctp_make_sack: acking %x\n", sctp->sctp_ftsn - 1));
18577c478bd9Sstevel@tonic-gate 
18587f093707Skcpoon 	if (dups != NULL)
18597f093707Skcpoon 		dups_len = MBLKL(dups);
18607f093707Skcpoon 	else
18617f093707Skcpoon 		dups_len = 0;
18627c478bd9Sstevel@tonic-gate 	slen = sizeof (*sch) + sizeof (*sc) +
18637c478bd9Sstevel@tonic-gate 	    (sizeof (sctp_sack_frag_t) * sctp->sctp_sack_gaps);
18647f093707Skcpoon 
18657f093707Skcpoon 	/*
18667f093707Skcpoon 	 * If there are error chunks, check and see if we can send the
18677f093707Skcpoon 	 * SACK chunk and error chunks together in one packet.  If not,
18687f093707Skcpoon 	 * send the error chunks out now.
18697f093707Skcpoon 	 */
18707f093707Skcpoon 	if (sctp->sctp_err_chunks != NULL) {
18717f093707Skcpoon 		fp = SCTP_CHUNK_DEST(sctp->sctp_err_chunks);
18726be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		if (sctp->sctp_err_len + slen + dups_len > fp->sf_pmss) {
18737f093707Skcpoon 			if ((smp = sctp_make_mp(sctp, fp, 0)) == NULL) {
18747f093707Skcpoon 				SCTP_KSTAT(sctps, sctp_send_err_failed);
18757f093707Skcpoon 				SCTP_KSTAT(sctps, sctp_send_sack_failed);
18767f093707Skcpoon 				freemsg(sctp->sctp_err_chunks);
18777f093707Skcpoon 				sctp->sctp_err_chunks = NULL;
18787f093707Skcpoon 				sctp->sctp_err_len = 0;
18797f093707Skcpoon 				return (NULL);
18807f093707Skcpoon 			}
18817f093707Skcpoon 			smp->b_cont = sctp->sctp_err_chunks;
18826be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 			sctp_set_iplen(sctp, smp, fp->sf_ixa);
18836be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 			(void) conn_ip_output(smp, fp->sf_ixa);
1884bd670b35SErik Nordmark 			BUMP_LOCAL(sctp->sctp_opkts);
18857f093707Skcpoon 			sctp->sctp_err_chunks = NULL;
18867f093707Skcpoon 			sctp->sctp_err_len = 0;
18877f093707Skcpoon 		}
18887f093707Skcpoon 	}
18897c478bd9Sstevel@tonic-gate 	smp = sctp_make_mp(sctp, sendto, slen);
18907c478bd9Sstevel@tonic-gate 	if (smp == NULL) {
1891f4b3ec61Sdh155122 		SCTP_KSTAT(sctps, sctp_send_sack_failed);
18927c478bd9Sstevel@tonic-gate 		return (NULL);
18937c478bd9Sstevel@tonic-gate 	}
18947c478bd9Sstevel@tonic-gate 	sch = (sctp_chunk_hdr_t *)smp->b_wptr;
18957c478bd9Sstevel@tonic-gate 
18967c478bd9Sstevel@tonic-gate 	sctp_fill_sack(sctp, smp->b_wptr, slen);
18977c478bd9Sstevel@tonic-gate 	smp->b_wptr += slen;
18987f093707Skcpoon 	if (dups != NULL) {
18997c478bd9Sstevel@tonic-gate 		sc = (sctp_sack_chunk_t *)(sch + 1);
19007f093707Skcpoon 		sc->ssc_numdups = htons(MBLKL(dups) / sizeof (uint32_t));
19017f093707Skcpoon 		sch->sch_len = htons(slen + dups_len);
19027c478bd9Sstevel@tonic-gate 		smp->b_cont = dups;
19037c478bd9Sstevel@tonic-gate 	}
19047c478bd9Sstevel@tonic-gate 
19057f093707Skcpoon 	if (sctp->sctp_err_chunks != NULL) {
19067f093707Skcpoon 		linkb(smp, sctp->sctp_err_chunks);
19077f093707Skcpoon 		sctp->sctp_err_chunks = NULL;
19087f093707Skcpoon 		sctp->sctp_err_len = 0;
19097f093707Skcpoon 	}
19107c478bd9Sstevel@tonic-gate 	return (smp);
19117c478bd9Sstevel@tonic-gate }
19127c478bd9Sstevel@tonic-gate 
19137f093707Skcpoon /*
19147f093707Skcpoon  * Check and see if we need to send a SACK chunk.  If it is needed,
19157f093707Skcpoon  * send it out.  Return true if a SACK chunk is sent, false otherwise.
19167f093707Skcpoon  */
19177f093707Skcpoon boolean_t
19187c478bd9Sstevel@tonic-gate sctp_sack(sctp_t *sctp, mblk_t *dups)
19197c478bd9Sstevel@tonic-gate {
19207c478bd9Sstevel@tonic-gate 	mblk_t *smp;
1921f4b3ec61Sdh155122 	sctp_stack_t	*sctps = sctp->sctp_sctps;
19227c478bd9Sstevel@tonic-gate 
19237c478bd9Sstevel@tonic-gate 	/* If we are shutting down, let send_shutdown() bundle the SACK */
19247c478bd9Sstevel@tonic-gate 	if (sctp->sctp_state == SCTPS_SHUTDOWN_SENT) {
19257c478bd9Sstevel@tonic-gate 		sctp_send_shutdown(sctp, 0);
19267c478bd9Sstevel@tonic-gate 	}
19277c478bd9Sstevel@tonic-gate 
19287c478bd9Sstevel@tonic-gate 	ASSERT(sctp->sctp_lastdata != NULL);
19297c478bd9Sstevel@tonic-gate 
19307c478bd9Sstevel@tonic-gate 	if ((smp = sctp_make_sack(sctp, sctp->sctp_lastdata, dups)) == NULL) {
19317c478bd9Sstevel@tonic-gate 		/* The caller of sctp_sack() will not free the dups mblk. */
19327c478bd9Sstevel@tonic-gate 		if (dups != NULL)
19337c478bd9Sstevel@tonic-gate 			freeb(dups);
19347f093707Skcpoon 		return (B_FALSE);
19357c478bd9Sstevel@tonic-gate 	}
19367c478bd9Sstevel@tonic-gate 	dprint(2, ("sctp_sack: sending to %p %x:%x:%x:%x\n",
193745916cd2Sjpk 	    (void *)sctp->sctp_lastdata,
19386be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 	    SCTP_PRINTADDR(sctp->sctp_lastdata->sf_faddr)));
19397c478bd9Sstevel@tonic-gate 
19405dd46ab5SKacheong Poon 	sctp->sctp_active = LBOLT_FASTPATH64;
19417c478bd9Sstevel@tonic-gate 
19425dd46ab5SKacheong Poon 	SCTPS_BUMP_MIB(sctps, sctpOutAck);
1943bd670b35SErik Nordmark 
19446be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 	sctp_set_iplen(sctp, smp, sctp->sctp_lastdata->sf_ixa);
19456be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 	(void) conn_ip_output(smp, sctp->sctp_lastdata->sf_ixa);
1946bd670b35SErik Nordmark 	BUMP_LOCAL(sctp->sctp_opkts);
19477f093707Skcpoon 	return (B_TRUE);
19487c478bd9Sstevel@tonic-gate }
19497c478bd9Sstevel@tonic-gate 
19507c478bd9Sstevel@tonic-gate /*
19517c478bd9Sstevel@tonic-gate  * This is called if we have a message that was partially sent and is
19527c478bd9Sstevel@tonic-gate  * abandoned. The cum TSN will be the last chunk sent for this message,
19537c478bd9Sstevel@tonic-gate  * subsequent chunks will be marked ABANDONED. We send a Forward TSN
19547c478bd9Sstevel@tonic-gate  * chunk in this case with the TSN of the last sent chunk so that the
19557c478bd9Sstevel@tonic-gate  * peer can clean up its fragment list for this message. This message
19567c478bd9Sstevel@tonic-gate  * will be removed from the transmit list when the peer sends a SACK
19577c478bd9Sstevel@tonic-gate  * back.
19587c478bd9Sstevel@tonic-gate  */
19597c478bd9Sstevel@tonic-gate int
19607c478bd9Sstevel@tonic-gate sctp_check_abandoned_msg(sctp_t *sctp, mblk_t *meta)
19617c478bd9Sstevel@tonic-gate {
19627c478bd9Sstevel@tonic-gate 	sctp_data_hdr_t	*dh;
19637c478bd9Sstevel@tonic-gate 	mblk_t		*nmp;
19647c478bd9Sstevel@tonic-gate 	mblk_t		*head;
19657c478bd9Sstevel@tonic-gate 	int32_t		unsent = 0;
19667c478bd9Sstevel@tonic-gate 	mblk_t		*mp1 = meta->b_cont;
19677c478bd9Sstevel@tonic-gate 	uint32_t	adv_pap = sctp->sctp_adv_pap;
19687c478bd9Sstevel@tonic-gate 	sctp_faddr_t	*fp = sctp->sctp_current;
1969f4b3ec61Sdh155122 	sctp_stack_t	*sctps = sctp->sctp_sctps;
19707c478bd9Sstevel@tonic-gate 
19717c478bd9Sstevel@tonic-gate 	dh = (sctp_data_hdr_t *)mp1->b_rptr;
19727c478bd9Sstevel@tonic-gate 	if (SEQ_GEQ(sctp->sctp_lastack_rxd, ntohl(dh->sdh_tsn))) {
19737c478bd9Sstevel@tonic-gate 		sctp_ftsn_set_t	*sets = NULL;
19747c478bd9Sstevel@tonic-gate 		uint_t		nsets = 0;
19757c478bd9Sstevel@tonic-gate 		uint32_t	seglen = sizeof (uint32_t);
19767c478bd9Sstevel@tonic-gate 		boolean_t	ubit = SCTP_DATA_GET_UBIT(dh);
19777c478bd9Sstevel@tonic-gate 
19787c478bd9Sstevel@tonic-gate 		while (mp1->b_next != NULL && SCTP_CHUNK_ISSENT(mp1->b_next))
19797c478bd9Sstevel@tonic-gate 			mp1 = mp1->b_next;
19807c478bd9Sstevel@tonic-gate 		dh = (sctp_data_hdr_t *)mp1->b_rptr;
19817c478bd9Sstevel@tonic-gate 		sctp->sctp_adv_pap = ntohl(dh->sdh_tsn);
19827c478bd9Sstevel@tonic-gate 		if (!ubit &&
19837c478bd9Sstevel@tonic-gate 		    !sctp_add_ftsn_set(&sets, fp, meta, &nsets, &seglen)) {
19847c478bd9Sstevel@tonic-gate 			sctp->sctp_adv_pap = adv_pap;
19857c478bd9Sstevel@tonic-gate 			return (ENOMEM);
19867c478bd9Sstevel@tonic-gate 		}
19877c478bd9Sstevel@tonic-gate 		nmp = sctp_make_ftsn_chunk(sctp, fp, sets, nsets, seglen);
19887c478bd9Sstevel@tonic-gate 		sctp_free_ftsn_set(sets);
19897c478bd9Sstevel@tonic-gate 		if (nmp == NULL) {
19907c478bd9Sstevel@tonic-gate 			sctp->sctp_adv_pap = adv_pap;
19917c478bd9Sstevel@tonic-gate 			return (ENOMEM);
19927c478bd9Sstevel@tonic-gate 		}
1993df19b344Svi117747 		head = sctp_add_proto_hdr(sctp, fp, nmp, 0, NULL);
19947c478bd9Sstevel@tonic-gate 		if (head == NULL) {
19957c478bd9Sstevel@tonic-gate 			sctp->sctp_adv_pap = adv_pap;
19967c478bd9Sstevel@tonic-gate 			freemsg(nmp);
1997f4b3ec61Sdh155122 			SCTP_KSTAT(sctps, sctp_send_ftsn_failed);
19987c478bd9Sstevel@tonic-gate 			return (ENOMEM);
19997c478bd9Sstevel@tonic-gate 		}
20007c478bd9Sstevel@tonic-gate 		SCTP_MSG_SET_ABANDONED(meta);
20016be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		sctp_set_iplen(sctp, head, fp->sf_ixa);
20026be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		(void) conn_ip_output(head, fp->sf_ixa);
2003bd670b35SErik Nordmark 		BUMP_LOCAL(sctp->sctp_opkts);
20046be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		if (!fp->sf_timer_running)
20056be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 			SCTP_FADDR_TIMER_RESTART(sctp, fp, fp->sf_rto);
20067c478bd9Sstevel@tonic-gate 		mp1 = mp1->b_next;
20077c478bd9Sstevel@tonic-gate 		while (mp1 != NULL) {
20087c478bd9Sstevel@tonic-gate 			ASSERT(!SCTP_CHUNK_ISSENT(mp1));
20097c478bd9Sstevel@tonic-gate 			ASSERT(!SCTP_CHUNK_ABANDONED(mp1));
20107c478bd9Sstevel@tonic-gate 			SCTP_ABANDON_CHUNK(mp1);
20117c478bd9Sstevel@tonic-gate 			dh = (sctp_data_hdr_t *)mp1->b_rptr;
20127c478bd9Sstevel@tonic-gate 			unsent += ntohs(dh->sdh_len) - sizeof (*dh);
20137c478bd9Sstevel@tonic-gate 			mp1 = mp1->b_next;
20147c478bd9Sstevel@tonic-gate 		}
20157c478bd9Sstevel@tonic-gate 		ASSERT(sctp->sctp_unsent >= unsent);
20167c478bd9Sstevel@tonic-gate 		sctp->sctp_unsent -= unsent;
20177c478bd9Sstevel@tonic-gate 		/*
20187c478bd9Sstevel@tonic-gate 		 * Update ULP the amount of queued data, which is
20197c478bd9Sstevel@tonic-gate 		 * sent-unack'ed + unsent.
20207c478bd9Sstevel@tonic-gate 		 */
20210f1702c5SYu Xiangning 		if (!SCTP_IS_DETACHED(sctp))
20220f1702c5SYu Xiangning 			SCTP_TXQ_UPDATE(sctp);
20237c478bd9Sstevel@tonic-gate 		return (0);
20247c478bd9Sstevel@tonic-gate 	}
20257c478bd9Sstevel@tonic-gate 	return (-1);
20267c478bd9Sstevel@tonic-gate }
20277c478bd9Sstevel@tonic-gate 
20287c478bd9Sstevel@tonic-gate uint32_t
20297c478bd9Sstevel@tonic-gate sctp_cumack(sctp_t *sctp, uint32_t tsn, mblk_t **first_unacked)
20307c478bd9Sstevel@tonic-gate {
20317c478bd9Sstevel@tonic-gate 	mblk_t *ump, *nump, *mp = NULL;
20327c478bd9Sstevel@tonic-gate 	uint16_t chunklen;
20337c478bd9Sstevel@tonic-gate 	uint32_t xtsn;
20347c478bd9Sstevel@tonic-gate 	sctp_faddr_t *fp;
20357c478bd9Sstevel@tonic-gate 	sctp_data_hdr_t *sdc;
20367c478bd9Sstevel@tonic-gate 	uint32_t cumack_forward = 0;
20377c478bd9Sstevel@tonic-gate 	sctp_msg_hdr_t	*mhdr;
2038f4b3ec61Sdh155122 	sctp_stack_t	*sctps = sctp->sctp_sctps;
20397c478bd9Sstevel@tonic-gate 
20407c478bd9Sstevel@tonic-gate 	ump = sctp->sctp_xmit_head;
20417c478bd9Sstevel@tonic-gate 
20427c478bd9Sstevel@tonic-gate 	/*
20437c478bd9Sstevel@tonic-gate 	 * Free messages only when they're completely acked.
20447c478bd9Sstevel@tonic-gate 	 */
20457c478bd9Sstevel@tonic-gate 	while (ump != NULL) {
20467c478bd9Sstevel@tonic-gate 		mhdr = (sctp_msg_hdr_t *)ump->b_rptr;
20477c478bd9Sstevel@tonic-gate 		for (mp = ump->b_cont; mp != NULL; mp = mp->b_next) {
20487c478bd9Sstevel@tonic-gate 			if (SCTP_CHUNK_ABANDONED(mp)) {
20497c478bd9Sstevel@tonic-gate 				ASSERT(SCTP_IS_MSG_ABANDONED(ump));
20507c478bd9Sstevel@tonic-gate 				mp = NULL;
20517c478bd9Sstevel@tonic-gate 				break;
20527c478bd9Sstevel@tonic-gate 			}
20537c478bd9Sstevel@tonic-gate 			/*
20547c478bd9Sstevel@tonic-gate 			 * We check for abandoned message if we are PR-SCTP
20557c478bd9Sstevel@tonic-gate 			 * aware, if this is not the first chunk in the
20567c478bd9Sstevel@tonic-gate 			 * message (b_cont) and if the message is marked
20577c478bd9Sstevel@tonic-gate 			 * abandoned.
20587c478bd9Sstevel@tonic-gate 			 */
20597c478bd9Sstevel@tonic-gate 			if (!SCTP_CHUNK_ISSENT(mp)) {
20607c478bd9Sstevel@tonic-gate 				if (sctp->sctp_prsctp_aware &&
20617c478bd9Sstevel@tonic-gate 				    mp != ump->b_cont &&
20627c478bd9Sstevel@tonic-gate 				    (SCTP_IS_MSG_ABANDONED(ump) ||
20637c478bd9Sstevel@tonic-gate 				    SCTP_MSG_TO_BE_ABANDONED(ump, mhdr,
20647c478bd9Sstevel@tonic-gate 				    sctp))) {
20657c478bd9Sstevel@tonic-gate 					(void) sctp_check_abandoned_msg(sctp,
20667c478bd9Sstevel@tonic-gate 					    ump);
20677c478bd9Sstevel@tonic-gate 				}
20687c478bd9Sstevel@tonic-gate 				goto cum_ack_done;
20697c478bd9Sstevel@tonic-gate 			}
20707c478bd9Sstevel@tonic-gate 			sdc = (sctp_data_hdr_t *)mp->b_rptr;
20717c478bd9Sstevel@tonic-gate 			xtsn = ntohl(sdc->sdh_tsn);
20727c478bd9Sstevel@tonic-gate 			if (SEQ_GEQ(sctp->sctp_lastack_rxd, xtsn))
20737c478bd9Sstevel@tonic-gate 				continue;
20747c478bd9Sstevel@tonic-gate 			if (SEQ_GEQ(tsn, xtsn)) {
20757c478bd9Sstevel@tonic-gate 				fp = SCTP_CHUNK_DEST(mp);
20767c478bd9Sstevel@tonic-gate 				chunklen = ntohs(sdc->sdh_len);
20777c478bd9Sstevel@tonic-gate 
20787c478bd9Sstevel@tonic-gate 				if (sctp->sctp_out_time != 0 &&
20797c478bd9Sstevel@tonic-gate 				    xtsn == sctp->sctp_rtt_tsn) {
20807c478bd9Sstevel@tonic-gate 					/* Got a new RTT measurement */
20817c478bd9Sstevel@tonic-gate 					sctp_update_rtt(sctp, fp,
2082d3d50737SRafael Vanoni 					    ddi_get_lbolt64() -
2083d3d50737SRafael Vanoni 					    sctp->sctp_out_time);
20847c478bd9Sstevel@tonic-gate 					sctp->sctp_out_time = 0;
20857c478bd9Sstevel@tonic-gate 				}
20867c478bd9Sstevel@tonic-gate 				if (SCTP_CHUNK_ISACKED(mp))
20877c478bd9Sstevel@tonic-gate 					continue;
208877c67f2fSkcpoon 				SCTP_CHUNK_SET_SACKCNT(mp, 0);
20897c478bd9Sstevel@tonic-gate 				SCTP_CHUNK_ACKED(mp);
20906be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 				ASSERT(fp->sf_suna >= chunklen);
20916be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 				fp->sf_suna -= chunklen;
20926be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 				fp->sf_acked += chunklen;
20937c478bd9Sstevel@tonic-gate 				cumack_forward += chunklen;
20947c478bd9Sstevel@tonic-gate 				ASSERT(sctp->sctp_unacked >=
20957c478bd9Sstevel@tonic-gate 				    (chunklen - sizeof (*sdc)));
20967c478bd9Sstevel@tonic-gate 				sctp->sctp_unacked -=
20977c478bd9Sstevel@tonic-gate 				    (chunklen - sizeof (*sdc));
20986be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 				if (fp->sf_suna == 0) {
20997c478bd9Sstevel@tonic-gate 					/* all outstanding data acked */
21006be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 					fp->sf_pba = 0;
21017c478bd9Sstevel@tonic-gate 					SCTP_FADDR_TIMER_STOP(fp);
21027c478bd9Sstevel@tonic-gate 				} else {
21037c478bd9Sstevel@tonic-gate 					SCTP_FADDR_TIMER_RESTART(sctp, fp,
21046be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 					    fp->sf_rto);
21057c478bd9Sstevel@tonic-gate 				}
21067c478bd9Sstevel@tonic-gate 			} else {
21077c478bd9Sstevel@tonic-gate 				goto cum_ack_done;
21087c478bd9Sstevel@tonic-gate 			}
21097c478bd9Sstevel@tonic-gate 		}
21107c478bd9Sstevel@tonic-gate 		nump = ump->b_next;
21117c478bd9Sstevel@tonic-gate 		if (nump != NULL)
21127c478bd9Sstevel@tonic-gate 			nump->b_prev = NULL;
21137c478bd9Sstevel@tonic-gate 		if (ump == sctp->sctp_xmit_tail)
21147c478bd9Sstevel@tonic-gate 			sctp->sctp_xmit_tail = nump;
21157c478bd9Sstevel@tonic-gate 		if (SCTP_IS_MSG_ABANDONED(ump)) {
21167c478bd9Sstevel@tonic-gate 			BUMP_LOCAL(sctp->sctp_prsctpdrop);
21177c478bd9Sstevel@tonic-gate 			ump->b_next = NULL;
21187c478bd9Sstevel@tonic-gate 			sctp_sendfail_event(sctp, ump, 0, B_TRUE);
21197c478bd9Sstevel@tonic-gate 		} else {
21207c478bd9Sstevel@tonic-gate 			sctp_free_msg(ump);
21217c478bd9Sstevel@tonic-gate 		}
21227c478bd9Sstevel@tonic-gate 		sctp->sctp_xmit_head = ump = nump;
21237c478bd9Sstevel@tonic-gate 	}
21247c478bd9Sstevel@tonic-gate cum_ack_done:
21257c478bd9Sstevel@tonic-gate 	*first_unacked = mp;
21267c478bd9Sstevel@tonic-gate 	if (cumack_forward > 0) {
21275dd46ab5SKacheong Poon 		SCTPS_BUMP_MIB(sctps, sctpInAck);
21287c478bd9Sstevel@tonic-gate 		if (SEQ_GT(sctp->sctp_lastack_rxd, sctp->sctp_recovery_tsn)) {
21297c478bd9Sstevel@tonic-gate 			sctp->sctp_recovery_tsn = sctp->sctp_lastack_rxd;
21307c478bd9Sstevel@tonic-gate 		}
21317c478bd9Sstevel@tonic-gate 
21327c478bd9Sstevel@tonic-gate 		/*
21337c478bd9Sstevel@tonic-gate 		 * Update ULP the amount of queued data, which is
21347c478bd9Sstevel@tonic-gate 		 * sent-unack'ed + unsent.
21357c478bd9Sstevel@tonic-gate 		 */
21360f1702c5SYu Xiangning 		if (!SCTP_IS_DETACHED(sctp))
21370f1702c5SYu Xiangning 			SCTP_TXQ_UPDATE(sctp);
21387c478bd9Sstevel@tonic-gate 
21397c478bd9Sstevel@tonic-gate 		/* Time to send a shutdown? */
21407c478bd9Sstevel@tonic-gate 		if (sctp->sctp_state == SCTPS_SHUTDOWN_PENDING) {
21417c478bd9Sstevel@tonic-gate 			sctp_send_shutdown(sctp, 0);
21427c478bd9Sstevel@tonic-gate 		}
21437c478bd9Sstevel@tonic-gate 		sctp->sctp_xmit_unacked = mp;
21447c478bd9Sstevel@tonic-gate 	} else {
21457c478bd9Sstevel@tonic-gate 		/* dup ack */
21465dd46ab5SKacheong Poon 		SCTPS_BUMP_MIB(sctps, sctpInDupAck);
21477c478bd9Sstevel@tonic-gate 	}
21487c478bd9Sstevel@tonic-gate 	sctp->sctp_lastack_rxd = tsn;
21497c478bd9Sstevel@tonic-gate 	if (SEQ_LT(sctp->sctp_adv_pap, sctp->sctp_lastack_rxd))
21507c478bd9Sstevel@tonic-gate 		sctp->sctp_adv_pap = sctp->sctp_lastack_rxd;
21517c478bd9Sstevel@tonic-gate 	ASSERT(sctp->sctp_xmit_head || sctp->sctp_unacked == 0);
21527c478bd9Sstevel@tonic-gate 
21537c478bd9Sstevel@tonic-gate 	return (cumack_forward);
21547c478bd9Sstevel@tonic-gate }
21557c478bd9Sstevel@tonic-gate 
21567c478bd9Sstevel@tonic-gate static int
21577c478bd9Sstevel@tonic-gate sctp_set_frwnd(sctp_t *sctp, uint32_t frwnd)
21587c478bd9Sstevel@tonic-gate {
21597c478bd9Sstevel@tonic-gate 	uint32_t orwnd;
21607c478bd9Sstevel@tonic-gate 
21617c478bd9Sstevel@tonic-gate 	if (sctp->sctp_unacked > frwnd) {
21627c478bd9Sstevel@tonic-gate 		sctp->sctp_frwnd = 0;
21637c478bd9Sstevel@tonic-gate 		return (0);
21647c478bd9Sstevel@tonic-gate 	}
21657c478bd9Sstevel@tonic-gate 	orwnd = sctp->sctp_frwnd;
21667c478bd9Sstevel@tonic-gate 	sctp->sctp_frwnd = frwnd - sctp->sctp_unacked;
21677c478bd9Sstevel@tonic-gate 	if (orwnd < sctp->sctp_frwnd) {
21687c478bd9Sstevel@tonic-gate 		return (1);
21697c478bd9Sstevel@tonic-gate 	} else {
21707c478bd9Sstevel@tonic-gate 		return (0);
21717c478bd9Sstevel@tonic-gate 	}
21727c478bd9Sstevel@tonic-gate }
21737c478bd9Sstevel@tonic-gate 
21747c478bd9Sstevel@tonic-gate /*
21757c478bd9Sstevel@tonic-gate  * For un-ordered messages.
21767c478bd9Sstevel@tonic-gate  * Walk the sctp->sctp_uo_frag list and remove any fragments with TSN
21777c478bd9Sstevel@tonic-gate  * less than/equal to ftsn. Fragments for un-ordered messages are
21787c478bd9Sstevel@tonic-gate  * strictly in sequence (w.r.t TSN).
21797c478bd9Sstevel@tonic-gate  */
21807c478bd9Sstevel@tonic-gate static int
21817c478bd9Sstevel@tonic-gate sctp_ftsn_check_uo_frag(sctp_t *sctp, uint32_t ftsn)
21827c478bd9Sstevel@tonic-gate {
21837c478bd9Sstevel@tonic-gate 	mblk_t		*hmp;
21847c478bd9Sstevel@tonic-gate 	mblk_t		*hmp_next;
21857c478bd9Sstevel@tonic-gate 	sctp_data_hdr_t	*dc;
21867c478bd9Sstevel@tonic-gate 	int		dlen = 0;
21877c478bd9Sstevel@tonic-gate 
21887c478bd9Sstevel@tonic-gate 	hmp = sctp->sctp_uo_frags;
21897c478bd9Sstevel@tonic-gate 	while (hmp != NULL) {
21907c478bd9Sstevel@tonic-gate 		hmp_next = hmp->b_next;
21917c478bd9Sstevel@tonic-gate 		dc = (sctp_data_hdr_t *)hmp->b_rptr;
21927c478bd9Sstevel@tonic-gate 		if (SEQ_GT(ntohl(dc->sdh_tsn), ftsn))
21937c478bd9Sstevel@tonic-gate 			return (dlen);
21947c478bd9Sstevel@tonic-gate 		sctp->sctp_uo_frags = hmp_next;
21957c478bd9Sstevel@tonic-gate 		if (hmp_next != NULL)
21967c478bd9Sstevel@tonic-gate 			hmp_next->b_prev = NULL;
21977c478bd9Sstevel@tonic-gate 		hmp->b_next = NULL;
21987c478bd9Sstevel@tonic-gate 		dlen += ntohs(dc->sdh_len) - sizeof (*dc);
21997c478bd9Sstevel@tonic-gate 		freeb(hmp);
22007c478bd9Sstevel@tonic-gate 		hmp = hmp_next;
22017c478bd9Sstevel@tonic-gate 	}
22027c478bd9Sstevel@tonic-gate 	return (dlen);
22037c478bd9Sstevel@tonic-gate }
22047c478bd9Sstevel@tonic-gate 
22057c478bd9Sstevel@tonic-gate /*
22067c478bd9Sstevel@tonic-gate  * For ordered messages.
22077c478bd9Sstevel@tonic-gate  * Check for existing fragments for an sid-ssn pair reported as abandoned,
22087c478bd9Sstevel@tonic-gate  * hence will not receive, in the Forward TSN. If there are fragments, then
22097c478bd9Sstevel@tonic-gate  * we just nuke them. If and when Partial Delivery API is supported, we
22107c478bd9Sstevel@tonic-gate  * would need to send a notification to the upper layer about this.
22117c478bd9Sstevel@tonic-gate  */
22127c478bd9Sstevel@tonic-gate static int
22137c478bd9Sstevel@tonic-gate sctp_ftsn_check_frag(sctp_t *sctp, uint16_t ssn, sctp_instr_t *sip)
22147c478bd9Sstevel@tonic-gate {
22157c478bd9Sstevel@tonic-gate 	sctp_reass_t	*srp;
22167c478bd9Sstevel@tonic-gate 	mblk_t		*hmp;
22177c478bd9Sstevel@tonic-gate 	mblk_t		*dmp;
22187c478bd9Sstevel@tonic-gate 	mblk_t		*hmp_next;
22197c478bd9Sstevel@tonic-gate 	sctp_data_hdr_t	*dc;
22207c478bd9Sstevel@tonic-gate 	int		dlen = 0;
22217c478bd9Sstevel@tonic-gate 
22227c478bd9Sstevel@tonic-gate 	hmp = sip->istr_reass;
22237c478bd9Sstevel@tonic-gate 	while (hmp != NULL) {
22247c478bd9Sstevel@tonic-gate 		hmp_next = hmp->b_next;
22257c478bd9Sstevel@tonic-gate 		srp = (sctp_reass_t *)DB_BASE(hmp);
22266be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		if (SSN_GT(srp->sr_ssn, ssn))
22277c478bd9Sstevel@tonic-gate 			return (dlen);
22287c478bd9Sstevel@tonic-gate 		/*
22297c478bd9Sstevel@tonic-gate 		 * If we had sent part of this message up, send a partial
22307c478bd9Sstevel@tonic-gate 		 * delivery event. Since this is ordered delivery, we should
22317c478bd9Sstevel@tonic-gate 		 * have sent partial message only for the next in sequence,
22327c478bd9Sstevel@tonic-gate 		 * hence the ASSERT. See comments in sctp_data_chunk() for
22337c478bd9Sstevel@tonic-gate 		 * trypartial.
22347c478bd9Sstevel@tonic-gate 		 */
22356be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		if (srp->sr_partial_delivered) {
22366be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 			if (srp->sr_ssn != sip->nextseq)
2237c3c17166SGeorge Shepherd 				cmn_err(CE_WARN, "sctp partial"
2238c3c17166SGeorge Shepherd 				    " delivery notify, sctp 0x%p"
2239c3c17166SGeorge Shepherd 				    " sip = 0x%p ssn != nextseq"
2240c3c17166SGeorge Shepherd 				    " ssn 0x%x nextseq 0x%x",
2241c3c17166SGeorge Shepherd 				    (void *)sctp, (void *)sip,
22426be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 				    srp->sr_ssn, sip->nextseq);
22436be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 			ASSERT(sip->nextseq == srp->sr_ssn);
22447c478bd9Sstevel@tonic-gate 			sctp_partial_delivery_event(sctp);
22457c478bd9Sstevel@tonic-gate 		}
22467c478bd9Sstevel@tonic-gate 		/* Take it out of the reass queue */
22477c478bd9Sstevel@tonic-gate 		sip->istr_reass = hmp_next;
22487c478bd9Sstevel@tonic-gate 		if (hmp_next != NULL)
22497c478bd9Sstevel@tonic-gate 			hmp_next->b_prev = NULL;
22507c478bd9Sstevel@tonic-gate 		hmp->b_next = NULL;
22517c478bd9Sstevel@tonic-gate 		ASSERT(hmp->b_prev == NULL);
22527c478bd9Sstevel@tonic-gate 		dmp = hmp;
22537d546a59Svi117747 		ASSERT(DB_TYPE(hmp) == M_CTL);
22547c478bd9Sstevel@tonic-gate 		dmp = hmp->b_cont;
22557c478bd9Sstevel@tonic-gate 		hmp->b_cont = NULL;
22567c478bd9Sstevel@tonic-gate 		freeb(hmp);
22577c478bd9Sstevel@tonic-gate 		hmp = dmp;
22587c478bd9Sstevel@tonic-gate 		while (dmp != NULL) {
22597c478bd9Sstevel@tonic-gate 			dc = (sctp_data_hdr_t *)dmp->b_rptr;
22607c478bd9Sstevel@tonic-gate 			dlen += ntohs(dc->sdh_len) - sizeof (*dc);
22617c478bd9Sstevel@tonic-gate 			dmp = dmp->b_cont;
22627c478bd9Sstevel@tonic-gate 		}
22637c478bd9Sstevel@tonic-gate 		freemsg(hmp);
22647c478bd9Sstevel@tonic-gate 		hmp = hmp_next;
22657c478bd9Sstevel@tonic-gate 	}
22667c478bd9Sstevel@tonic-gate 	return (dlen);
22677c478bd9Sstevel@tonic-gate }
22687c478bd9Sstevel@tonic-gate 
22697c478bd9Sstevel@tonic-gate /*
22707c478bd9Sstevel@tonic-gate  * Update sctp_ftsn to the cumulative TSN from the Forward TSN chunk. Remove
22717c478bd9Sstevel@tonic-gate  * any SACK gaps less than the newly updated sctp_ftsn. Walk through the
22727c478bd9Sstevel@tonic-gate  * sid-ssn pair in the Forward TSN and for each, clean the fragment list
22737c478bd9Sstevel@tonic-gate  * for this pair, if needed, and check if we can deliver subsequent
22747c478bd9Sstevel@tonic-gate  * messages, if any, from the instream queue (that were waiting for this
22757c478bd9Sstevel@tonic-gate  * sid-ssn message to show up). Once we are done try to update the SACK
22767c478bd9Sstevel@tonic-gate  * info. We could get a duplicate Forward TSN, in which case just send
2277bd670b35SErik Nordmark  * a SACK. If any of the sid values in the Forward TSN is invalid,
22787c478bd9Sstevel@tonic-gate  * send back an "Invalid Stream Identifier" error and continue processing
22797c478bd9Sstevel@tonic-gate  * the rest.
22807c478bd9Sstevel@tonic-gate  */
22817c478bd9Sstevel@tonic-gate static void
22827c478bd9Sstevel@tonic-gate sctp_process_forward_tsn(sctp_t *sctp, sctp_chunk_hdr_t *ch, sctp_faddr_t *fp,
2283bd670b35SErik Nordmark     ip_pkt_t *ipp, ip_recv_attr_t *ira)
22847c478bd9Sstevel@tonic-gate {
22857c478bd9Sstevel@tonic-gate 	uint32_t	*ftsn = (uint32_t *)(ch + 1);
22867c478bd9Sstevel@tonic-gate 	ftsn_entry_t	*ftsn_entry;
22877c478bd9Sstevel@tonic-gate 	sctp_instr_t	*instr;
22887c478bd9Sstevel@tonic-gate 	boolean_t	can_deliver = B_TRUE;
22897c478bd9Sstevel@tonic-gate 	size_t		dlen;
22907c478bd9Sstevel@tonic-gate 	int		flen;
22917c478bd9Sstevel@tonic-gate 	mblk_t		*dmp;
22927c478bd9Sstevel@tonic-gate 	mblk_t		*pmp;
22937c478bd9Sstevel@tonic-gate 	sctp_data_hdr_t	*dc;
22947c478bd9Sstevel@tonic-gate 	ssize_t		remaining;
2295f4b3ec61Sdh155122 	sctp_stack_t	*sctps = sctp->sctp_sctps;
22967c478bd9Sstevel@tonic-gate 
22977c478bd9Sstevel@tonic-gate 	*ftsn = ntohl(*ftsn);
22987c478bd9Sstevel@tonic-gate 	remaining =  ntohs(ch->sch_len) - sizeof (*ch) - sizeof (*ftsn);
22997c478bd9Sstevel@tonic-gate 
23007c478bd9Sstevel@tonic-gate 	if (SCTP_IS_DETACHED(sctp)) {
23015dd46ab5SKacheong Poon 		SCTPS_BUMP_MIB(sctps, sctpInClosed);
23027c478bd9Sstevel@tonic-gate 		can_deliver = B_FALSE;
23037c478bd9Sstevel@tonic-gate 	}
23047c478bd9Sstevel@tonic-gate 	/*
23057c478bd9Sstevel@tonic-gate 	 * un-ordered messages don't have SID-SSN pair entries, we check
23067c478bd9Sstevel@tonic-gate 	 * for any fragments (for un-ordered message) to be discarded using
23077c478bd9Sstevel@tonic-gate 	 * the cumulative FTSN.
23087c478bd9Sstevel@tonic-gate 	 */
23097c478bd9Sstevel@tonic-gate 	flen = sctp_ftsn_check_uo_frag(sctp, *ftsn);
23107c478bd9Sstevel@tonic-gate 	if (flen > 0) {
23117c478bd9Sstevel@tonic-gate 		ASSERT(sctp->sctp_rxqueued >= flen);
23127c478bd9Sstevel@tonic-gate 		sctp->sctp_rxqueued -= flen;
23137c478bd9Sstevel@tonic-gate 	}
23147c478bd9Sstevel@tonic-gate 	ftsn_entry = (ftsn_entry_t *)(ftsn + 1);
23157c478bd9Sstevel@tonic-gate 	while (remaining >= sizeof (*ftsn_entry)) {
23167c478bd9Sstevel@tonic-gate 		ftsn_entry->ftsn_sid = ntohs(ftsn_entry->ftsn_sid);
23177c478bd9Sstevel@tonic-gate 		ftsn_entry->ftsn_ssn = ntohs(ftsn_entry->ftsn_ssn);
23187c478bd9Sstevel@tonic-gate 		if (ftsn_entry->ftsn_sid >= sctp->sctp_num_istr) {
231947b33325SGeorge Shepherd 			sctp_bsc_t	inval_parm;
23207c478bd9Sstevel@tonic-gate 
232147b33325SGeorge Shepherd 			/* Will populate the CAUSE block in the ERROR chunk. */
232247b33325SGeorge Shepherd 			inval_parm.bsc_sid = htons(ftsn_entry->ftsn_sid);
232347b33325SGeorge Shepherd 			/* RESERVED, ignored at the receiving end */
232447b33325SGeorge Shepherd 			inval_parm.bsc_pad = 0;
232547b33325SGeorge Shepherd 
232647b33325SGeorge Shepherd 			sctp_add_err(sctp, SCTP_ERR_BAD_SID,
232747b33325SGeorge Shepherd 			    (void *)&inval_parm, sizeof (sctp_bsc_t), fp);
23287c478bd9Sstevel@tonic-gate 			ftsn_entry++;
23297c478bd9Sstevel@tonic-gate 			remaining -= sizeof (*ftsn_entry);
23307c478bd9Sstevel@tonic-gate 			continue;
23317c478bd9Sstevel@tonic-gate 		}
23327c478bd9Sstevel@tonic-gate 		instr = &sctp->sctp_instr[ftsn_entry->ftsn_sid];
23337c478bd9Sstevel@tonic-gate 		flen = sctp_ftsn_check_frag(sctp, ftsn_entry->ftsn_ssn, instr);
23347c478bd9Sstevel@tonic-gate 		/* Indicates frags were nuked, update rxqueued */
23357c478bd9Sstevel@tonic-gate 		if (flen > 0) {
23367c478bd9Sstevel@tonic-gate 			ASSERT(sctp->sctp_rxqueued >= flen);
23377c478bd9Sstevel@tonic-gate 			sctp->sctp_rxqueued -= flen;
23387c478bd9Sstevel@tonic-gate 		}
23397c478bd9Sstevel@tonic-gate 		/*
23407c478bd9Sstevel@tonic-gate 		 * It is possible to receive an FTSN chunk with SSN smaller
23417c478bd9Sstevel@tonic-gate 		 * than then nextseq if this chunk is a retransmission because
23427c478bd9Sstevel@tonic-gate 		 * of incomplete processing when it was first processed.
23437c478bd9Sstevel@tonic-gate 		 */
23447c478bd9Sstevel@tonic-gate 		if (SSN_GE(ftsn_entry->ftsn_ssn, instr->nextseq))
23457c478bd9Sstevel@tonic-gate 			instr->nextseq = ftsn_entry->ftsn_ssn + 1;
23467c478bd9Sstevel@tonic-gate 		while (instr->istr_nmsgs > 0) {
23477c478bd9Sstevel@tonic-gate 			mblk_t	*next;
23487c478bd9Sstevel@tonic-gate 
23497c478bd9Sstevel@tonic-gate 			dmp = (mblk_t *)instr->istr_msgs;
23507c478bd9Sstevel@tonic-gate 			dc = (sctp_data_hdr_t *)dmp->b_rptr;
23517c478bd9Sstevel@tonic-gate 			if (ntohs(dc->sdh_ssn) != instr->nextseq)
23527c478bd9Sstevel@tonic-gate 				break;
23537c478bd9Sstevel@tonic-gate 
23547c478bd9Sstevel@tonic-gate 			next = dmp->b_next;
23557c478bd9Sstevel@tonic-gate 			dlen = dmp->b_wptr - dmp->b_rptr - sizeof (*dc);
23567c478bd9Sstevel@tonic-gate 			for (pmp = dmp->b_cont; pmp != NULL;
23577c478bd9Sstevel@tonic-gate 			    pmp = pmp->b_cont) {
2358f0c3911fSGeorge Shepherd 				dlen += MBLKL(pmp);
23597c478bd9Sstevel@tonic-gate 			}
23607c478bd9Sstevel@tonic-gate 			if (can_deliver) {
23610f1702c5SYu Xiangning 				int error;
23627c478bd9Sstevel@tonic-gate 
23637c478bd9Sstevel@tonic-gate 				dmp->b_rptr = (uchar_t *)(dc + 1);
23647c478bd9Sstevel@tonic-gate 				dmp->b_next = NULL;
23657c478bd9Sstevel@tonic-gate 				ASSERT(dmp->b_prev == NULL);
23667c478bd9Sstevel@tonic-gate 				if (sctp_input_add_ancillary(sctp,
2367bd670b35SErik Nordmark 				    &dmp, dc, fp, ipp, ira) == 0) {
23687c478bd9Sstevel@tonic-gate 					sctp->sctp_rxqueued -= dlen;
23690f1702c5SYu Xiangning 					/*
23700f1702c5SYu Xiangning 					 * Override b_flag for SCTP sockfs
23710f1702c5SYu Xiangning 					 * internal use
23720f1702c5SYu Xiangning 					 */
23730f1702c5SYu Xiangning 
23740f1702c5SYu Xiangning 					dmp->b_flag = 0;
2375*a215d4ebSKacheong Poon 					if (sctp->sctp_flowctrld) {
2376*a215d4ebSKacheong Poon 						sctp->sctp_rwnd -= dlen;
2377*a215d4ebSKacheong Poon 						if (sctp->sctp_rwnd < 0)
2378f0c3911fSGeorge Shepherd 							sctp->sctp_rwnd = 0;
2379*a215d4ebSKacheong Poon 					}
2380*a215d4ebSKacheong Poon 					if (sctp->sctp_ulp_recv(
2381*a215d4ebSKacheong Poon 					    sctp->sctp_ulpd, dmp, msgdsize(dmp),
2382*a215d4ebSKacheong Poon 					    0, &error, NULL) <= 0) {
2383*a215d4ebSKacheong Poon 						sctp->sctp_flowctrld = B_TRUE;
2384*a215d4ebSKacheong Poon 					}
23857c478bd9Sstevel@tonic-gate 				} else {
23867c478bd9Sstevel@tonic-gate 					/*
23877c478bd9Sstevel@tonic-gate 					 * We will resume processing when
23887c478bd9Sstevel@tonic-gate 					 * the FTSN chunk is re-xmitted.
23897c478bd9Sstevel@tonic-gate 					 */
23907c478bd9Sstevel@tonic-gate 					dmp->b_rptr = (uchar_t *)dc;
23917c478bd9Sstevel@tonic-gate 					dmp->b_next = next;
23927c478bd9Sstevel@tonic-gate 					dprint(0,
23937c478bd9Sstevel@tonic-gate 					    ("FTSN dequeuing %u failed\n",
23947c478bd9Sstevel@tonic-gate 					    ntohs(dc->sdh_ssn)));
23957c478bd9Sstevel@tonic-gate 					return;
23967c478bd9Sstevel@tonic-gate 				}
23977c478bd9Sstevel@tonic-gate 			} else {
23987c478bd9Sstevel@tonic-gate 				sctp->sctp_rxqueued -= dlen;
23997c478bd9Sstevel@tonic-gate 				ASSERT(dmp->b_prev == NULL);
24007c478bd9Sstevel@tonic-gate 				dmp->b_next = NULL;
24017c478bd9Sstevel@tonic-gate 				freemsg(dmp);
24027c478bd9Sstevel@tonic-gate 			}
24037c478bd9Sstevel@tonic-gate 			instr->istr_nmsgs--;
24047c478bd9Sstevel@tonic-gate 			instr->nextseq++;
24057c478bd9Sstevel@tonic-gate 			sctp->sctp_istr_nmsgs--;
24067c478bd9Sstevel@tonic-gate 			if (next != NULL)
24077c478bd9Sstevel@tonic-gate 				next->b_prev = NULL;
24087c478bd9Sstevel@tonic-gate 			instr->istr_msgs = next;
24097c478bd9Sstevel@tonic-gate 		}
24107c478bd9Sstevel@tonic-gate 		ftsn_entry++;
24117c478bd9Sstevel@tonic-gate 		remaining -= sizeof (*ftsn_entry);
24127c478bd9Sstevel@tonic-gate 	}
24137c478bd9Sstevel@tonic-gate 	/* Duplicate FTSN */
24147c478bd9Sstevel@tonic-gate 	if (*ftsn <= (sctp->sctp_ftsn - 1)) {
24157c478bd9Sstevel@tonic-gate 		sctp->sctp_force_sack = 1;
24167c478bd9Sstevel@tonic-gate 		return;
24177c478bd9Sstevel@tonic-gate 	}
24187c478bd9Sstevel@tonic-gate 	/* Advance cum TSN to that reported in the Forward TSN chunk */
24197c478bd9Sstevel@tonic-gate 	sctp->sctp_ftsn = *ftsn + 1;
24207c478bd9Sstevel@tonic-gate 
24217c478bd9Sstevel@tonic-gate 	/* Remove all the SACK gaps before the new cum TSN */
24227c478bd9Sstevel@tonic-gate 	if (sctp->sctp_sack_info != NULL) {
24237c478bd9Sstevel@tonic-gate 		sctp_ack_rem(&sctp->sctp_sack_info, sctp->sctp_ftsn - 1,
24247c478bd9Sstevel@tonic-gate 		    &sctp->sctp_sack_gaps);
24257c478bd9Sstevel@tonic-gate 	}
24267c478bd9Sstevel@tonic-gate 	/*
24277c478bd9Sstevel@tonic-gate 	 * If there are gap reports pending, check if advancing
24287c478bd9Sstevel@tonic-gate 	 * the ftsn here closes a gap. If so, we can advance
24297c478bd9Sstevel@tonic-gate 	 * ftsn to the end of the set.
24307c478bd9Sstevel@tonic-gate 	 * If ftsn has moved forward, maybe we can remove gap reports.
24317c478bd9Sstevel@tonic-gate 	 */
24327c478bd9Sstevel@tonic-gate 	if (sctp->sctp_sack_info != NULL &&
24337c478bd9Sstevel@tonic-gate 	    sctp->sctp_ftsn == sctp->sctp_sack_info->begin) {
24347c478bd9Sstevel@tonic-gate 		sctp->sctp_ftsn = sctp->sctp_sack_info->end + 1;
24357c478bd9Sstevel@tonic-gate 		sctp_ack_rem(&sctp->sctp_sack_info, sctp->sctp_ftsn - 1,
24367c478bd9Sstevel@tonic-gate 		    &sctp->sctp_sack_gaps);
24377c478bd9Sstevel@tonic-gate 	}
24387c478bd9Sstevel@tonic-gate }
24397c478bd9Sstevel@tonic-gate 
24407c478bd9Sstevel@tonic-gate /*
24417c478bd9Sstevel@tonic-gate  * When we have processed a SACK we check to see if we can advance the
24427c478bd9Sstevel@tonic-gate  * cumulative TSN if there are abandoned chunks immediately following
24437c478bd9Sstevel@tonic-gate  * the updated cumulative TSN. If there are, we attempt to send a
24447c478bd9Sstevel@tonic-gate  * Forward TSN chunk.
24457c478bd9Sstevel@tonic-gate  */
24467c478bd9Sstevel@tonic-gate static void
24477c478bd9Sstevel@tonic-gate sctp_check_abandoned_data(sctp_t *sctp, sctp_faddr_t *fp)
24487c478bd9Sstevel@tonic-gate {
24497c478bd9Sstevel@tonic-gate 	mblk_t		*meta = sctp->sctp_xmit_head;
24507c478bd9Sstevel@tonic-gate 	mblk_t		*mp;
24517c478bd9Sstevel@tonic-gate 	mblk_t		*nmp;
24527c478bd9Sstevel@tonic-gate 	uint32_t	seglen;
24537c478bd9Sstevel@tonic-gate 	uint32_t	adv_pap = sctp->sctp_adv_pap;
24547c478bd9Sstevel@tonic-gate 
24557c478bd9Sstevel@tonic-gate 	/*
24567c478bd9Sstevel@tonic-gate 	 * We only check in the first meta since otherwise we can't
24577c478bd9Sstevel@tonic-gate 	 * advance the cumulative ack point. We just look for chunks
24587c478bd9Sstevel@tonic-gate 	 * marked for retransmission, else we might prematurely
24597c478bd9Sstevel@tonic-gate 	 * send an FTSN for a sent, but unacked, chunk.
24607c478bd9Sstevel@tonic-gate 	 */
24617c478bd9Sstevel@tonic-gate 	for (mp = meta->b_cont; mp != NULL; mp = mp->b_next) {
24627c478bd9Sstevel@tonic-gate 		if (!SCTP_CHUNK_ISSENT(mp))
24637c478bd9Sstevel@tonic-gate 			return;
24647c478bd9Sstevel@tonic-gate 		if (SCTP_CHUNK_WANT_REXMIT(mp))
24657c478bd9Sstevel@tonic-gate 			break;
24667c478bd9Sstevel@tonic-gate 	}
24677c478bd9Sstevel@tonic-gate 	if (mp == NULL)
24687c478bd9Sstevel@tonic-gate 		return;
24697c478bd9Sstevel@tonic-gate 	sctp_check_adv_ack_pt(sctp, meta, mp);
24707c478bd9Sstevel@tonic-gate 	if (SEQ_GT(sctp->sctp_adv_pap, adv_pap)) {
24717c478bd9Sstevel@tonic-gate 		sctp_make_ftsns(sctp, meta, mp, &nmp, fp, &seglen);
24727c478bd9Sstevel@tonic-gate 		if (nmp == NULL) {
24737c478bd9Sstevel@tonic-gate 			sctp->sctp_adv_pap = adv_pap;
24746be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 			if (!fp->sf_timer_running)
24756be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 				SCTP_FADDR_TIMER_RESTART(sctp, fp, fp->sf_rto);
24767c478bd9Sstevel@tonic-gate 			return;
24777c478bd9Sstevel@tonic-gate 		}
24786be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		sctp_set_iplen(sctp, nmp, fp->sf_ixa);
24796be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		(void) conn_ip_output(nmp, fp->sf_ixa);
2480bd670b35SErik Nordmark 		BUMP_LOCAL(sctp->sctp_opkts);
24816be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		if (!fp->sf_timer_running)
24826be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 			SCTP_FADDR_TIMER_RESTART(sctp, fp, fp->sf_rto);
24837c478bd9Sstevel@tonic-gate 	}
24847c478bd9Sstevel@tonic-gate }
24857c478bd9Sstevel@tonic-gate 
24861d8c4025Svi117747 /*
24871d8c4025Svi117747  * The processing here follows the same logic in sctp_got_sack(), the reason
24881d8c4025Svi117747  * we do this separately is because, usually, gap blocks are ordered and
24891d8c4025Svi117747  * we can process it in sctp_got_sack(). However if they aren't we would
24901d8c4025Svi117747  * need to do some additional non-optimal stuff when we start processing the
24911d8c4025Svi117747  * unordered gaps. To that effect sctp_got_sack() does the processing in the
24921d8c4025Svi117747  * simple case and this does the same in the more involved case.
24931d8c4025Svi117747  */
24941d8c4025Svi117747 static uint32_t
24951d8c4025Svi117747 sctp_process_uo_gaps(sctp_t *sctp, uint32_t ctsn, sctp_sack_frag_t *ssf,
24961d8c4025Svi117747     int num_gaps, mblk_t *umphead, mblk_t *mphead, int *trysend,
24971d8c4025Svi117747     boolean_t *fast_recovery, uint32_t fr_xtsn)
24981d8c4025Svi117747 {
24991d8c4025Svi117747 	uint32_t		xtsn;
25001d8c4025Svi117747 	uint32_t		gapstart = 0;
25011d8c4025Svi117747 	uint32_t		gapend = 0;
25021d8c4025Svi117747 	int			gapcnt;
25031d8c4025Svi117747 	uint16_t		chunklen;
25041d8c4025Svi117747 	sctp_data_hdr_t		*sdc;
25051d8c4025Svi117747 	int			gstart;
25061d8c4025Svi117747 	mblk_t			*ump = umphead;
25071d8c4025Svi117747 	mblk_t			*mp = mphead;
25081d8c4025Svi117747 	sctp_faddr_t		*fp;
25091d8c4025Svi117747 	uint32_t		acked = 0;
2510f4b3ec61Sdh155122 	sctp_stack_t		*sctps = sctp->sctp_sctps;
25111d8c4025Svi117747 
25121d8c4025Svi117747 	/*
25131d8c4025Svi117747 	 * gstart tracks the last (in the order of TSN) gapstart that
25141d8c4025Svi117747 	 * we process in this SACK gaps walk.
25151d8c4025Svi117747 	 */
25161d8c4025Svi117747 	gstart = ctsn;
25171d8c4025Svi117747 
25181d8c4025Svi117747 	sdc = (sctp_data_hdr_t *)mp->b_rptr;
25191d8c4025Svi117747 	xtsn = ntohl(sdc->sdh_tsn);
25201d8c4025Svi117747 	for (gapcnt = 0; gapcnt < num_gaps; gapcnt++, ssf++) {
25211d8c4025Svi117747 		if (gapstart != 0) {
25221d8c4025Svi117747 			/*
25231d8c4025Svi117747 			 * If we have reached the end of the transmit list or
25241d8c4025Svi117747 			 * hit an unsent chunk or encountered an unordered gap
25251d8c4025Svi117747 			 * block start from the ctsn again.
25261d8c4025Svi117747 			 */
25271d8c4025Svi117747 			if (ump == NULL || !SCTP_CHUNK_ISSENT(mp) ||
25281d8c4025Svi117747 			    SEQ_LT(ctsn + ntohs(ssf->ssf_start), xtsn)) {
25291d8c4025Svi117747 				ump = umphead;
25301d8c4025Svi117747 				mp = mphead;
25311d8c4025Svi117747 				sdc = (sctp_data_hdr_t *)mp->b_rptr;
25321d8c4025Svi117747 				xtsn = ntohl(sdc->sdh_tsn);
25331d8c4025Svi117747 			}
25341d8c4025Svi117747 		}
25351d8c4025Svi117747 
25361d8c4025Svi117747 		gapstart = ctsn + ntohs(ssf->ssf_start);
25371d8c4025Svi117747 		gapend = ctsn + ntohs(ssf->ssf_end);
25381d8c4025Svi117747 
25392282c4abSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		/*
25402282c4abSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		 * Sanity checks:
25412282c4abSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		 *
25422282c4abSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		 * 1. SACK for TSN we have not sent - ABORT
25432282c4abSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		 * 2. Invalid or spurious gaps, ignore all gaps
25442282c4abSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		 */
25451d8c4025Svi117747 		if (SEQ_GT(gapstart, sctp->sctp_ltsn - 1) ||
25461d8c4025Svi117747 		    SEQ_GT(gapend, sctp->sctp_ltsn - 1)) {
25475dd46ab5SKacheong Poon 			SCTPS_BUMP_MIB(sctps, sctpInAckUnsent);
25481d8c4025Svi117747 			*trysend = -1;
25491d8c4025Svi117747 			return (acked);
25502282c4abSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		} else if (SEQ_LT(gapend, gapstart) ||
25512282c4abSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		    SEQ_LEQ(gapstart, ctsn)) {
25521d8c4025Svi117747 			break;
25531d8c4025Svi117747 		}
25541d8c4025Svi117747 		/*
25551d8c4025Svi117747 		 * The xtsn can be the TSN processed for the last gap
25561d8c4025Svi117747 		 * (gapend) or it could be the cumulative TSN. We continue
25571d8c4025Svi117747 		 * with the last xtsn as long as the gaps are ordered, when
25581d8c4025Svi117747 		 * we hit an unordered gap, we re-start from the cumulative
25591d8c4025Svi117747 		 * TSN. For the first gap it is always the cumulative TSN.
25601d8c4025Svi117747 		 */
25611d8c4025Svi117747 		while (xtsn != gapstart) {
25621d8c4025Svi117747 			/*
25631d8c4025Svi117747 			 * We can't reliably check for reneged chunks
25641d8c4025Svi117747 			 * when walking the unordered list, so we don't.
25651d8c4025Svi117747 			 * In case the peer reneges then we will end up
25661d8c4025Svi117747 			 * sending the reneged chunk via timeout.
25671d8c4025Svi117747 			 */
25681d8c4025Svi117747 			mp = mp->b_next;
25691d8c4025Svi117747 			if (mp == NULL) {
25701d8c4025Svi117747 				ump = ump->b_next;
25711d8c4025Svi117747 				/*
25721d8c4025Svi117747 				 * ump can't be NULL because of the sanity
25731d8c4025Svi117747 				 * check above.
25741d8c4025Svi117747 				 */
25751d8c4025Svi117747 				ASSERT(ump != NULL);
25761d8c4025Svi117747 				mp = ump->b_cont;
25771d8c4025Svi117747 			}
25781d8c4025Svi117747 			/*
25791d8c4025Svi117747 			 * mp can't be unsent because of the sanity check
25801d8c4025Svi117747 			 * above.
25811d8c4025Svi117747 			 */
25821d8c4025Svi117747 			ASSERT(SCTP_CHUNK_ISSENT(mp));
25831d8c4025Svi117747 			sdc = (sctp_data_hdr_t *)mp->b_rptr;
25841d8c4025Svi117747 			xtsn = ntohl(sdc->sdh_tsn);
25851d8c4025Svi117747 		}
25861d8c4025Svi117747 		/*
25871d8c4025Svi117747 		 * Now that we have found the chunk with TSN == 'gapstart',
25881d8c4025Svi117747 		 * let's walk till we hit the chunk with TSN == 'gapend'.
25891d8c4025Svi117747 		 * All intermediate chunks will be marked ACKED, if they
25901d8c4025Svi117747 		 * haven't already been.
25911d8c4025Svi117747 		 */
25921d8c4025Svi117747 		while (SEQ_LEQ(xtsn, gapend)) {
25931d8c4025Svi117747 			/*
25941d8c4025Svi117747 			 * SACKed
25951d8c4025Svi117747 			 */
25961d8c4025Svi117747 			SCTP_CHUNK_SET_SACKCNT(mp, 0);
25971d8c4025Svi117747 			if (!SCTP_CHUNK_ISACKED(mp)) {
25981d8c4025Svi117747 				SCTP_CHUNK_ACKED(mp);
25991d8c4025Svi117747 
26001d8c4025Svi117747 				fp = SCTP_CHUNK_DEST(mp);
26011d8c4025Svi117747 				chunklen = ntohs(sdc->sdh_len);
26026be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 				ASSERT(fp->sf_suna >= chunklen);
26036be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 				fp->sf_suna -= chunklen;
26046be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 				if (fp->sf_suna == 0) {
26051d8c4025Svi117747 					/* All outstanding data acked. */
26066be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 					fp->sf_pba = 0;
26071d8c4025Svi117747 					SCTP_FADDR_TIMER_STOP(fp);
26081d8c4025Svi117747 				}
26096be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 				fp->sf_acked += chunklen;
26101d8c4025Svi117747 				acked += chunklen;
26111d8c4025Svi117747 				sctp->sctp_unacked -= chunklen - sizeof (*sdc);
26121d8c4025Svi117747 				ASSERT(sctp->sctp_unacked >= 0);
26131d8c4025Svi117747 			}
26141d8c4025Svi117747 			/*
26151d8c4025Svi117747 			 * Move to the next message in the transmit list
26161d8c4025Svi117747 			 * if we are done with all the chunks from the current
26171d8c4025Svi117747 			 * message. Note, it is possible to hit the end of the
26181d8c4025Svi117747 			 * transmit list here, i.e. if we have already completed
26191d8c4025Svi117747 			 * processing the gap block.
26201d8c4025Svi117747 			 */
26211d8c4025Svi117747 			mp = mp->b_next;
26221d8c4025Svi117747 			if (mp == NULL) {
26231d8c4025Svi117747 				ump = ump->b_next;
26241d8c4025Svi117747 				if (ump == NULL) {
26251d8c4025Svi117747 					ASSERT(xtsn == gapend);
26261d8c4025Svi117747 					break;
26271d8c4025Svi117747 				}
26281d8c4025Svi117747 				mp = ump->b_cont;
26291d8c4025Svi117747 			}
26301d8c4025Svi117747 			/*
26311d8c4025Svi117747 			 * Likewise, we can hit an unsent chunk once we have
26321d8c4025Svi117747 			 * completed processing the gap block.
26331d8c4025Svi117747 			 */
26341d8c4025Svi117747 			if (!SCTP_CHUNK_ISSENT(mp)) {
26351d8c4025Svi117747 				ASSERT(xtsn == gapend);
26361d8c4025Svi117747 				break;
26371d8c4025Svi117747 			}
26381d8c4025Svi117747 			sdc = (sctp_data_hdr_t *)mp->b_rptr;
26391d8c4025Svi117747 			xtsn = ntohl(sdc->sdh_tsn);
26401d8c4025Svi117747 		}
26411d8c4025Svi117747 		/*
26421d8c4025Svi117747 		 * We keep track of the last gap we successfully processed
26431d8c4025Svi117747 		 * so that we can terminate the walk below for incrementing
26441d8c4025Svi117747 		 * the SACK count.
26451d8c4025Svi117747 		 */
26461d8c4025Svi117747 		if (SEQ_LT(gstart, gapstart))
26471d8c4025Svi117747 			gstart = gapstart;
26481d8c4025Svi117747 	}
26491d8c4025Svi117747 	/*
26501d8c4025Svi117747 	 * Check if have incremented the SACK count for all unacked TSNs in
26511d8c4025Svi117747 	 * sctp_got_sack(), if so we are done.
26521d8c4025Svi117747 	 */
26531d8c4025Svi117747 	if (SEQ_LEQ(gstart, fr_xtsn))
26541d8c4025Svi117747 		return (acked);
26551d8c4025Svi117747 
26561d8c4025Svi117747 	ump = umphead;
26571d8c4025Svi117747 	mp = mphead;
26581d8c4025Svi117747 	sdc = (sctp_data_hdr_t *)mp->b_rptr;
26591d8c4025Svi117747 	xtsn = ntohl(sdc->sdh_tsn);
26601d8c4025Svi117747 	while (SEQ_LT(xtsn, gstart)) {
26611d8c4025Svi117747 		/*
26621d8c4025Svi117747 		 * We have incremented SACK count for TSNs less than fr_tsn
26631d8c4025Svi117747 		 * in sctp_got_sack(), so don't increment them again here.
26641d8c4025Svi117747 		 */
26651d8c4025Svi117747 		if (SEQ_GT(xtsn, fr_xtsn) && !SCTP_CHUNK_ISACKED(mp)) {
26661d8c4025Svi117747 			SCTP_CHUNK_SET_SACKCNT(mp, SCTP_CHUNK_SACKCNT(mp) + 1);
2667f4b3ec61Sdh155122 			if (SCTP_CHUNK_SACKCNT(mp) ==
2668f4b3ec61Sdh155122 			    sctps->sctps_fast_rxt_thresh) {
2669c3c17166SGeorge Shepherd 				SCTP_CHUNK_REXMIT(sctp, mp);
26701d8c4025Svi117747 				sctp->sctp_chk_fast_rexmit = B_TRUE;
26711d8c4025Svi117747 				*trysend = 1;
26721d8c4025Svi117747 				if (!*fast_recovery) {
26731d8c4025Svi117747 					/*
26741d8c4025Svi117747 					 * Entering fast recovery.
26751d8c4025Svi117747 					 */
26761d8c4025Svi117747 					fp = SCTP_CHUNK_DEST(mp);
26776be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 					fp->sf_ssthresh = fp->sf_cwnd / 2;
26786be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 					if (fp->sf_ssthresh < 2 * fp->sf_pmss) {
26796be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 						fp->sf_ssthresh =
26806be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 						    2 * fp->sf_pmss;
26811d8c4025Svi117747 					}
26826be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 					fp->sf_cwnd = fp->sf_ssthresh;
26836be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 					fp->sf_pba = 0;
26841d8c4025Svi117747 					sctp->sctp_recovery_tsn =
26851d8c4025Svi117747 					    sctp->sctp_ltsn - 1;
26861d8c4025Svi117747 					*fast_recovery = B_TRUE;
26871d8c4025Svi117747 				}
26881d8c4025Svi117747 			}
26891d8c4025Svi117747 		}
26901d8c4025Svi117747 		mp = mp->b_next;
26911d8c4025Svi117747 		if (mp == NULL) {
26921d8c4025Svi117747 			ump = ump->b_next;
26931d8c4025Svi117747 			/* We can't get to the end of the transmit list here */
26941d8c4025Svi117747 			ASSERT(ump != NULL);
26951d8c4025Svi117747 			mp = ump->b_cont;
26961d8c4025Svi117747 		}
26971d8c4025Svi117747 		/* We can't hit an unsent chunk here */
26981d8c4025Svi117747 		ASSERT(SCTP_CHUNK_ISSENT(mp));
26991d8c4025Svi117747 		sdc = (sctp_data_hdr_t *)mp->b_rptr;
27001d8c4025Svi117747 		xtsn = ntohl(sdc->sdh_tsn);
27011d8c4025Svi117747 	}
27021d8c4025Svi117747 	return (acked);
27031d8c4025Svi117747 }
27041d8c4025Svi117747 
27057c478bd9Sstevel@tonic-gate static int
27067c478bd9Sstevel@tonic-gate sctp_got_sack(sctp_t *sctp, sctp_chunk_hdr_t *sch)
27077c478bd9Sstevel@tonic-gate {
27087c478bd9Sstevel@tonic-gate 	sctp_sack_chunk_t	*sc;
27097c478bd9Sstevel@tonic-gate 	sctp_data_hdr_t		*sdc;
27107c478bd9Sstevel@tonic-gate 	sctp_sack_frag_t	*ssf;
27117c478bd9Sstevel@tonic-gate 	mblk_t			*ump;
27127c478bd9Sstevel@tonic-gate 	mblk_t			*mp;
27131d8c4025Svi117747 	mblk_t			*mp1;
27141d8c4025Svi117747 	uint32_t		cumtsn;
27157c478bd9Sstevel@tonic-gate 	uint32_t		xtsn;
27161d8c4025Svi117747 	uint32_t		gapstart = 0;
27171d8c4025Svi117747 	uint32_t		gapend = 0;
27187c478bd9Sstevel@tonic-gate 	uint32_t		acked = 0;
27197c478bd9Sstevel@tonic-gate 	uint16_t		chunklen;
27207c478bd9Sstevel@tonic-gate 	sctp_faddr_t		*fp;
27217c478bd9Sstevel@tonic-gate 	int			num_gaps;
27227c478bd9Sstevel@tonic-gate 	int			trysend = 0;
27237c478bd9Sstevel@tonic-gate 	int			i;
27247c478bd9Sstevel@tonic-gate 	boolean_t		fast_recovery = B_FALSE;
27257c478bd9Sstevel@tonic-gate 	boolean_t		cumack_forward = B_FALSE;
27267c478bd9Sstevel@tonic-gate 	boolean_t		fwd_tsn = B_FALSE;
2727f4b3ec61Sdh155122 	sctp_stack_t		*sctps = sctp->sctp_sctps;
27287c478bd9Sstevel@tonic-gate 
27297c478bd9Sstevel@tonic-gate 	BUMP_LOCAL(sctp->sctp_ibchunks);
27309f13099eSGeorge Shepherd 	BUMP_LOCAL(sctp->sctp_isacks);
27317c478bd9Sstevel@tonic-gate 	chunklen = ntohs(sch->sch_len);
27327c478bd9Sstevel@tonic-gate 	if (chunklen < (sizeof (*sch) + sizeof (*sc)))
27337c478bd9Sstevel@tonic-gate 		return (0);
27347c478bd9Sstevel@tonic-gate 
27357c478bd9Sstevel@tonic-gate 	sc = (sctp_sack_chunk_t *)(sch + 1);
27361d8c4025Svi117747 	cumtsn = ntohl(sc->ssc_cumtsn);
27377c478bd9Sstevel@tonic-gate 
27381d8c4025Svi117747 	dprint(2, ("got sack cumtsn %x -> %x\n", sctp->sctp_lastack_rxd,
27391d8c4025Svi117747 	    cumtsn));
27407c478bd9Sstevel@tonic-gate 
27417c478bd9Sstevel@tonic-gate 	/* out of order */
27421d8c4025Svi117747 	if (SEQ_LT(cumtsn, sctp->sctp_lastack_rxd))
27437c478bd9Sstevel@tonic-gate 		return (0);
27447c478bd9Sstevel@tonic-gate 
27451d8c4025Svi117747 	if (SEQ_GT(cumtsn, sctp->sctp_ltsn - 1)) {
27465dd46ab5SKacheong Poon 		SCTPS_BUMP_MIB(sctps, sctpInAckUnsent);
27471d8c4025Svi117747 		/* Send an ABORT */
27481d8c4025Svi117747 		return (-1);
27497c478bd9Sstevel@tonic-gate 	}
27507c478bd9Sstevel@tonic-gate 
27517c478bd9Sstevel@tonic-gate 	/*
27527c478bd9Sstevel@tonic-gate 	 * Cwnd only done when not in fast recovery mode.
27537c478bd9Sstevel@tonic-gate 	 */
27547c478bd9Sstevel@tonic-gate 	if (SEQ_LT(sctp->sctp_lastack_rxd, sctp->sctp_recovery_tsn))
27557c478bd9Sstevel@tonic-gate 		fast_recovery = B_TRUE;
27567c478bd9Sstevel@tonic-gate 
27577c478bd9Sstevel@tonic-gate 	/*
27587c478bd9Sstevel@tonic-gate 	 * .. and if the cum TSN is not moving ahead on account Forward TSN
27597c478bd9Sstevel@tonic-gate 	 */
27607c478bd9Sstevel@tonic-gate 	if (SEQ_LT(sctp->sctp_lastack_rxd, sctp->sctp_adv_pap))
27617c478bd9Sstevel@tonic-gate 		fwd_tsn = B_TRUE;
27627c478bd9Sstevel@tonic-gate 
27631d8c4025Svi117747 	if (cumtsn == sctp->sctp_lastack_rxd &&
27647c478bd9Sstevel@tonic-gate 	    (sctp->sctp_xmit_unacked == NULL ||
27657c478bd9Sstevel@tonic-gate 	    !SCTP_CHUNK_ABANDONED(sctp->sctp_xmit_unacked))) {
27667c478bd9Sstevel@tonic-gate 		if (sctp->sctp_xmit_unacked != NULL)
27677c478bd9Sstevel@tonic-gate 			mp = sctp->sctp_xmit_unacked;
27687c478bd9Sstevel@tonic-gate 		else if (sctp->sctp_xmit_head != NULL)
27697c478bd9Sstevel@tonic-gate 			mp = sctp->sctp_xmit_head->b_cont;
27707c478bd9Sstevel@tonic-gate 		else
27717c478bd9Sstevel@tonic-gate 			mp = NULL;
27725dd46ab5SKacheong Poon 		SCTPS_BUMP_MIB(sctps, sctpInDupAck);
2773769b977dSvi117747 		/*
2774769b977dSvi117747 		 * If we were doing a zero win probe and the win
2775769b977dSvi117747 		 * has now opened to at least MSS, re-transmit the
2776769b977dSvi117747 		 * zero win probe via sctp_rexmit_packet().
2777769b977dSvi117747 		 */
2778769b977dSvi117747 		if (mp != NULL && sctp->sctp_zero_win_probe &&
27796be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		    ntohl(sc->ssc_a_rwnd) >= sctp->sctp_current->sf_pmss) {
2780769b977dSvi117747 			mblk_t	*pkt;
2781769b977dSvi117747 			uint_t	pkt_len;
2782769b977dSvi117747 			mblk_t	*mp1 = mp;
2783769b977dSvi117747 			mblk_t	*meta = sctp->sctp_xmit_head;
2784769b977dSvi117747 
2785769b977dSvi117747 			/*
2786769b977dSvi117747 			 * Reset the RTO since we have been backing-off
2787769b977dSvi117747 			 * to send the ZWP.
2788769b977dSvi117747 			 */
2789769b977dSvi117747 			fp = sctp->sctp_current;
27906be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 			fp->sf_rto = fp->sf_srtt + 4 * fp->sf_rttvar;
27919f13099eSGeorge Shepherd 			SCTP_MAX_RTO(sctp, fp);
2792769b977dSvi117747 			/* Resend the ZWP */
2793769b977dSvi117747 			pkt = sctp_rexmit_packet(sctp, &meta, &mp1, fp,
2794769b977dSvi117747 			    &pkt_len);
2795769b977dSvi117747 			if (pkt == NULL) {
2796f4b3ec61Sdh155122 				SCTP_KSTAT(sctps, sctp_ss_rexmit_failed);
2797769b977dSvi117747 				return (0);
2798769b977dSvi117747 			}
27996be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 			ASSERT(pkt_len <= fp->sf_pmss);
2800769b977dSvi117747 			sctp->sctp_zero_win_probe = B_FALSE;
2801769b977dSvi117747 			sctp->sctp_rxt_nxttsn = sctp->sctp_ltsn;
2802769b977dSvi117747 			sctp->sctp_rxt_maxtsn = sctp->sctp_ltsn;
28036be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 			sctp_set_iplen(sctp, pkt, fp->sf_ixa);
28046be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 			(void) conn_ip_output(pkt, fp->sf_ixa);
2805bd670b35SErik Nordmark 			BUMP_LOCAL(sctp->sctp_opkts);
2806769b977dSvi117747 		}
28077c478bd9Sstevel@tonic-gate 	} else {
2808769b977dSvi117747 		if (sctp->sctp_zero_win_probe) {
2809769b977dSvi117747 			/*
2810769b977dSvi117747 			 * Reset the RTO since we have been backing-off
2811769b977dSvi117747 			 * to send the ZWP.
2812769b977dSvi117747 			 */
2813769b977dSvi117747 			fp = sctp->sctp_current;
28146be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 			fp->sf_rto = fp->sf_srtt + 4 * fp->sf_rttvar;
28159f13099eSGeorge Shepherd 			SCTP_MAX_RTO(sctp, fp);
2816769b977dSvi117747 			sctp->sctp_zero_win_probe = B_FALSE;
2817769b977dSvi117747 			/* This is probably not required */
2818769b977dSvi117747 			if (!sctp->sctp_rexmitting) {
2819769b977dSvi117747 				sctp->sctp_rxt_nxttsn = sctp->sctp_ltsn;
2820769b977dSvi117747 				sctp->sctp_rxt_maxtsn = sctp->sctp_ltsn;
2821769b977dSvi117747 			}
2822769b977dSvi117747 		}
28231d8c4025Svi117747 		acked = sctp_cumack(sctp, cumtsn, &mp);
28247c478bd9Sstevel@tonic-gate 		sctp->sctp_xmit_unacked = mp;
28257c478bd9Sstevel@tonic-gate 		if (acked > 0) {
28267c478bd9Sstevel@tonic-gate 			trysend = 1;
28277c478bd9Sstevel@tonic-gate 			cumack_forward = B_TRUE;
28287c478bd9Sstevel@tonic-gate 			if (fwd_tsn && SEQ_GEQ(sctp->sctp_lastack_rxd,
28297c478bd9Sstevel@tonic-gate 			    sctp->sctp_adv_pap)) {
28307c478bd9Sstevel@tonic-gate 				cumack_forward = B_FALSE;
28317c478bd9Sstevel@tonic-gate 			}
28327c478bd9Sstevel@tonic-gate 		}
28337c478bd9Sstevel@tonic-gate 	}
28347c478bd9Sstevel@tonic-gate 	num_gaps = ntohs(sc->ssc_numfrags);
28359f13099eSGeorge Shepherd 	UPDATE_LOCAL(sctp->sctp_gapcnt, num_gaps);
28367c478bd9Sstevel@tonic-gate 	if (num_gaps == 0 || mp == NULL || !SCTP_CHUNK_ISSENT(mp) ||
28377c478bd9Sstevel@tonic-gate 	    chunklen < (sizeof (*sch) + sizeof (*sc) +
28387c478bd9Sstevel@tonic-gate 	    num_gaps * sizeof (*ssf))) {
28397c478bd9Sstevel@tonic-gate 		goto ret;
28407c478bd9Sstevel@tonic-gate 	}
28411d8c4025Svi117747 #ifdef	DEBUG
28421d8c4025Svi117747 	/*
28431d8c4025Svi117747 	 * Since we delete any message that has been acked completely,
28441d8c4025Svi117747 	 * the unacked chunk must belong to sctp_xmit_head (as
28451d8c4025Svi117747 	 * we don't have a back pointer from the mp to the meta data
28461d8c4025Svi117747 	 * we do this).
28471d8c4025Svi117747 	 */
28481d8c4025Svi117747 	{
28491d8c4025Svi117747 		mblk_t	*mp2 = sctp->sctp_xmit_head->b_cont;
28507c478bd9Sstevel@tonic-gate 
28511d8c4025Svi117747 		while (mp2 != NULL) {
28521d8c4025Svi117747 			if (mp2 == mp)
28531d8c4025Svi117747 				break;
28541d8c4025Svi117747 			mp2 = mp2->b_next;
28551d8c4025Svi117747 		}
28561d8c4025Svi117747 		ASSERT(mp2 != NULL);
28571d8c4025Svi117747 	}
28581d8c4025Svi117747 #endif
28597c478bd9Sstevel@tonic-gate 	ump = sctp->sctp_xmit_head;
28607c478bd9Sstevel@tonic-gate 
28617c478bd9Sstevel@tonic-gate 	/*
28621d8c4025Svi117747 	 * Just remember where we started from, in case we need to call
28631d8c4025Svi117747 	 * sctp_process_uo_gaps() if the gap blocks are unordered.
28641d8c4025Svi117747 	 */
28651d8c4025Svi117747 	mp1 = mp;
28661d8c4025Svi117747 
28671d8c4025Svi117747 	sdc = (sctp_data_hdr_t *)mp->b_rptr;
28681d8c4025Svi117747 	xtsn = ntohl(sdc->sdh_tsn);
28691d8c4025Svi117747 	ASSERT(xtsn == cumtsn + 1);
28701d8c4025Svi117747 
28711d8c4025Svi117747 	/*
28727c478bd9Sstevel@tonic-gate 	 * Go through SACK gaps. They are ordered based on start TSN.
28737c478bd9Sstevel@tonic-gate 	 */
28747c478bd9Sstevel@tonic-gate 	ssf = (sctp_sack_frag_t *)(sc + 1);
28751d8c4025Svi117747 	for (i = 0; i < num_gaps; i++, ssf++) {
28761d8c4025Svi117747 		if (gapstart != 0) {
28771d8c4025Svi117747 			/* check for unordered gap */
28781d8c4025Svi117747 			if (SEQ_LEQ(cumtsn + ntohs(ssf->ssf_start), gapstart)) {
28791d8c4025Svi117747 				acked += sctp_process_uo_gaps(sctp,
28801d8c4025Svi117747 				    cumtsn, ssf, num_gaps - i,
28811d8c4025Svi117747 				    sctp->sctp_xmit_head, mp1,
28821d8c4025Svi117747 				    &trysend, &fast_recovery, gapstart);
28831d8c4025Svi117747 				if (trysend < 0) {
28845dd46ab5SKacheong Poon 					SCTPS_BUMP_MIB(sctps, sctpInAckUnsent);
28851d8c4025Svi117747 					return (-1);
28861d8c4025Svi117747 				}
28871d8c4025Svi117747 				break;
28881d8c4025Svi117747 			}
28891d8c4025Svi117747 		}
28901d8c4025Svi117747 		gapstart = cumtsn + ntohs(ssf->ssf_start);
28911d8c4025Svi117747 		gapend = cumtsn + ntohs(ssf->ssf_end);
28927c478bd9Sstevel@tonic-gate 
28932282c4abSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		/*
28942282c4abSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		 * Sanity checks:
28952282c4abSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		 *
28962282c4abSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		 * 1. SACK for TSN we have not sent - ABORT
28972282c4abSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		 * 2. Invalid or spurious gaps, ignore all gaps
28982282c4abSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		 */
28991d8c4025Svi117747 		if (SEQ_GT(gapstart, sctp->sctp_ltsn - 1) ||
29001d8c4025Svi117747 		    SEQ_GT(gapend, sctp->sctp_ltsn - 1)) {
29015dd46ab5SKacheong Poon 			SCTPS_BUMP_MIB(sctps, sctpInAckUnsent);
29021d8c4025Svi117747 			return (-1);
29032282c4abSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		} else if (SEQ_LT(gapend, gapstart) ||
29042282c4abSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		    SEQ_LEQ(gapstart, cumtsn)) {
29051d8c4025Svi117747 			break;
29061d8c4025Svi117747 		}
29071d8c4025Svi117747 		/*
29081d8c4025Svi117747 		 * Let's start at the current TSN (for the 1st gap we start
29091d8c4025Svi117747 		 * from the cumulative TSN, for subsequent ones we start from
29101d8c4025Svi117747 		 * where the previous gapend was found - second while loop
29111d8c4025Svi117747 		 * below) and walk the transmit list till we find the TSN
29121d8c4025Svi117747 		 * corresponding to gapstart. All the unacked chunks till we
29131d8c4025Svi117747 		 * get to the chunk with TSN == gapstart will have their
29141d8c4025Svi117747 		 * SACKCNT incremented by 1. Note since the gap blocks are
29151d8c4025Svi117747 		 * ordered, we won't be incrementing the SACKCNT for an
29161d8c4025Svi117747 		 * unacked chunk by more than one while processing the gap
29171d8c4025Svi117747 		 * blocks. If the SACKCNT for any unacked chunk exceeds
29181d8c4025Svi117747 		 * the fast retransmit threshold, we will fast retransmit
29191d8c4025Svi117747 		 * after processing all the gap blocks.
29201d8c4025Svi117747 		 */
29212282c4abSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		ASSERT(SEQ_LEQ(xtsn, gapstart));
29227c478bd9Sstevel@tonic-gate 		while (xtsn != gapstart) {
29237c478bd9Sstevel@tonic-gate 			SCTP_CHUNK_SET_SACKCNT(mp, SCTP_CHUNK_SACKCNT(mp) + 1);
2924f4b3ec61Sdh155122 			if (SCTP_CHUNK_SACKCNT(mp) ==
2925f4b3ec61Sdh155122 			    sctps->sctps_fast_rxt_thresh) {
2926c3c17166SGeorge Shepherd 				SCTP_CHUNK_REXMIT(sctp, mp);
29277c478bd9Sstevel@tonic-gate 				sctp->sctp_chk_fast_rexmit = B_TRUE;
29287c478bd9Sstevel@tonic-gate 				trysend = 1;
29297c478bd9Sstevel@tonic-gate 				if (!fast_recovery) {
29307c478bd9Sstevel@tonic-gate 					/*
29317c478bd9Sstevel@tonic-gate 					 * Entering fast recovery.
29327c478bd9Sstevel@tonic-gate 					 */
29337c478bd9Sstevel@tonic-gate 					fp = SCTP_CHUNK_DEST(mp);
29346be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 					fp->sf_ssthresh = fp->sf_cwnd / 2;
29356be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 					if (fp->sf_ssthresh < 2 * fp->sf_pmss) {
29366be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 						fp->sf_ssthresh =
29376be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 						    2 * fp->sf_pmss;
29387c478bd9Sstevel@tonic-gate 					}
29396be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 					fp->sf_cwnd = fp->sf_ssthresh;
29406be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 					fp->sf_pba = 0;
29417c478bd9Sstevel@tonic-gate 					sctp->sctp_recovery_tsn =
29427c478bd9Sstevel@tonic-gate 					    sctp->sctp_ltsn - 1;
29437c478bd9Sstevel@tonic-gate 					fast_recovery = B_TRUE;
29447c478bd9Sstevel@tonic-gate 				}
29457c478bd9Sstevel@tonic-gate 			}
29467c478bd9Sstevel@tonic-gate 
29477c478bd9Sstevel@tonic-gate 			/*
29487c478bd9Sstevel@tonic-gate 			 * Peer may have reneged on this chunk, so un-sack
29497c478bd9Sstevel@tonic-gate 			 * it now. If the peer did renege, we need to
29507c478bd9Sstevel@tonic-gate 			 * readjust unacked.
29517c478bd9Sstevel@tonic-gate 			 */
29527c478bd9Sstevel@tonic-gate 			if (SCTP_CHUNK_ISACKED(mp)) {
29537c478bd9Sstevel@tonic-gate 				chunklen = ntohs(sdc->sdh_len);
29547c478bd9Sstevel@tonic-gate 				fp = SCTP_CHUNK_DEST(mp);
29556be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 				fp->sf_suna += chunklen;
29567c478bd9Sstevel@tonic-gate 				sctp->sctp_unacked += chunklen - sizeof (*sdc);
2957c3c17166SGeorge Shepherd 				SCTP_CHUNK_CLEAR_ACKED(sctp, mp);
29586be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 				if (!fp->sf_timer_running) {
29597c478bd9Sstevel@tonic-gate 					SCTP_FADDR_TIMER_RESTART(sctp, fp,
29606be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 					    fp->sf_rto);
29617c478bd9Sstevel@tonic-gate 				}
29627c478bd9Sstevel@tonic-gate 			}
29637c478bd9Sstevel@tonic-gate 
29647c478bd9Sstevel@tonic-gate 			mp = mp->b_next;
29657c478bd9Sstevel@tonic-gate 			if (mp == NULL) {
29667c478bd9Sstevel@tonic-gate 				ump = ump->b_next;
29671d8c4025Svi117747 				/*
29681d8c4025Svi117747 				 * ump can't be NULL given the sanity check
29692282c4abSchandrasekar marimuthu - Sun Microsystems - Bangalore India 				 * above.  But if it is NULL, it means that
29702282c4abSchandrasekar marimuthu - Sun Microsystems - Bangalore India 				 * there is a data corruption.  We'd better
29712282c4abSchandrasekar marimuthu - Sun Microsystems - Bangalore India 				 * panic.
29721d8c4025Svi117747 				 */
29732282c4abSchandrasekar marimuthu - Sun Microsystems - Bangalore India 				if (ump == NULL) {
29742282c4abSchandrasekar marimuthu - Sun Microsystems - Bangalore India 					panic("Memory corruption detected: gap "
29752282c4abSchandrasekar marimuthu - Sun Microsystems - Bangalore India 					    "start TSN 0x%x missing from the "
29762282c4abSchandrasekar marimuthu - Sun Microsystems - Bangalore India 					    "xmit list: %p", gapstart,
29772282c4abSchandrasekar marimuthu - Sun Microsystems - Bangalore India 					    (void *)sctp);
29782282c4abSchandrasekar marimuthu - Sun Microsystems - Bangalore India 				}
29797c478bd9Sstevel@tonic-gate 				mp = ump->b_cont;
29807c478bd9Sstevel@tonic-gate 			}
29811d8c4025Svi117747 			/*
29821d8c4025Svi117747 			 * mp can't be unsent given the sanity check above.
29831d8c4025Svi117747 			 */
29841d8c4025Svi117747 			ASSERT(SCTP_CHUNK_ISSENT(mp));
29857c478bd9Sstevel@tonic-gate 			sdc = (sctp_data_hdr_t *)mp->b_rptr;
29867c478bd9Sstevel@tonic-gate 			xtsn = ntohl(sdc->sdh_tsn);
29877c478bd9Sstevel@tonic-gate 		}
29881d8c4025Svi117747 		/*
29891d8c4025Svi117747 		 * Now that we have found the chunk with TSN == 'gapstart',
29901d8c4025Svi117747 		 * let's walk till we hit the chunk with TSN == 'gapend'.
29911d8c4025Svi117747 		 * All intermediate chunks will be marked ACKED, if they
29921d8c4025Svi117747 		 * haven't already been.
29931d8c4025Svi117747 		 */
29947c478bd9Sstevel@tonic-gate 		while (SEQ_LEQ(xtsn, gapend)) {
29957c478bd9Sstevel@tonic-gate 			/*
29967c478bd9Sstevel@tonic-gate 			 * SACKed
29977c478bd9Sstevel@tonic-gate 			 */
29987c478bd9Sstevel@tonic-gate 			SCTP_CHUNK_SET_SACKCNT(mp, 0);
29997c478bd9Sstevel@tonic-gate 			if (!SCTP_CHUNK_ISACKED(mp)) {
30007c478bd9Sstevel@tonic-gate 				SCTP_CHUNK_ACKED(mp);
30017c478bd9Sstevel@tonic-gate 
30027c478bd9Sstevel@tonic-gate 				fp = SCTP_CHUNK_DEST(mp);
30037c478bd9Sstevel@tonic-gate 				chunklen = ntohs(sdc->sdh_len);
30046be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 				ASSERT(fp->sf_suna >= chunklen);
30056be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 				fp->sf_suna -= chunklen;
30066be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 				if (fp->sf_suna == 0) {
30077c478bd9Sstevel@tonic-gate 					/* All outstanding data acked. */
30086be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 					fp->sf_pba = 0;
30097c478bd9Sstevel@tonic-gate 					SCTP_FADDR_TIMER_STOP(fp);
30107c478bd9Sstevel@tonic-gate 				}
30116be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 				fp->sf_acked += chunklen;
30127c478bd9Sstevel@tonic-gate 				acked += chunklen;
30137c478bd9Sstevel@tonic-gate 				sctp->sctp_unacked -= chunklen - sizeof (*sdc);
30147c478bd9Sstevel@tonic-gate 				ASSERT(sctp->sctp_unacked >= 0);
30157c478bd9Sstevel@tonic-gate 			}
30161d8c4025Svi117747 			/* Go to the next chunk of the current message */
30177c478bd9Sstevel@tonic-gate 			mp = mp->b_next;
30181d8c4025Svi117747 			/*
30191d8c4025Svi117747 			 * Move to the next message in the transmit list
30201d8c4025Svi117747 			 * if we are done with all the chunks from the current
30211d8c4025Svi117747 			 * message. Note, it is possible to hit the end of the
30221d8c4025Svi117747 			 * transmit list here, i.e. if we have already completed
30232282c4abSchandrasekar marimuthu - Sun Microsystems - Bangalore India 			 * processing the gap block.  But the TSN must be equal
30242282c4abSchandrasekar marimuthu - Sun Microsystems - Bangalore India 			 * to the gapend because of the above sanity check.
30252282c4abSchandrasekar marimuthu - Sun Microsystems - Bangalore India 			 * If it is not equal, it means that some data is
30262282c4abSchandrasekar marimuthu - Sun Microsystems - Bangalore India 			 * missing.
30271d8c4025Svi117747 			 * Also, note that we break here, which means we
30281d8c4025Svi117747 			 * continue processing gap blocks, if any. In case of
30291d8c4025Svi117747 			 * ordered gap blocks there can't be any following
30301d8c4025Svi117747 			 * this (if there is it will fail the sanity check
30311d8c4025Svi117747 			 * above). In case of un-ordered gap blocks we will
30321d8c4025Svi117747 			 * switch to sctp_process_uo_gaps().  In either case
30331d8c4025Svi117747 			 * it should be fine to continue with NULL ump/mp,
30341d8c4025Svi117747 			 * but we just reset it to xmit_head.
30351d8c4025Svi117747 			 */
30367c478bd9Sstevel@tonic-gate 			if (mp == NULL) {
30377c478bd9Sstevel@tonic-gate 				ump = ump->b_next;
30387c478bd9Sstevel@tonic-gate 				if (ump == NULL) {
30392282c4abSchandrasekar marimuthu - Sun Microsystems - Bangalore India 					if (xtsn != gapend) {
30402282c4abSchandrasekar marimuthu - Sun Microsystems - Bangalore India 						panic("Memory corruption "
30412282c4abSchandrasekar marimuthu - Sun Microsystems - Bangalore India 						    "detected: gap end TSN "
30422282c4abSchandrasekar marimuthu - Sun Microsystems - Bangalore India 						    "0x%x missing from the "
30432282c4abSchandrasekar marimuthu - Sun Microsystems - Bangalore India 						    "xmit list: %p", gapend,
30442282c4abSchandrasekar marimuthu - Sun Microsystems - Bangalore India 						    (void *)sctp);
30452282c4abSchandrasekar marimuthu - Sun Microsystems - Bangalore India 					}
30461d8c4025Svi117747 					ump = sctp->sctp_xmit_head;
30471d8c4025Svi117747 					mp = mp1;
30481d8c4025Svi117747 					sdc = (sctp_data_hdr_t *)mp->b_rptr;
30491d8c4025Svi117747 					xtsn = ntohl(sdc->sdh_tsn);
30501d8c4025Svi117747 					break;
30517c478bd9Sstevel@tonic-gate 				}
30527c478bd9Sstevel@tonic-gate 				mp = ump->b_cont;
30537c478bd9Sstevel@tonic-gate 			}
30541d8c4025Svi117747 			/*
30551d8c4025Svi117747 			 * Likewise, we could hit an unsent chunk once we have
30561d8c4025Svi117747 			 * completed processing the gap block. Again, it is
30571d8c4025Svi117747 			 * fine to continue processing gap blocks with mp
30581d8c4025Svi117747 			 * pointing to the unsent chunk, because if there
30591d8c4025Svi117747 			 * are more ordered gap blocks, they will fail the
30601d8c4025Svi117747 			 * sanity check, and if there are un-ordered gap blocks,
30611d8c4025Svi117747 			 * we will continue processing in sctp_process_uo_gaps()
30621d8c4025Svi117747 			 * We just reset the mp to the one we started with.
30631d8c4025Svi117747 			 */
30647c478bd9Sstevel@tonic-gate 			if (!SCTP_CHUNK_ISSENT(mp)) {
30651d8c4025Svi117747 				ASSERT(xtsn == gapend);
30661d8c4025Svi117747 				ump = sctp->sctp_xmit_head;
30671d8c4025Svi117747 				mp = mp1;
30681d8c4025Svi117747 				sdc = (sctp_data_hdr_t *)mp->b_rptr;
30691d8c4025Svi117747 				xtsn = ntohl(sdc->sdh_tsn);
30701d8c4025Svi117747 				break;
30717c478bd9Sstevel@tonic-gate 			}
30727c478bd9Sstevel@tonic-gate 			sdc = (sctp_data_hdr_t *)mp->b_rptr;
30737c478bd9Sstevel@tonic-gate 			xtsn = ntohl(sdc->sdh_tsn);
30747c478bd9Sstevel@tonic-gate 		}
30757c478bd9Sstevel@tonic-gate 	}
30767c478bd9Sstevel@tonic-gate 	if (sctp->sctp_prsctp_aware)
30777c478bd9Sstevel@tonic-gate 		sctp_check_abandoned_data(sctp, sctp->sctp_current);
30787c478bd9Sstevel@tonic-gate 	if (sctp->sctp_chk_fast_rexmit)
30797c478bd9Sstevel@tonic-gate 		sctp_fast_rexmit(sctp);
30807c478bd9Sstevel@tonic-gate ret:
30817c478bd9Sstevel@tonic-gate 	trysend += sctp_set_frwnd(sctp, ntohl(sc->ssc_a_rwnd));
30827c478bd9Sstevel@tonic-gate 
30837c478bd9Sstevel@tonic-gate 	/*
30847c478bd9Sstevel@tonic-gate 	 * If receive window is closed while there is unsent data,
30857c478bd9Sstevel@tonic-gate 	 * set a timer for doing zero window probes.
30867c478bd9Sstevel@tonic-gate 	 */
30877c478bd9Sstevel@tonic-gate 	if (sctp->sctp_frwnd == 0 && sctp->sctp_unacked == 0 &&
30887c478bd9Sstevel@tonic-gate 	    sctp->sctp_unsent != 0) {
30897c478bd9Sstevel@tonic-gate 		SCTP_FADDR_TIMER_RESTART(sctp, sctp->sctp_current,
30906be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		    sctp->sctp_current->sf_rto);
30917c478bd9Sstevel@tonic-gate 	}
30927c478bd9Sstevel@tonic-gate 
30937c478bd9Sstevel@tonic-gate 	/*
30947c478bd9Sstevel@tonic-gate 	 * Set cwnd for all destinations.
30957c478bd9Sstevel@tonic-gate 	 * Congestion window gets increased only when cumulative
30967c478bd9Sstevel@tonic-gate 	 * TSN moves forward, we're not in fast recovery, and
30977c478bd9Sstevel@tonic-gate 	 * cwnd has been fully utilized (almost fully, need to allow
30987c478bd9Sstevel@tonic-gate 	 * some leeway due to non-MSS sized messages).
30997c478bd9Sstevel@tonic-gate 	 */
31006be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 	if (sctp->sctp_current->sf_acked == acked) {
31017c478bd9Sstevel@tonic-gate 		/*
31027c478bd9Sstevel@tonic-gate 		 * Fast-path, only data sent to sctp_current got acked.
31037c478bd9Sstevel@tonic-gate 		 */
31047c478bd9Sstevel@tonic-gate 		fp = sctp->sctp_current;
31057c478bd9Sstevel@tonic-gate 		if (cumack_forward && !fast_recovery &&
31066be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		    (fp->sf_acked + fp->sf_suna > fp->sf_cwnd - fp->sf_pmss)) {
31076be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 			if (fp->sf_cwnd < fp->sf_ssthresh) {
31087c478bd9Sstevel@tonic-gate 				/*
31097c478bd9Sstevel@tonic-gate 				 * Slow start
31107c478bd9Sstevel@tonic-gate 				 */
31116be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 				if (fp->sf_acked > fp->sf_pmss) {
31126be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 					fp->sf_cwnd += fp->sf_pmss;
31137c478bd9Sstevel@tonic-gate 				} else {
31146be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 					fp->sf_cwnd += fp->sf_acked;
31157c478bd9Sstevel@tonic-gate 				}
31166be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 				fp->sf_cwnd = MIN(fp->sf_cwnd,
31176be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 				    sctp->sctp_cwnd_max);
31187c478bd9Sstevel@tonic-gate 			} else {
31197c478bd9Sstevel@tonic-gate 				/*
31207c478bd9Sstevel@tonic-gate 				 * Congestion avoidance
31217c478bd9Sstevel@tonic-gate 				 */
31226be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 				fp->sf_pba += fp->sf_acked;
31236be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 				if (fp->sf_pba >= fp->sf_cwnd) {
31246be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 					fp->sf_pba -= fp->sf_cwnd;
31256be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 					fp->sf_cwnd += fp->sf_pmss;
31266be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 					fp->sf_cwnd = MIN(fp->sf_cwnd,
31277c478bd9Sstevel@tonic-gate 					    sctp->sctp_cwnd_max);
31287c478bd9Sstevel@tonic-gate 				}
31297c478bd9Sstevel@tonic-gate 			}
31307c478bd9Sstevel@tonic-gate 		}
31317c478bd9Sstevel@tonic-gate 		/*
31327c478bd9Sstevel@tonic-gate 		 * Limit the burst of transmitted data segments.
31337c478bd9Sstevel@tonic-gate 		 */
31346be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		if (fp->sf_suna + sctps->sctps_maxburst * fp->sf_pmss <
31356be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		    fp->sf_cwnd) {
31366be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 			fp->sf_cwnd = fp->sf_suna + sctps->sctps_maxburst *
31376be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 			    fp->sf_pmss;
31387c478bd9Sstevel@tonic-gate 		}
31396be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		fp->sf_acked = 0;
314077c67f2fSkcpoon 		goto check_ss_rxmit;
31417c478bd9Sstevel@tonic-gate 	}
31426be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 	for (fp = sctp->sctp_faddrs; fp != NULL; fp = fp->sf_next) {
31436be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		if (cumack_forward && fp->sf_acked && !fast_recovery &&
31446be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		    (fp->sf_acked + fp->sf_suna > fp->sf_cwnd - fp->sf_pmss)) {
31456be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 			if (fp->sf_cwnd < fp->sf_ssthresh) {
31466be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 				if (fp->sf_acked > fp->sf_pmss) {
31476be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 					fp->sf_cwnd += fp->sf_pmss;
31487c478bd9Sstevel@tonic-gate 				} else {
31496be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 					fp->sf_cwnd += fp->sf_acked;
31507c478bd9Sstevel@tonic-gate 				}
31516be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 				fp->sf_cwnd = MIN(fp->sf_cwnd,
31526be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 				    sctp->sctp_cwnd_max);
31537c478bd9Sstevel@tonic-gate 			} else {
31546be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 				fp->sf_pba += fp->sf_acked;
31556be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 				if (fp->sf_pba >= fp->sf_cwnd) {
31566be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 					fp->sf_pba -= fp->sf_cwnd;
31576be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 					fp->sf_cwnd += fp->sf_pmss;
31586be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 					fp->sf_cwnd = MIN(fp->sf_cwnd,
31597c478bd9Sstevel@tonic-gate 					    sctp->sctp_cwnd_max);
31607c478bd9Sstevel@tonic-gate 				}
31617c478bd9Sstevel@tonic-gate 			}
31627c478bd9Sstevel@tonic-gate 		}
31636be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		if (fp->sf_suna + sctps->sctps_maxburst * fp->sf_pmss <
31646be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		    fp->sf_cwnd) {
31656be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 			fp->sf_cwnd = fp->sf_suna + sctps->sctps_maxburst *
31666be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 			    fp->sf_pmss;
31677c478bd9Sstevel@tonic-gate 		}
31686be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		fp->sf_acked = 0;
31697c478bd9Sstevel@tonic-gate 	}
31701d19ca10Svi117747 	fp = sctp->sctp_current;
317177c67f2fSkcpoon check_ss_rxmit:
317277c67f2fSkcpoon 	/*
317377c67f2fSkcpoon 	 * If this is a SACK following a timeout, check if there are
317477c67f2fSkcpoon 	 * still unacked chunks (sent before the timeout) that we can
317577c67f2fSkcpoon 	 * send.
317677c67f2fSkcpoon 	 */
317777c67f2fSkcpoon 	if (sctp->sctp_rexmitting) {
317877c67f2fSkcpoon 		if (SEQ_LT(sctp->sctp_lastack_rxd, sctp->sctp_rxt_maxtsn)) {
317977c67f2fSkcpoon 			/*
318077c67f2fSkcpoon 			 * As we are in retransmission phase, we may get a
318177c67f2fSkcpoon 			 * SACK which indicates some new chunks are received
318277c67f2fSkcpoon 			 * but cum_tsn does not advance.  During this
318377c67f2fSkcpoon 			 * phase, the other side advances cum_tsn only because
318477c67f2fSkcpoon 			 * it receives our retransmitted chunks.  Only
318577c67f2fSkcpoon 			 * this signals that some chunks are still
318677c67f2fSkcpoon 			 * missing.
318777c67f2fSkcpoon 			 */
318812f47623Skcpoon 			if (cumack_forward) {
31896be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 				fp->sf_rxt_unacked -= acked;
319077c67f2fSkcpoon 				sctp_ss_rexmit(sctp);
319112f47623Skcpoon 			}
319277c67f2fSkcpoon 		} else {
319377c67f2fSkcpoon 			sctp->sctp_rexmitting = B_FALSE;
319477c67f2fSkcpoon 			sctp->sctp_rxt_nxttsn = sctp->sctp_ltsn;
319577c67f2fSkcpoon 			sctp->sctp_rxt_maxtsn = sctp->sctp_ltsn;
31966be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 			fp->sf_rxt_unacked = 0;
319777c67f2fSkcpoon 		}
319877c67f2fSkcpoon 	}
31997c478bd9Sstevel@tonic-gate 	return (trysend);
32007c478bd9Sstevel@tonic-gate }
32017c478bd9Sstevel@tonic-gate 
32027c478bd9Sstevel@tonic-gate /*
32037c478bd9Sstevel@tonic-gate  * Returns 0 if the caller should stop processing any more chunks,
32047c478bd9Sstevel@tonic-gate  * 1 if the caller should skip this chunk and continue processing.
32057c478bd9Sstevel@tonic-gate  */
32067c478bd9Sstevel@tonic-gate static int
32077c478bd9Sstevel@tonic-gate sctp_strange_chunk(sctp_t *sctp, sctp_chunk_hdr_t *ch, sctp_faddr_t *fp)
32087c478bd9Sstevel@tonic-gate {
32097c478bd9Sstevel@tonic-gate 	size_t len;
32107c478bd9Sstevel@tonic-gate 
32117c478bd9Sstevel@tonic-gate 	BUMP_LOCAL(sctp->sctp_ibchunks);
32127c478bd9Sstevel@tonic-gate 	/* check top two bits for action required */
32137c478bd9Sstevel@tonic-gate 	if (ch->sch_id & 0x40) {	/* also matches 0xc0 */
32147c478bd9Sstevel@tonic-gate 		len = ntohs(ch->sch_len);
32157f093707Skcpoon 		sctp_add_err(sctp, SCTP_ERR_UNREC_CHUNK, ch, len, fp);
32167f093707Skcpoon 
32177c478bd9Sstevel@tonic-gate 		if ((ch->sch_id & 0xc0) == 0xc0) {
32187c478bd9Sstevel@tonic-gate 			/* skip and continue */
32197c478bd9Sstevel@tonic-gate 			return (1);
32207c478bd9Sstevel@tonic-gate 		} else {
32217c478bd9Sstevel@tonic-gate 			/* stop processing */
32227c478bd9Sstevel@tonic-gate 			return (0);
32237c478bd9Sstevel@tonic-gate 		}
32247c478bd9Sstevel@tonic-gate 	}
32257c478bd9Sstevel@tonic-gate 	if (ch->sch_id & 0x80) {
32267c478bd9Sstevel@tonic-gate 		/* skip and continue, no error */
32277c478bd9Sstevel@tonic-gate 		return (1);
32287c478bd9Sstevel@tonic-gate 	}
32297c478bd9Sstevel@tonic-gate 	/* top two bits are clear; stop processing and no error */
32307c478bd9Sstevel@tonic-gate 	return (0);
32317c478bd9Sstevel@tonic-gate }
32327c478bd9Sstevel@tonic-gate 
32337c478bd9Sstevel@tonic-gate /*
32347c478bd9Sstevel@tonic-gate  * Basic sanity checks on all input chunks and parameters: they must
32357c478bd9Sstevel@tonic-gate  * be of legitimate size for their purported type, and must follow
32367c478bd9Sstevel@tonic-gate  * ordering conventions as defined in rfc2960.
32377c478bd9Sstevel@tonic-gate  *
32387c478bd9Sstevel@tonic-gate  * Returns 1 if the chunk and all encloded params are legitimate,
32397c478bd9Sstevel@tonic-gate  * 0 otherwise.
32407c478bd9Sstevel@tonic-gate  */
32417c478bd9Sstevel@tonic-gate /*ARGSUSED*/
32427c478bd9Sstevel@tonic-gate static int
32437c478bd9Sstevel@tonic-gate sctp_check_input(sctp_t *sctp, sctp_chunk_hdr_t *ch, ssize_t len, int first)
32447c478bd9Sstevel@tonic-gate {
32457c478bd9Sstevel@tonic-gate 	sctp_parm_hdr_t	*ph;
32467c478bd9Sstevel@tonic-gate 	void		*p = NULL;
32477c478bd9Sstevel@tonic-gate 	ssize_t		clen;
32487c478bd9Sstevel@tonic-gate 	uint16_t	ch_len;
32497c478bd9Sstevel@tonic-gate 
32507c478bd9Sstevel@tonic-gate 	ch_len = ntohs(ch->sch_len);
32517c478bd9Sstevel@tonic-gate 	if (ch_len > len) {
32527c478bd9Sstevel@tonic-gate 		return (0);
32537c478bd9Sstevel@tonic-gate 	}
32547c478bd9Sstevel@tonic-gate 
32557c478bd9Sstevel@tonic-gate 	switch (ch->sch_id) {
32567c478bd9Sstevel@tonic-gate 	case CHUNK_DATA:
32577c478bd9Sstevel@tonic-gate 		if (ch_len < sizeof (sctp_data_hdr_t)) {
32587c478bd9Sstevel@tonic-gate 			return (0);
32597c478bd9Sstevel@tonic-gate 		}
32607c478bd9Sstevel@tonic-gate 		return (1);
32617c478bd9Sstevel@tonic-gate 	case CHUNK_INIT:
32627c478bd9Sstevel@tonic-gate 	case CHUNK_INIT_ACK:
32637c478bd9Sstevel@tonic-gate 		{
32647c478bd9Sstevel@tonic-gate 			ssize_t	remlen = len;
32657c478bd9Sstevel@tonic-gate 
32667c478bd9Sstevel@tonic-gate 			/*
32677c478bd9Sstevel@tonic-gate 			 * INIT and INIT-ACK chunks must not be bundled with
32687c478bd9Sstevel@tonic-gate 			 * any other.
32697c478bd9Sstevel@tonic-gate 			 */
32707c478bd9Sstevel@tonic-gate 			if (!first || sctp_next_chunk(ch, &remlen) != NULL ||
32717c478bd9Sstevel@tonic-gate 			    (ch_len < (sizeof (*ch) +
32727c478bd9Sstevel@tonic-gate 			    sizeof (sctp_init_chunk_t)))) {
32737c478bd9Sstevel@tonic-gate 				return (0);
32747c478bd9Sstevel@tonic-gate 			}
32757c478bd9Sstevel@tonic-gate 			/* may have params that need checking */
32767c478bd9Sstevel@tonic-gate 			p = (char *)(ch + 1) + sizeof (sctp_init_chunk_t);
32777c478bd9Sstevel@tonic-gate 			clen = ch_len - (sizeof (*ch) +
32787c478bd9Sstevel@tonic-gate 			    sizeof (sctp_init_chunk_t));
32797c478bd9Sstevel@tonic-gate 		}
32807c478bd9Sstevel@tonic-gate 		break;
32817c478bd9Sstevel@tonic-gate 	case CHUNK_SACK:
32827c478bd9Sstevel@tonic-gate 		if (ch_len < (sizeof (*ch) + sizeof (sctp_sack_chunk_t))) {
32837c478bd9Sstevel@tonic-gate 			return (0);
32847c478bd9Sstevel@tonic-gate 		}
32857c478bd9Sstevel@tonic-gate 		/* dup and gap reports checked by got_sack() */
32867c478bd9Sstevel@tonic-gate 		return (1);
32877c478bd9Sstevel@tonic-gate 	case CHUNK_SHUTDOWN:
32887c478bd9Sstevel@tonic-gate 		if (ch_len < (sizeof (*ch) + sizeof (uint32_t))) {
32897c478bd9Sstevel@tonic-gate 			return (0);
32907c478bd9Sstevel@tonic-gate 		}
32917c478bd9Sstevel@tonic-gate 		return (1);
32927c478bd9Sstevel@tonic-gate 	case CHUNK_ABORT:
32937c478bd9Sstevel@tonic-gate 	case CHUNK_ERROR:
32947c478bd9Sstevel@tonic-gate 		if (ch_len < sizeof (*ch)) {
32957c478bd9Sstevel@tonic-gate 			return (0);
32967c478bd9Sstevel@tonic-gate 		}
32977c478bd9Sstevel@tonic-gate 		/* may have params that need checking */
32987c478bd9Sstevel@tonic-gate 		p = ch + 1;
32997c478bd9Sstevel@tonic-gate 		clen = ch_len - sizeof (*ch);
33007c478bd9Sstevel@tonic-gate 		break;
33017c478bd9Sstevel@tonic-gate 	case CHUNK_ECNE:
33027c478bd9Sstevel@tonic-gate 	case CHUNK_CWR:
33037c478bd9Sstevel@tonic-gate 	case CHUNK_HEARTBEAT:
33047c478bd9Sstevel@tonic-gate 	case CHUNK_HEARTBEAT_ACK:
33057c478bd9Sstevel@tonic-gate 	/* Full ASCONF chunk and parameter checks are in asconf.c */
33067c478bd9Sstevel@tonic-gate 	case CHUNK_ASCONF:
33077c478bd9Sstevel@tonic-gate 	case CHUNK_ASCONF_ACK:
33087c478bd9Sstevel@tonic-gate 		if (ch_len < sizeof (*ch)) {
33097c478bd9Sstevel@tonic-gate 			return (0);
33107c478bd9Sstevel@tonic-gate 		}
33117c478bd9Sstevel@tonic-gate 		/* heartbeat data checked by process_heartbeat() */
33127c478bd9Sstevel@tonic-gate 		return (1);
33137c478bd9Sstevel@tonic-gate 	case CHUNK_SHUTDOWN_COMPLETE:
33147c478bd9Sstevel@tonic-gate 		{
33157c478bd9Sstevel@tonic-gate 			ssize_t remlen = len;
33167c478bd9Sstevel@tonic-gate 
33177c478bd9Sstevel@tonic-gate 			/*
33187c478bd9Sstevel@tonic-gate 			 * SHUTDOWN-COMPLETE chunk must not be bundled with any
33197c478bd9Sstevel@tonic-gate 			 * other
33207c478bd9Sstevel@tonic-gate 			 */
33217c478bd9Sstevel@tonic-gate 			if (!first || sctp_next_chunk(ch, &remlen) != NULL ||
33227c478bd9Sstevel@tonic-gate 			    ch_len < sizeof (*ch)) {
33237c478bd9Sstevel@tonic-gate 				return (0);
33247c478bd9Sstevel@tonic-gate 			}
33257c478bd9Sstevel@tonic-gate 		}
33267c478bd9Sstevel@tonic-gate 		return (1);
33277c478bd9Sstevel@tonic-gate 	case CHUNK_COOKIE:
33287c478bd9Sstevel@tonic-gate 	case CHUNK_COOKIE_ACK:
33297c478bd9Sstevel@tonic-gate 	case CHUNK_SHUTDOWN_ACK:
33307c478bd9Sstevel@tonic-gate 		if (ch_len < sizeof (*ch) || !first) {
33317c478bd9Sstevel@tonic-gate 			return (0);
33327c478bd9Sstevel@tonic-gate 		}
33337c478bd9Sstevel@tonic-gate 		return (1);
33347c478bd9Sstevel@tonic-gate 	case CHUNK_FORWARD_TSN:
33357c478bd9Sstevel@tonic-gate 		if (ch_len < (sizeof (*ch) + sizeof (uint32_t)))
33367c478bd9Sstevel@tonic-gate 			return (0);
33377c478bd9Sstevel@tonic-gate 		return (1);
33387c478bd9Sstevel@tonic-gate 	default:
33397c478bd9Sstevel@tonic-gate 		return (1);	/* handled by strange_chunk() */
33407c478bd9Sstevel@tonic-gate 	}
33417c478bd9Sstevel@tonic-gate 
33427c478bd9Sstevel@tonic-gate 	/* check and byteorder parameters */
33437c478bd9Sstevel@tonic-gate 	if (clen <= 0) {
33447c478bd9Sstevel@tonic-gate 		return (1);
33457c478bd9Sstevel@tonic-gate 	}
33467c478bd9Sstevel@tonic-gate 	ASSERT(p != NULL);
33477c478bd9Sstevel@tonic-gate 
33487c478bd9Sstevel@tonic-gate 	ph = p;
33497c478bd9Sstevel@tonic-gate 	while (ph != NULL && clen > 0) {
33507c478bd9Sstevel@tonic-gate 		ch_len = ntohs(ph->sph_len);
33517c478bd9Sstevel@tonic-gate 		if (ch_len > len || ch_len < sizeof (*ph)) {
33527c478bd9Sstevel@tonic-gate 			return (0);
33537c478bd9Sstevel@tonic-gate 		}
33547c478bd9Sstevel@tonic-gate 		ph = sctp_next_parm(ph, &clen);
33557c478bd9Sstevel@tonic-gate 	}
33567c478bd9Sstevel@tonic-gate 
33577c478bd9Sstevel@tonic-gate 	/* All OK */
33587c478bd9Sstevel@tonic-gate 	return (1);
33597c478bd9Sstevel@tonic-gate }
33607c478bd9Sstevel@tonic-gate 
33617c478bd9Sstevel@tonic-gate static mblk_t *
3362bd670b35SErik Nordmark sctp_check_in_policy(mblk_t *mp, ip_recv_attr_t *ira, ip_stack_t *ipst)
33637c478bd9Sstevel@tonic-gate {
33647c478bd9Sstevel@tonic-gate 	boolean_t policy_present;
33657c478bd9Sstevel@tonic-gate 	ipha_t *ipha;
33667c478bd9Sstevel@tonic-gate 	ip6_t *ip6h;
3367bd670b35SErik Nordmark 	netstack_t	*ns = ipst->ips_netstack;
3368bd670b35SErik Nordmark 	ipsec_stack_t	*ipss = ns->netstack_ipsec;
33697c478bd9Sstevel@tonic-gate 
33707c478bd9Sstevel@tonic-gate 	if (IPH_HDR_VERSION(mp->b_rptr) == IPV4_VERSION) {
3371f4b3ec61Sdh155122 		policy_present = ipss->ipsec_inbound_v4_policy_present;
33727c478bd9Sstevel@tonic-gate 		ipha = (ipha_t *)mp->b_rptr;
33737c478bd9Sstevel@tonic-gate 		ip6h = NULL;
33747c478bd9Sstevel@tonic-gate 	} else {
3375f4b3ec61Sdh155122 		policy_present = ipss->ipsec_inbound_v6_policy_present;
33767c478bd9Sstevel@tonic-gate 		ipha = NULL;
33777c478bd9Sstevel@tonic-gate 		ip6h = (ip6_t *)mp->b_rptr;
33787c478bd9Sstevel@tonic-gate 	}
33797c478bd9Sstevel@tonic-gate 
3380bd670b35SErik Nordmark 	if (policy_present) {
33817c478bd9Sstevel@tonic-gate 		/*
33827c478bd9Sstevel@tonic-gate 		 * The conn_t parameter is NULL because we already know
33837c478bd9Sstevel@tonic-gate 		 * nobody's home.
33847c478bd9Sstevel@tonic-gate 		 */
3385bd670b35SErik Nordmark 		mp = ipsec_check_global_policy(mp, (conn_t *)NULL,
3386bd670b35SErik Nordmark 		    ipha, ip6h, ira, ns);
3387bd670b35SErik Nordmark 		if (mp == NULL)
33887c478bd9Sstevel@tonic-gate 			return (NULL);
33897c478bd9Sstevel@tonic-gate 	}
33907c478bd9Sstevel@tonic-gate 	return (mp);
33917c478bd9Sstevel@tonic-gate }
33927c478bd9Sstevel@tonic-gate 
33937c478bd9Sstevel@tonic-gate /* Handle out-of-the-blue packets */
33947c478bd9Sstevel@tonic-gate void
3395bd670b35SErik Nordmark sctp_ootb_input(mblk_t *mp, ip_recv_attr_t *ira, ip_stack_t *ipst)
33967c478bd9Sstevel@tonic-gate {
33977c478bd9Sstevel@tonic-gate 	sctp_t			*sctp;
33987c478bd9Sstevel@tonic-gate 	sctp_chunk_hdr_t	*ch;
33997c478bd9Sstevel@tonic-gate 	sctp_hdr_t		*sctph;
34007c478bd9Sstevel@tonic-gate 	in6_addr_t		src, dst;
3401bd670b35SErik Nordmark 	uint_t			ip_hdr_len = ira->ira_ip_hdr_length;
34027c478bd9Sstevel@tonic-gate 	ssize_t			mlen;
3403f4b3ec61Sdh155122 	sctp_stack_t		*sctps;
3404bd670b35SErik Nordmark 	boolean_t		secure;
3405bd670b35SErik Nordmark 	zoneid_t		zoneid = ira->ira_zoneid;
3406bd670b35SErik Nordmark 	uchar_t			*rptr;
34077c478bd9Sstevel@tonic-gate 
3408bd670b35SErik Nordmark 	ASSERT(ira->ira_ill == NULL);
3409bd670b35SErik Nordmark 
3410bd670b35SErik Nordmark 	secure = ira->ira_flags & IRAF_IPSEC_SECURE;
3411bd670b35SErik Nordmark 
3412f4b3ec61Sdh155122 	sctps = ipst->ips_netstack->netstack_sctp;
3413f4b3ec61Sdh155122 
34145dd46ab5SKacheong Poon 	SCTPS_BUMP_MIB(sctps, sctpOutOfBlue);
34155dd46ab5SKacheong Poon 	SCTPS_BUMP_MIB(sctps, sctpInSCTPPkts);
3416f4b3ec61Sdh155122 
34177c478bd9Sstevel@tonic-gate 	if (mp->b_cont != NULL) {
34187c478bd9Sstevel@tonic-gate 		/*
34197c478bd9Sstevel@tonic-gate 		 * All subsequent code is vastly simplified if it can
34207c478bd9Sstevel@tonic-gate 		 * assume a single contiguous chunk of data.
34217c478bd9Sstevel@tonic-gate 		 */
34227c478bd9Sstevel@tonic-gate 		if (pullupmsg(mp, -1) == 0) {
3423bd670b35SErik Nordmark 			BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsInDiscards);
3424bd670b35SErik Nordmark 			ip_drop_input("ipIfStatsInDiscards", mp, NULL);
3425bd670b35SErik Nordmark 			freemsg(mp);
34267c478bd9Sstevel@tonic-gate 			return;
34277c478bd9Sstevel@tonic-gate 		}
34287c478bd9Sstevel@tonic-gate 	}
34297c478bd9Sstevel@tonic-gate 
3430bd670b35SErik Nordmark 	rptr = mp->b_rptr;
3431bd670b35SErik Nordmark 	sctph = ((sctp_hdr_t *)&rptr[ip_hdr_len]);
3432bd670b35SErik Nordmark 	if (ira->ira_flags & IRAF_IS_IPV4) {
3433bd670b35SErik Nordmark 		ipha_t *ipha;
3434bd670b35SErik Nordmark 
3435bd670b35SErik Nordmark 		ipha = (ipha_t *)rptr;
3436bd670b35SErik Nordmark 		IN6_IPADDR_TO_V4MAPPED(ipha->ipha_src, &src);
3437bd670b35SErik Nordmark 		IN6_IPADDR_TO_V4MAPPED(ipha->ipha_dst, &dst);
3438bd670b35SErik Nordmark 	} else {
3439bd670b35SErik Nordmark 		ip6_t *ip6h;
3440bd670b35SErik Nordmark 
3441bd670b35SErik Nordmark 		ip6h = (ip6_t *)rptr;
3442bd670b35SErik Nordmark 		src = ip6h->ip6_src;
3443bd670b35SErik Nordmark 		dst = ip6h->ip6_dst;
3444bd670b35SErik Nordmark 	}
3445bd670b35SErik Nordmark 
34467c478bd9Sstevel@tonic-gate 	mlen = mp->b_wptr - (uchar_t *)(sctph + 1);
34477c478bd9Sstevel@tonic-gate 	if ((ch = sctp_first_chunk((uchar_t *)(sctph + 1), mlen)) == NULL) {
34487c478bd9Sstevel@tonic-gate 		dprint(3, ("sctp_ootb_input: invalid packet\n"));
3449bd670b35SErik Nordmark 		BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsInDiscards);
3450bd670b35SErik Nordmark 		ip_drop_input("ipIfStatsInDiscards", mp, NULL);
3451bd670b35SErik Nordmark 		freemsg(mp);
34527c478bd9Sstevel@tonic-gate 		return;
34537c478bd9Sstevel@tonic-gate 	}
34547c478bd9Sstevel@tonic-gate 
34557c478bd9Sstevel@tonic-gate 	switch (ch->sch_id) {
34567c478bd9Sstevel@tonic-gate 	case CHUNK_INIT:
34577c478bd9Sstevel@tonic-gate 		/* no listener; send abort  */
3458bd670b35SErik Nordmark 		if (secure && sctp_check_in_policy(mp, ira, ipst) == NULL)
34597c478bd9Sstevel@tonic-gate 			return;
3460bd670b35SErik Nordmark 		sctp_ootb_send_abort(sctp_init2vtag(ch), 0,
3461bd670b35SErik Nordmark 		    NULL, 0, mp, 0, B_TRUE, ira, ipst);
34627c478bd9Sstevel@tonic-gate 		break;
34637c478bd9Sstevel@tonic-gate 	case CHUNK_INIT_ACK:
34647c478bd9Sstevel@tonic-gate 		/* check for changed src addr */
3465e35d2278Svi117747 		sctp = sctp_addrlist2sctp(mp, sctph, ch, zoneid, sctps);
34667c478bd9Sstevel@tonic-gate 		if (sctp != NULL) {
34677c478bd9Sstevel@tonic-gate 			/* success; proceed to normal path */
34687c478bd9Sstevel@tonic-gate 			mutex_enter(&sctp->sctp_lock);
34697c478bd9Sstevel@tonic-gate 			if (sctp->sctp_running) {
3470bd670b35SErik Nordmark 				sctp_add_recvq(sctp, mp, B_FALSE, ira);
34717c478bd9Sstevel@tonic-gate 				mutex_exit(&sctp->sctp_lock);
34727c478bd9Sstevel@tonic-gate 			} else {
34737c478bd9Sstevel@tonic-gate 				/*
34747c478bd9Sstevel@tonic-gate 				 * If the source address is changed, we
34757c478bd9Sstevel@tonic-gate 				 * don't need to worry too much about
34767c478bd9Sstevel@tonic-gate 				 * out of order processing.  So we don't
34777c478bd9Sstevel@tonic-gate 				 * check if the recvq is empty or not here.
34787c478bd9Sstevel@tonic-gate 				 */
34797c478bd9Sstevel@tonic-gate 				sctp->sctp_running = B_TRUE;
34807c478bd9Sstevel@tonic-gate 				mutex_exit(&sctp->sctp_lock);
3481bd670b35SErik Nordmark 				sctp_input_data(sctp, mp, ira);
34827c478bd9Sstevel@tonic-gate 				WAKE_SCTP(sctp);
34837c478bd9Sstevel@tonic-gate 			}
34847c478bd9Sstevel@tonic-gate 			SCTP_REFRELE(sctp);
34857c478bd9Sstevel@tonic-gate 			return;
34867c478bd9Sstevel@tonic-gate 		}
34877c478bd9Sstevel@tonic-gate 		/* else bogus init ack; drop it */
34887c478bd9Sstevel@tonic-gate 		break;
34897c478bd9Sstevel@tonic-gate 	case CHUNK_SHUTDOWN_ACK:
3490bd670b35SErik Nordmark 		if (secure && sctp_check_in_policy(mp, ira, ipst) == NULL)
34917c478bd9Sstevel@tonic-gate 			return;
3492bd670b35SErik Nordmark 		sctp_ootb_shutdown_ack(mp, ip_hdr_len, ira, ipst);
34937c478bd9Sstevel@tonic-gate 		return;
34947c478bd9Sstevel@tonic-gate 	case CHUNK_ERROR:
34957c478bd9Sstevel@tonic-gate 	case CHUNK_ABORT:
34967c478bd9Sstevel@tonic-gate 	case CHUNK_COOKIE_ACK:
34977c478bd9Sstevel@tonic-gate 	case CHUNK_SHUTDOWN_COMPLETE:
34987c478bd9Sstevel@tonic-gate 		break;
34997c478bd9Sstevel@tonic-gate 	default:
3500bd670b35SErik Nordmark 		if (secure && sctp_check_in_policy(mp, ira, ipst) == NULL)
35017c478bd9Sstevel@tonic-gate 			return;
3502bd670b35SErik Nordmark 		sctp_ootb_send_abort(sctph->sh_verf, 0,
3503bd670b35SErik Nordmark 		    NULL, 0, mp, 0, B_TRUE, ira, ipst);
35047c478bd9Sstevel@tonic-gate 		break;
35057c478bd9Sstevel@tonic-gate 	}
35067c478bd9Sstevel@tonic-gate 	freemsg(mp);
35077c478bd9Sstevel@tonic-gate }
35087c478bd9Sstevel@tonic-gate 
3509bd670b35SErik Nordmark /*
3510bd670b35SErik Nordmark  * Handle sctp packets.
3511bd670b35SErik Nordmark  * Note that we rele the sctp_t (the caller got a reference on it).
3512bd670b35SErik Nordmark  */
35137c478bd9Sstevel@tonic-gate void
3514bd670b35SErik Nordmark sctp_input(conn_t *connp, ipha_t *ipha, ip6_t *ip6h, mblk_t *mp,
3515bd670b35SErik Nordmark     ip_recv_attr_t *ira)
35167c478bd9Sstevel@tonic-gate {
35177c478bd9Sstevel@tonic-gate 	sctp_t		*sctp = CONN2SCTP(connp);
3518bd670b35SErik Nordmark 	boolean_t	secure;
3519bd670b35SErik Nordmark 	ill_t		*ill = ira->ira_ill;
3520bd670b35SErik Nordmark 	ip_stack_t	*ipst = ill->ill_ipst;
3521f4b3ec61Sdh155122 	ipsec_stack_t	*ipss = ipst->ips_netstack->netstack_ipsec;
3522bd670b35SErik Nordmark 	iaflags_t	iraflags = ira->ira_flags;
3523bd670b35SErik Nordmark 	ill_t		*rill = ira->ira_rill;
3524bd670b35SErik Nordmark 
3525bd670b35SErik Nordmark 	secure = iraflags & IRAF_IPSEC_SECURE;
35267c478bd9Sstevel@tonic-gate 
35277c478bd9Sstevel@tonic-gate 	/*
35287c478bd9Sstevel@tonic-gate 	 * We check some fields in conn_t without holding a lock.
35297c478bd9Sstevel@tonic-gate 	 * This should be fine.
35307c478bd9Sstevel@tonic-gate 	 */
3531bd670b35SErik Nordmark 	if (((iraflags & IRAF_IS_IPV4) ?
3532bd670b35SErik Nordmark 	    CONN_INBOUND_POLICY_PRESENT(connp, ipss) :
3533bd670b35SErik Nordmark 	    CONN_INBOUND_POLICY_PRESENT_V6(connp, ipss)) ||
3534bd670b35SErik Nordmark 	    secure) {
3535bd670b35SErik Nordmark 		mp = ipsec_check_inbound_policy(mp, connp, ipha,
3536bd670b35SErik Nordmark 		    ip6h, ira);
35377c478bd9Sstevel@tonic-gate 		if (mp == NULL) {
3538bd670b35SErik Nordmark 			BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
3539bd670b35SErik Nordmark 			/* Note that mp is NULL */
3540bd670b35SErik Nordmark 			ip_drop_input("ipIfStatsInDiscards", mp, ill);
35417c478bd9Sstevel@tonic-gate 			SCTP_REFRELE(sctp);
35427c478bd9Sstevel@tonic-gate 			return;
35437c478bd9Sstevel@tonic-gate 		}
35447c478bd9Sstevel@tonic-gate 	}
35457c478bd9Sstevel@tonic-gate 
3546bd670b35SErik Nordmark 	ira->ira_ill = ira->ira_rill = NULL;
35477c478bd9Sstevel@tonic-gate 
35487c478bd9Sstevel@tonic-gate 	mutex_enter(&sctp->sctp_lock);
35497c478bd9Sstevel@tonic-gate 	if (sctp->sctp_running) {
3550bd670b35SErik Nordmark 		sctp_add_recvq(sctp, mp, B_FALSE, ira);
35517c478bd9Sstevel@tonic-gate 		mutex_exit(&sctp->sctp_lock);
3552bd670b35SErik Nordmark 		goto done;
35537c478bd9Sstevel@tonic-gate 	} else {
35547c478bd9Sstevel@tonic-gate 		sctp->sctp_running = B_TRUE;
35557c478bd9Sstevel@tonic-gate 		mutex_exit(&sctp->sctp_lock);
35567c478bd9Sstevel@tonic-gate 
35577c478bd9Sstevel@tonic-gate 		mutex_enter(&sctp->sctp_recvq_lock);
35587c478bd9Sstevel@tonic-gate 		if (sctp->sctp_recvq != NULL) {
3559bd670b35SErik Nordmark 			sctp_add_recvq(sctp, mp, B_TRUE, ira);
35607c478bd9Sstevel@tonic-gate 			mutex_exit(&sctp->sctp_recvq_lock);
35617c478bd9Sstevel@tonic-gate 			WAKE_SCTP(sctp);
3562bd670b35SErik Nordmark 			goto done;
35637c478bd9Sstevel@tonic-gate 		}
35647c478bd9Sstevel@tonic-gate 	}
35657c478bd9Sstevel@tonic-gate 	mutex_exit(&sctp->sctp_recvq_lock);
3566bd670b35SErik Nordmark 	if (ira->ira_flags & IRAF_ICMP_ERROR)
3567bd670b35SErik Nordmark 		sctp_icmp_error(sctp, mp);
3568bd670b35SErik Nordmark 	else
3569bd670b35SErik Nordmark 		sctp_input_data(sctp, mp, ira);
35707c478bd9Sstevel@tonic-gate 	WAKE_SCTP(sctp);
3571bd670b35SErik Nordmark 
3572bd670b35SErik Nordmark done:
35737c478bd9Sstevel@tonic-gate 	SCTP_REFRELE(sctp);
3574bd670b35SErik Nordmark 	ira->ira_ill = ill;
3575bd670b35SErik Nordmark 	ira->ira_rill = rill;
35767c478bd9Sstevel@tonic-gate }
35777c478bd9Sstevel@tonic-gate 
35787c478bd9Sstevel@tonic-gate static void
35797c478bd9Sstevel@tonic-gate sctp_process_abort(sctp_t *sctp, sctp_chunk_hdr_t *ch, int err)
35807c478bd9Sstevel@tonic-gate {
3581f4b3ec61Sdh155122 	sctp_stack_t	*sctps = sctp->sctp_sctps;
3582f4b3ec61Sdh155122 
35835dd46ab5SKacheong Poon 	SCTPS_BUMP_MIB(sctps, sctpAborted);
35847c478bd9Sstevel@tonic-gate 	BUMP_LOCAL(sctp->sctp_ibchunks);
35857c478bd9Sstevel@tonic-gate 
35866be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 	/*
35876be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 	 * SCTP_COMM_LOST is only sent up if the association is
35886be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 	 * established (sctp_state >= SCTPS_ESTABLISHED).
35896be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 	 */
35906be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 	if (sctp->sctp_state >= SCTPS_ESTABLISHED) {
35917c478bd9Sstevel@tonic-gate 		sctp_assoc_event(sctp, SCTP_COMM_LOST,
35927c478bd9Sstevel@tonic-gate 		    ntohs(((sctp_parm_hdr_t *)(ch + 1))->sph_type), ch);
35936be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 	}
35946be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 
35957c478bd9Sstevel@tonic-gate 	sctp_clean_death(sctp, err);
35967c478bd9Sstevel@tonic-gate }
35977c478bd9Sstevel@tonic-gate 
35987c478bd9Sstevel@tonic-gate void
3599bd670b35SErik Nordmark sctp_input_data(sctp_t *sctp, mblk_t *mp, ip_recv_attr_t *ira)
36007c478bd9Sstevel@tonic-gate {
36017c478bd9Sstevel@tonic-gate 	sctp_chunk_hdr_t	*ch;
36027c478bd9Sstevel@tonic-gate 	ssize_t			mlen;
36037c478bd9Sstevel@tonic-gate 	int			gotdata;
36047c478bd9Sstevel@tonic-gate 	int			trysend;
36057c478bd9Sstevel@tonic-gate 	sctp_faddr_t		*fp;
36067c478bd9Sstevel@tonic-gate 	sctp_init_chunk_t	*iack;
36077c478bd9Sstevel@tonic-gate 	uint32_t		tsn;
36087c478bd9Sstevel@tonic-gate 	sctp_data_hdr_t		*sdc;
3609bd670b35SErik Nordmark 	ip_pkt_t		ipp;
36107c478bd9Sstevel@tonic-gate 	in6_addr_t		src;
36117c478bd9Sstevel@tonic-gate 	in6_addr_t		dst;
36127c478bd9Sstevel@tonic-gate 	uint_t			ifindex;
36137c478bd9Sstevel@tonic-gate 	sctp_hdr_t		*sctph;
3614bd670b35SErik Nordmark 	uint_t			ip_hdr_len = ira->ira_ip_hdr_length;
36157c478bd9Sstevel@tonic-gate 	mblk_t			*dups = NULL;
3616558fbd03Skcpoon 	int			recv_adaptation;
36177c478bd9Sstevel@tonic-gate 	boolean_t		wake_eager = B_FALSE;
36187c478bd9Sstevel@tonic-gate 	in6_addr_t		peer_src;
36197c478bd9Sstevel@tonic-gate 	int64_t			now;
3620f4b3ec61Sdh155122 	sctp_stack_t		*sctps = sctp->sctp_sctps;
3621f4b3ec61Sdh155122 	ip_stack_t		*ipst = sctps->sctps_netstack->netstack_ip;
36227f093707Skcpoon 	boolean_t		hb_already = B_FALSE;
3623de8c4a14SErik Nordmark 	cred_t			*cr;
3624de8c4a14SErik Nordmark 	pid_t			cpid;
3625bd670b35SErik Nordmark 	uchar_t			*rptr;
3626bd670b35SErik Nordmark 	conn_t			*connp = sctp->sctp_connp;
36273e1dae9fSAnil udupa 	boolean_t		shutdown_ack_needed = B_FALSE;
36287c478bd9Sstevel@tonic-gate 
36297c478bd9Sstevel@tonic-gate 	ASSERT(DB_TYPE(mp) == M_DATA);
3630bd670b35SErik Nordmark 	ASSERT(ira->ira_ill == NULL);
36317c478bd9Sstevel@tonic-gate 
36327c478bd9Sstevel@tonic-gate 	if (mp->b_cont != NULL) {
36337c478bd9Sstevel@tonic-gate 		/*
36347c478bd9Sstevel@tonic-gate 		 * All subsequent code is vastly simplified if it can
36357c478bd9Sstevel@tonic-gate 		 * assume a single contiguous chunk of data.
36367c478bd9Sstevel@tonic-gate 		 */
36377c478bd9Sstevel@tonic-gate 		if (pullupmsg(mp, -1) == 0) {
3638f4b3ec61Sdh155122 			BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsInDiscards);
3639bd670b35SErik Nordmark 			ip_drop_input("ipIfStatsInDiscards", mp, NULL);
36407c478bd9Sstevel@tonic-gate 			freemsg(mp);
36417c478bd9Sstevel@tonic-gate 			return;
36427c478bd9Sstevel@tonic-gate 		}
36437c478bd9Sstevel@tonic-gate 	}
36447c478bd9Sstevel@tonic-gate 
36457c478bd9Sstevel@tonic-gate 	BUMP_LOCAL(sctp->sctp_ipkts);
3646bd670b35SErik Nordmark 	ifindex = ira->ira_ruifindex;
3647bd670b35SErik Nordmark 
3648bd670b35SErik Nordmark 	rptr = mp->b_rptr;
3649bd670b35SErik Nordmark 
3650bd670b35SErik Nordmark 	ipp.ipp_fields = 0;
3651bd670b35SErik Nordmark 	if (connp->conn_recv_ancillary.crb_all != 0) {
3652bd670b35SErik Nordmark 		/*
3653bd670b35SErik Nordmark 		 * Record packet information in the ip_pkt_t
3654bd670b35SErik Nordmark 		 */
3655bd670b35SErik Nordmark 		if (ira->ira_flags & IRAF_IS_IPV4) {
3656bd670b35SErik Nordmark 			(void) ip_find_hdr_v4((ipha_t *)rptr, &ipp,
3657bd670b35SErik Nordmark 			    B_FALSE);
3658bd670b35SErik Nordmark 		} else {
3659bd670b35SErik Nordmark 			uint8_t nexthdrp;
3660bd670b35SErik Nordmark 
3661bd670b35SErik Nordmark 			/*
3662bd670b35SErik Nordmark 			 * IPv6 packets can only be received by applications
3663bd670b35SErik Nordmark 			 * that are prepared to receive IPv6 addresses.
3664bd670b35SErik Nordmark 			 * The IP fanout must ensure this.
3665bd670b35SErik Nordmark 			 */
3666bd670b35SErik Nordmark 			ASSERT(connp->conn_family == AF_INET6);
3667bd670b35SErik Nordmark 
3668bd670b35SErik Nordmark 			(void) ip_find_hdr_v6(mp, (ip6_t *)rptr, B_TRUE, &ipp,
3669bd670b35SErik Nordmark 			    &nexthdrp);
3670bd670b35SErik Nordmark 			ASSERT(nexthdrp == IPPROTO_SCTP);
3671bd670b35SErik Nordmark 
3672bd670b35SErik Nordmark 			/* Could have caused a pullup? */
3673bd670b35SErik Nordmark 			rptr = mp->b_rptr;
3674bd670b35SErik Nordmark 		}
3675bd670b35SErik Nordmark 	}
3676bd670b35SErik Nordmark 
3677bd670b35SErik Nordmark 	sctph = ((sctp_hdr_t *)&rptr[ip_hdr_len]);
3678bd670b35SErik Nordmark 
3679bd670b35SErik Nordmark 	if (ira->ira_flags & IRAF_IS_IPV4) {
3680bd670b35SErik Nordmark 		ipha_t *ipha;
3681bd670b35SErik Nordmark 
3682bd670b35SErik Nordmark 		ipha = (ipha_t *)rptr;
3683bd670b35SErik Nordmark 		IN6_IPADDR_TO_V4MAPPED(ipha->ipha_src, &src);
3684bd670b35SErik Nordmark 		IN6_IPADDR_TO_V4MAPPED(ipha->ipha_dst, &dst);
3685bd670b35SErik Nordmark 	} else {
3686bd670b35SErik Nordmark 		ip6_t *ip6h;
3687bd670b35SErik Nordmark 
3688bd670b35SErik Nordmark 		ip6h = (ip6_t *)rptr;
3689bd670b35SErik Nordmark 		src = ip6h->ip6_src;
3690bd670b35SErik Nordmark 		dst = ip6h->ip6_dst;
3691bd670b35SErik Nordmark 	}
3692bd670b35SErik Nordmark 
36937c478bd9Sstevel@tonic-gate 	mlen = mp->b_wptr - (uchar_t *)(sctph + 1);
36947c478bd9Sstevel@tonic-gate 	ch = sctp_first_chunk((uchar_t *)(sctph + 1), mlen);
36957c478bd9Sstevel@tonic-gate 	if (ch == NULL) {
3696f4b3ec61Sdh155122 		BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsInDiscards);
3697bd670b35SErik Nordmark 		ip_drop_input("ipIfStatsInDiscards", mp, NULL);
36987c478bd9Sstevel@tonic-gate 		freemsg(mp);
36997c478bd9Sstevel@tonic-gate 		return;
37007c478bd9Sstevel@tonic-gate 	}
37017c478bd9Sstevel@tonic-gate 
37027c478bd9Sstevel@tonic-gate 	if (!sctp_check_input(sctp, ch, mlen, 1)) {
3703f4b3ec61Sdh155122 		BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsInDiscards);
3704bd670b35SErik Nordmark 		ip_drop_input("ipIfStatsInDiscards", mp, NULL);
37057c478bd9Sstevel@tonic-gate 		goto done;
37067c478bd9Sstevel@tonic-gate 	}
37077c478bd9Sstevel@tonic-gate 	/*
37087c478bd9Sstevel@tonic-gate 	 * Check verfication tag (special handling for INIT,
37097c478bd9Sstevel@tonic-gate 	 * COOKIE, SHUTDOWN_COMPLETE and SHUTDOWN_ACK chunks).
37107c478bd9Sstevel@tonic-gate 	 * ABORTs are handled in the chunk processing loop, since
37117c478bd9Sstevel@tonic-gate 	 * may not appear first. All other checked chunks must
37127c478bd9Sstevel@tonic-gate 	 * appear first, or will have been dropped by check_input().
37137c478bd9Sstevel@tonic-gate 	 */
37147c478bd9Sstevel@tonic-gate 	switch (ch->sch_id) {
37157c478bd9Sstevel@tonic-gate 	case CHUNK_INIT:
37167c478bd9Sstevel@tonic-gate 		if (sctph->sh_verf != 0) {
37177c478bd9Sstevel@tonic-gate 			/* drop it */
37187c478bd9Sstevel@tonic-gate 			goto done;
37197c478bd9Sstevel@tonic-gate 		}
37207c478bd9Sstevel@tonic-gate 		break;
37217c478bd9Sstevel@tonic-gate 	case CHUNK_SHUTDOWN_COMPLETE:
37227c478bd9Sstevel@tonic-gate 		if (sctph->sh_verf == sctp->sctp_lvtag)
37237c478bd9Sstevel@tonic-gate 			break;
37247c478bd9Sstevel@tonic-gate 		if (sctph->sh_verf == sctp->sctp_fvtag &&
37257c478bd9Sstevel@tonic-gate 		    SCTP_GET_TBIT(ch)) {
37267c478bd9Sstevel@tonic-gate 			break;
37277c478bd9Sstevel@tonic-gate 		}
37287c478bd9Sstevel@tonic-gate 		/* else drop it */
37297c478bd9Sstevel@tonic-gate 		goto done;
37307c478bd9Sstevel@tonic-gate 	case CHUNK_ABORT:
37317c478bd9Sstevel@tonic-gate 	case CHUNK_COOKIE:
37327c478bd9Sstevel@tonic-gate 		/* handled below */
37337c478bd9Sstevel@tonic-gate 		break;
37347c478bd9Sstevel@tonic-gate 	case CHUNK_SHUTDOWN_ACK:
37357c478bd9Sstevel@tonic-gate 		if (sctp->sctp_state > SCTPS_BOUND &&
37367c478bd9Sstevel@tonic-gate 		    sctp->sctp_state < SCTPS_ESTABLISHED) {
37377c478bd9Sstevel@tonic-gate 			/* treat as OOTB */
3738bd670b35SErik Nordmark 			sctp_ootb_shutdown_ack(mp, ip_hdr_len, ira, ipst);
37397c478bd9Sstevel@tonic-gate 			return;
37407c478bd9Sstevel@tonic-gate 		}
37417c478bd9Sstevel@tonic-gate 		/* else fallthru */
37427c478bd9Sstevel@tonic-gate 	default:
37437c478bd9Sstevel@tonic-gate 		/*
37447c478bd9Sstevel@tonic-gate 		 * All other packets must have a valid
37457c478bd9Sstevel@tonic-gate 		 * verification tag, however if this is a
37467c478bd9Sstevel@tonic-gate 		 * listener, we use a refined version of
37477c478bd9Sstevel@tonic-gate 		 * out-of-the-blue logic.
37487c478bd9Sstevel@tonic-gate 		 */
37497c478bd9Sstevel@tonic-gate 		if (sctph->sh_verf != sctp->sctp_lvtag &&
37507c478bd9Sstevel@tonic-gate 		    sctp->sctp_state != SCTPS_LISTEN) {
37517c478bd9Sstevel@tonic-gate 			/* drop it */
37527c478bd9Sstevel@tonic-gate 			goto done;
37537c478bd9Sstevel@tonic-gate 		}
37547c478bd9Sstevel@tonic-gate 		break;
37557c478bd9Sstevel@tonic-gate 	}
37567c478bd9Sstevel@tonic-gate 
37577c478bd9Sstevel@tonic-gate 	/* Have a valid sctp for this packet */
37587c478bd9Sstevel@tonic-gate 	fp = sctp_lookup_faddr(sctp, &src);
375945916cd2Sjpk 	dprint(2, ("sctp_dispatch_rput: mp=%p fp=%p sctp=%p\n", (void *)mp,
376045916cd2Sjpk 	    (void *)fp, (void *)sctp));
37617c478bd9Sstevel@tonic-gate 
37627c478bd9Sstevel@tonic-gate 	gotdata = 0;
37637c478bd9Sstevel@tonic-gate 	trysend = 0;
37647c478bd9Sstevel@tonic-gate 
37655dd46ab5SKacheong Poon 	now = LBOLT_FASTPATH64;
37667c478bd9Sstevel@tonic-gate 	/* Process the chunks */
37677c478bd9Sstevel@tonic-gate 	do {
37687c478bd9Sstevel@tonic-gate 		dprint(3, ("sctp_dispatch_rput: state=%d, chunk id=%d\n",
37697c478bd9Sstevel@tonic-gate 		    sctp->sctp_state, (int)(ch->sch_id)));
37707c478bd9Sstevel@tonic-gate 
37717c478bd9Sstevel@tonic-gate 		if (ch->sch_id == CHUNK_ABORT) {
37727c478bd9Sstevel@tonic-gate 			if (sctph->sh_verf != sctp->sctp_lvtag &&
37737c478bd9Sstevel@tonic-gate 			    sctph->sh_verf != sctp->sctp_fvtag) {
37747c478bd9Sstevel@tonic-gate 				/* drop it */
37757c478bd9Sstevel@tonic-gate 				goto done;
37767c478bd9Sstevel@tonic-gate 			}
37777c478bd9Sstevel@tonic-gate 		}
37787c478bd9Sstevel@tonic-gate 
37797c478bd9Sstevel@tonic-gate 		switch (sctp->sctp_state) {
37807c478bd9Sstevel@tonic-gate 
37817c478bd9Sstevel@tonic-gate 		case SCTPS_ESTABLISHED:
37827c478bd9Sstevel@tonic-gate 		case SCTPS_SHUTDOWN_PENDING:
37837c478bd9Sstevel@tonic-gate 		case SCTPS_SHUTDOWN_SENT:
37847c478bd9Sstevel@tonic-gate 			switch (ch->sch_id) {
37857c478bd9Sstevel@tonic-gate 			case CHUNK_DATA:
37867c478bd9Sstevel@tonic-gate 				/* 0-length data chunks are not allowed */
37877c478bd9Sstevel@tonic-gate 				if (ntohs(ch->sch_len) == sizeof (*sdc)) {
37887c478bd9Sstevel@tonic-gate 					sdc = (sctp_data_hdr_t *)ch;
37897c478bd9Sstevel@tonic-gate 					tsn = sdc->sdh_tsn;
37907c478bd9Sstevel@tonic-gate 					sctp_send_abort(sctp, sctp->sctp_fvtag,
37917c478bd9Sstevel@tonic-gate 					    SCTP_ERR_NO_USR_DATA, (char *)&tsn,
3792bd670b35SErik Nordmark 					    sizeof (tsn), mp, 0, B_FALSE, ira);
37937c478bd9Sstevel@tonic-gate 					sctp_assoc_event(sctp, SCTP_COMM_LOST,
37947c478bd9Sstevel@tonic-gate 					    0, NULL);
37957c478bd9Sstevel@tonic-gate 					sctp_clean_death(sctp, ECONNABORTED);
37967c478bd9Sstevel@tonic-gate 					goto done;
37977c478bd9Sstevel@tonic-gate 				}
37987c478bd9Sstevel@tonic-gate 
37997c478bd9Sstevel@tonic-gate 				ASSERT(fp != NULL);
38007c478bd9Sstevel@tonic-gate 				sctp->sctp_lastdata = fp;
3801bd670b35SErik Nordmark 				sctp_data_chunk(sctp, ch, mp, &dups, fp,
3802bd670b35SErik Nordmark 				    &ipp, ira);
38037c478bd9Sstevel@tonic-gate 				gotdata = 1;
38047c478bd9Sstevel@tonic-gate 				/* Restart shutdown timer if shutting down */
38057c478bd9Sstevel@tonic-gate 				if (sctp->sctp_state == SCTPS_SHUTDOWN_SENT) {
38067c478bd9Sstevel@tonic-gate 					/*
38077c478bd9Sstevel@tonic-gate 					 * If we have exceeded our max
38087c478bd9Sstevel@tonic-gate 					 * wait bound for waiting for a
38097c478bd9Sstevel@tonic-gate 					 * shutdown ack from the peer,
38107c478bd9Sstevel@tonic-gate 					 * abort the association.
38117c478bd9Sstevel@tonic-gate 					 */
3812f4b3ec61Sdh155122 					if (sctps->sctps_shutack_wait_bound !=
3813f4b3ec61Sdh155122 					    0 &&
38147c478bd9Sstevel@tonic-gate 					    TICK_TO_MSEC(now -
38157c478bd9Sstevel@tonic-gate 					    sctp->sctp_out_time) >
3816f4b3ec61Sdh155122 					    sctps->sctps_shutack_wait_bound) {
38177c478bd9Sstevel@tonic-gate 						sctp_send_abort(sctp,
38187c478bd9Sstevel@tonic-gate 						    sctp->sctp_fvtag, 0, NULL,
3819bd670b35SErik Nordmark 						    0, mp, 0, B_FALSE, ira);
38207c478bd9Sstevel@tonic-gate 						sctp_assoc_event(sctp,
38217c478bd9Sstevel@tonic-gate 						    SCTP_COMM_LOST, 0, NULL);
38227c478bd9Sstevel@tonic-gate 						sctp_clean_death(sctp,
38237c478bd9Sstevel@tonic-gate 						    ECONNABORTED);
38247c478bd9Sstevel@tonic-gate 						goto done;
38257c478bd9Sstevel@tonic-gate 					}
38267c478bd9Sstevel@tonic-gate 					SCTP_FADDR_TIMER_RESTART(sctp, fp,
38276be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 					    fp->sf_rto);
38287c478bd9Sstevel@tonic-gate 				}
38297c478bd9Sstevel@tonic-gate 				break;
38307c478bd9Sstevel@tonic-gate 			case CHUNK_SACK:
38317c478bd9Sstevel@tonic-gate 				ASSERT(fp != NULL);
38327c478bd9Sstevel@tonic-gate 				/*
38337c478bd9Sstevel@tonic-gate 				 * Peer is real and alive if it can ack our
38347c478bd9Sstevel@tonic-gate 				 * data.
38357c478bd9Sstevel@tonic-gate 				 */
38367c478bd9Sstevel@tonic-gate 				sctp_faddr_alive(sctp, fp);
38377c478bd9Sstevel@tonic-gate 				trysend = sctp_got_sack(sctp, ch);
38381d8c4025Svi117747 				if (trysend < 0) {
38391d8c4025Svi117747 					sctp_send_abort(sctp, sctph->sh_verf,
3840bd670b35SErik Nordmark 					    0, NULL, 0, mp, 0, B_FALSE, ira);
38411d8c4025Svi117747 					sctp_assoc_event(sctp,
38421d8c4025Svi117747 					    SCTP_COMM_LOST, 0, NULL);
38431d8c4025Svi117747 					sctp_clean_death(sctp,
38441d8c4025Svi117747 					    ECONNABORTED);
38451d8c4025Svi117747 					goto done;
38461d8c4025Svi117747 				}
38477c478bd9Sstevel@tonic-gate 				break;
38487c478bd9Sstevel@tonic-gate 			case CHUNK_HEARTBEAT:
38497f093707Skcpoon 				if (!hb_already) {
38507f093707Skcpoon 					/*
38517f093707Skcpoon 					 * In any one packet, there should
38527f093707Skcpoon 					 * only be one heartbeat chunk.  So
38537f093707Skcpoon 					 * we should not process more than
38547f093707Skcpoon 					 * once.
38557f093707Skcpoon 					 */
38567c478bd9Sstevel@tonic-gate 					sctp_return_heartbeat(sctp, ch, mp);
38577f093707Skcpoon 					hb_already = B_TRUE;
38587f093707Skcpoon 				}
38597c478bd9Sstevel@tonic-gate 				break;
38607c478bd9Sstevel@tonic-gate 			case CHUNK_HEARTBEAT_ACK:
38617c478bd9Sstevel@tonic-gate 				sctp_process_heartbeat(sctp, ch);
38627c478bd9Sstevel@tonic-gate 				break;
38637c478bd9Sstevel@tonic-gate 			case CHUNK_SHUTDOWN:
38647c478bd9Sstevel@tonic-gate 				sctp_shutdown_event(sctp);
38657c478bd9Sstevel@tonic-gate 				trysend = sctp_shutdown_received(sctp, ch,
386677c67f2fSkcpoon 				    B_FALSE, B_FALSE, fp);
38677c478bd9Sstevel@tonic-gate 				BUMP_LOCAL(sctp->sctp_ibchunks);
38687c478bd9Sstevel@tonic-gate 				break;
38697c478bd9Sstevel@tonic-gate 			case CHUNK_SHUTDOWN_ACK:
38707c478bd9Sstevel@tonic-gate 				BUMP_LOCAL(sctp->sctp_ibchunks);
38717c478bd9Sstevel@tonic-gate 				if (sctp->sctp_state == SCTPS_SHUTDOWN_SENT) {
38727c478bd9Sstevel@tonic-gate 					sctp_shutdown_complete(sctp);
38735dd46ab5SKacheong Poon 					SCTPS_BUMP_MIB(sctps, sctpShutdowns);
38747c478bd9Sstevel@tonic-gate 					sctp_assoc_event(sctp,
38757c478bd9Sstevel@tonic-gate 					    SCTP_SHUTDOWN_COMP, 0, NULL);
38767c478bd9Sstevel@tonic-gate 					sctp_clean_death(sctp, 0);
38777c478bd9Sstevel@tonic-gate 					goto done;
38787c478bd9Sstevel@tonic-gate 				}
38797c478bd9Sstevel@tonic-gate 				break;
38807c478bd9Sstevel@tonic-gate 			case CHUNK_ABORT: {
38817c478bd9Sstevel@tonic-gate 				sctp_saddr_ipif_t *sp;
38827c478bd9Sstevel@tonic-gate 
38837c478bd9Sstevel@tonic-gate 				/* Ignore if delete pending */
38841d8c4025Svi117747 				sp = sctp_saddr_lookup(sctp, &dst, 0);
38857c478bd9Sstevel@tonic-gate 				ASSERT(sp != NULL);
38867c478bd9Sstevel@tonic-gate 				if (sp->saddr_ipif_delete_pending) {
38877c478bd9Sstevel@tonic-gate 					BUMP_LOCAL(sctp->sctp_ibchunks);
38887c478bd9Sstevel@tonic-gate 					break;
38897c478bd9Sstevel@tonic-gate 				}
38907c478bd9Sstevel@tonic-gate 
38917c478bd9Sstevel@tonic-gate 				sctp_process_abort(sctp, ch, ECONNRESET);
38927c478bd9Sstevel@tonic-gate 				goto done;
38937c478bd9Sstevel@tonic-gate 			}
38947c478bd9Sstevel@tonic-gate 			case CHUNK_INIT:
3895bd670b35SErik Nordmark 				sctp_send_initack(sctp, sctph, ch, mp, ira);
38967c478bd9Sstevel@tonic-gate 				break;
38977c478bd9Sstevel@tonic-gate 			case CHUNK_COOKIE:
38987c478bd9Sstevel@tonic-gate 				if (sctp_process_cookie(sctp, ch, mp, &iack,
3899bd670b35SErik Nordmark 				    sctph, &recv_adaptation, NULL, ira) != -1) {
39007c478bd9Sstevel@tonic-gate 					sctp_send_cookie_ack(sctp);
39017c478bd9Sstevel@tonic-gate 					sctp_assoc_event(sctp, SCTP_RESTART,
39027c478bd9Sstevel@tonic-gate 					    0, NULL);
3903558fbd03Skcpoon 					if (recv_adaptation) {
3904558fbd03Skcpoon 						sctp->sctp_recv_adaptation = 1;
3905558fbd03Skcpoon 						sctp_adaptation_event(sctp);
39067c478bd9Sstevel@tonic-gate 					}
39077c478bd9Sstevel@tonic-gate 				} else {
39085dd46ab5SKacheong Poon 					SCTPS_BUMP_MIB(sctps,
39097c478bd9Sstevel@tonic-gate 					    sctpInInvalidCookie);
39107c478bd9Sstevel@tonic-gate 				}
39117c478bd9Sstevel@tonic-gate 				break;
39127c478bd9Sstevel@tonic-gate 			case CHUNK_ERROR: {
39137c478bd9Sstevel@tonic-gate 				int error;
39147c478bd9Sstevel@tonic-gate 
39157c478bd9Sstevel@tonic-gate 				BUMP_LOCAL(sctp->sctp_ibchunks);
3916bd670b35SErik Nordmark 				error = sctp_handle_error(sctp, sctph, ch, mp,
3917bd670b35SErik Nordmark 				    ira);
39187c478bd9Sstevel@tonic-gate 				if (error != 0) {
3919c9da23f8Skcpoon 					sctp_assoc_event(sctp, SCTP_COMM_LOST,
3920c9da23f8Skcpoon 					    0, NULL);
39217c478bd9Sstevel@tonic-gate 					sctp_clean_death(sctp, error);
39227c478bd9Sstevel@tonic-gate 					goto done;
39237c478bd9Sstevel@tonic-gate 				}
39247c478bd9Sstevel@tonic-gate 				break;
39257c478bd9Sstevel@tonic-gate 			}
39267c478bd9Sstevel@tonic-gate 			case CHUNK_ASCONF:
39277c478bd9Sstevel@tonic-gate 				ASSERT(fp != NULL);
39287c478bd9Sstevel@tonic-gate 				sctp_input_asconf(sctp, ch, fp);
39297c478bd9Sstevel@tonic-gate 				BUMP_LOCAL(sctp->sctp_ibchunks);
39307c478bd9Sstevel@tonic-gate 				break;
39317c478bd9Sstevel@tonic-gate 			case CHUNK_ASCONF_ACK:
39327c478bd9Sstevel@tonic-gate 				ASSERT(fp != NULL);
39337c478bd9Sstevel@tonic-gate 				sctp_faddr_alive(sctp, fp);
39347c478bd9Sstevel@tonic-gate 				sctp_input_asconf_ack(sctp, ch, fp);
39357c478bd9Sstevel@tonic-gate 				BUMP_LOCAL(sctp->sctp_ibchunks);
39367c478bd9Sstevel@tonic-gate 				break;
39377c478bd9Sstevel@tonic-gate 			case CHUNK_FORWARD_TSN:
39387c478bd9Sstevel@tonic-gate 				ASSERT(fp != NULL);
39397c478bd9Sstevel@tonic-gate 				sctp->sctp_lastdata = fp;
3940bd670b35SErik Nordmark 				sctp_process_forward_tsn(sctp, ch, fp,
3941bd670b35SErik Nordmark 				    &ipp, ira);
39427c478bd9Sstevel@tonic-gate 				gotdata = 1;
39437c478bd9Sstevel@tonic-gate 				BUMP_LOCAL(sctp->sctp_ibchunks);
39447c478bd9Sstevel@tonic-gate 				break;
39457c478bd9Sstevel@tonic-gate 			default:
39467c478bd9Sstevel@tonic-gate 				if (sctp_strange_chunk(sctp, ch, fp) == 0) {
39477c478bd9Sstevel@tonic-gate 					goto nomorechunks;
39487c478bd9Sstevel@tonic-gate 				} /* else skip and continue processing */
39497c478bd9Sstevel@tonic-gate 				break;
39507c478bd9Sstevel@tonic-gate 			}
39517c478bd9Sstevel@tonic-gate 			break;
39527c478bd9Sstevel@tonic-gate 
39537c478bd9Sstevel@tonic-gate 		case SCTPS_LISTEN:
39547c478bd9Sstevel@tonic-gate 			switch (ch->sch_id) {
39557c478bd9Sstevel@tonic-gate 			case CHUNK_INIT:
3956bd670b35SErik Nordmark 				sctp_send_initack(sctp, sctph, ch, mp, ira);
39577c478bd9Sstevel@tonic-gate 				break;
39587c478bd9Sstevel@tonic-gate 			case CHUNK_COOKIE: {
39597c478bd9Sstevel@tonic-gate 				sctp_t *eager;
39607c478bd9Sstevel@tonic-gate 
39617c478bd9Sstevel@tonic-gate 				if (sctp_process_cookie(sctp, ch, mp, &iack,
3962bd670b35SErik Nordmark 				    sctph, &recv_adaptation, &peer_src,
3963bd670b35SErik Nordmark 				    ira) == -1) {
39645dd46ab5SKacheong Poon 					SCTPS_BUMP_MIB(sctps,
39657c478bd9Sstevel@tonic-gate 					    sctpInInvalidCookie);
39667c478bd9Sstevel@tonic-gate 					goto done;
39677c478bd9Sstevel@tonic-gate 				}
39687c478bd9Sstevel@tonic-gate 
39697c478bd9Sstevel@tonic-gate 				/*
39707c478bd9Sstevel@tonic-gate 				 * The cookie is good; ensure that
39717c478bd9Sstevel@tonic-gate 				 * the peer used the verification
39727c478bd9Sstevel@tonic-gate 				 * tag from the init ack in the header.
39737c478bd9Sstevel@tonic-gate 				 */
39747c478bd9Sstevel@tonic-gate 				if (iack->sic_inittag != sctph->sh_verf)
39757c478bd9Sstevel@tonic-gate 					goto done;
39767c478bd9Sstevel@tonic-gate 
39777c478bd9Sstevel@tonic-gate 				eager = sctp_conn_request(sctp, mp, ifindex,
3978bd670b35SErik Nordmark 				    ip_hdr_len, iack, ira);
39797c478bd9Sstevel@tonic-gate 				if (eager == NULL) {
39807c478bd9Sstevel@tonic-gate 					sctp_send_abort(sctp, sctph->sh_verf,
39817c478bd9Sstevel@tonic-gate 					    SCTP_ERR_NO_RESOURCES, NULL, 0, mp,
3982bd670b35SErik Nordmark 					    0, B_FALSE, ira);
39837c478bd9Sstevel@tonic-gate 					goto done;
39847c478bd9Sstevel@tonic-gate 				}
39857c478bd9Sstevel@tonic-gate 
39867c478bd9Sstevel@tonic-gate 				/*
39877c478bd9Sstevel@tonic-gate 				 * If there were extra chunks
39887c478bd9Sstevel@tonic-gate 				 * bundled with the cookie,
39897c478bd9Sstevel@tonic-gate 				 * they must be processed
39907c478bd9Sstevel@tonic-gate 				 * on the eager's queue. We
39917c478bd9Sstevel@tonic-gate 				 * accomplish this by refeeding
39927c478bd9Sstevel@tonic-gate 				 * the whole packet into the
39937c478bd9Sstevel@tonic-gate 				 * state machine on the right
39947c478bd9Sstevel@tonic-gate 				 * q. The packet (mp) gets
39957c478bd9Sstevel@tonic-gate 				 * there via the eager's
39967c478bd9Sstevel@tonic-gate 				 * cookie_mp field (overloaded
39977c478bd9Sstevel@tonic-gate 				 * with the active open role).
39987c478bd9Sstevel@tonic-gate 				 * This is picked up when
39997c478bd9Sstevel@tonic-gate 				 * processing the null bind
40007c478bd9Sstevel@tonic-gate 				 * request put on the eager's
40017c478bd9Sstevel@tonic-gate 				 * q by sctp_accept(). We must
40027c478bd9Sstevel@tonic-gate 				 * first revert the cookie
40037c478bd9Sstevel@tonic-gate 				 * chunk's length field to network
40047c478bd9Sstevel@tonic-gate 				 * byteorder so it can be
40057c478bd9Sstevel@tonic-gate 				 * properly reprocessed on the
40067c478bd9Sstevel@tonic-gate 				 * eager's queue.
40077c478bd9Sstevel@tonic-gate 				 */
40085dd46ab5SKacheong Poon 				SCTPS_BUMP_MIB(sctps, sctpPassiveEstab);
40097c478bd9Sstevel@tonic-gate 				if (mlen > ntohs(ch->sch_len)) {
40107c478bd9Sstevel@tonic-gate 					eager->sctp_cookie_mp = dupb(mp);
40117c478bd9Sstevel@tonic-gate 					/*
40127c478bd9Sstevel@tonic-gate 					 * If no mem, just let
40137c478bd9Sstevel@tonic-gate 					 * the peer retransmit.
40147c478bd9Sstevel@tonic-gate 					 */
40157c478bd9Sstevel@tonic-gate 				}
40167c478bd9Sstevel@tonic-gate 				sctp_assoc_event(eager, SCTP_COMM_UP, 0, NULL);
4017558fbd03Skcpoon 				if (recv_adaptation) {
4018558fbd03Skcpoon 					eager->sctp_recv_adaptation = 1;
4019558fbd03Skcpoon 					eager->sctp_rx_adaptation_code =
4020558fbd03Skcpoon 					    sctp->sctp_rx_adaptation_code;
4021558fbd03Skcpoon 					sctp_adaptation_event(eager);
40227c478bd9Sstevel@tonic-gate 				}
40237c478bd9Sstevel@tonic-gate 
40247c478bd9Sstevel@tonic-gate 				eager->sctp_active = now;
40257c478bd9Sstevel@tonic-gate 				sctp_send_cookie_ack(eager);
40267c478bd9Sstevel@tonic-gate 
40277c478bd9Sstevel@tonic-gate 				wake_eager = B_TRUE;
40287c478bd9Sstevel@tonic-gate 
40297c478bd9Sstevel@tonic-gate 				/*
40307c478bd9Sstevel@tonic-gate 				 * Process rest of the chunks with eager.
40317c478bd9Sstevel@tonic-gate 				 */
40327c478bd9Sstevel@tonic-gate 				sctp = eager;
40337c478bd9Sstevel@tonic-gate 				fp = sctp_lookup_faddr(sctp, &peer_src);
40347c478bd9Sstevel@tonic-gate 				/*
40357c478bd9Sstevel@tonic-gate 				 * Confirm peer's original source.  fp can
40367c478bd9Sstevel@tonic-gate 				 * only be NULL if peer does not use the
40377c478bd9Sstevel@tonic-gate 				 * original source as one of its addresses...
40387c478bd9Sstevel@tonic-gate 				 */
40397c478bd9Sstevel@tonic-gate 				if (fp == NULL)
40407c478bd9Sstevel@tonic-gate 					fp = sctp_lookup_faddr(sctp, &src);
40417c478bd9Sstevel@tonic-gate 				else
40427c478bd9Sstevel@tonic-gate 					sctp_faddr_alive(sctp, fp);
40437c478bd9Sstevel@tonic-gate 
40447c478bd9Sstevel@tonic-gate 				/*
40457c478bd9Sstevel@tonic-gate 				 * Validate the peer addresses.  It also starts
40467c478bd9Sstevel@tonic-gate 				 * the heartbeat timer.
40477c478bd9Sstevel@tonic-gate 				 */
40487c478bd9Sstevel@tonic-gate 				sctp_validate_peer(sctp);
40497c478bd9Sstevel@tonic-gate 				break;
40507c478bd9Sstevel@tonic-gate 			}
40517c478bd9Sstevel@tonic-gate 			/* Anything else is considered out-of-the-blue */
40527c478bd9Sstevel@tonic-gate 			case CHUNK_ERROR:
40537c478bd9Sstevel@tonic-gate 			case CHUNK_ABORT:
40547c478bd9Sstevel@tonic-gate 			case CHUNK_COOKIE_ACK:
40557c478bd9Sstevel@tonic-gate 			case CHUNK_SHUTDOWN_COMPLETE:
40567c478bd9Sstevel@tonic-gate 				BUMP_LOCAL(sctp->sctp_ibchunks);
40577c478bd9Sstevel@tonic-gate 				goto done;
40587c478bd9Sstevel@tonic-gate 			default:
40597c478bd9Sstevel@tonic-gate 				BUMP_LOCAL(sctp->sctp_ibchunks);
40607c478bd9Sstevel@tonic-gate 				sctp_send_abort(sctp, sctph->sh_verf, 0, NULL,
4061bd670b35SErik Nordmark 				    0, mp, 0, B_TRUE, ira);
40627c478bd9Sstevel@tonic-gate 				goto done;
40637c478bd9Sstevel@tonic-gate 			}
40647c478bd9Sstevel@tonic-gate 			break;
40657c478bd9Sstevel@tonic-gate 
40667c478bd9Sstevel@tonic-gate 		case SCTPS_COOKIE_WAIT:
40677c478bd9Sstevel@tonic-gate 			switch (ch->sch_id) {
40687c478bd9Sstevel@tonic-gate 			case CHUNK_INIT_ACK:
40697c478bd9Sstevel@tonic-gate 				sctp_stop_faddr_timers(sctp);
40707c478bd9Sstevel@tonic-gate 				sctp_faddr_alive(sctp, sctp->sctp_current);
4071bd670b35SErik Nordmark 				sctp_send_cookie_echo(sctp, ch, mp, ira);
40727c478bd9Sstevel@tonic-gate 				BUMP_LOCAL(sctp->sctp_ibchunks);
40737c478bd9Sstevel@tonic-gate 				break;
40747c478bd9Sstevel@tonic-gate 			case CHUNK_ABORT:
40757c478bd9Sstevel@tonic-gate 				sctp_process_abort(sctp, ch, ECONNREFUSED);
40767c478bd9Sstevel@tonic-gate 				goto done;
40777c478bd9Sstevel@tonic-gate 			case CHUNK_INIT:
4078bd670b35SErik Nordmark 				sctp_send_initack(sctp, sctph, ch, mp, ira);
40797c478bd9Sstevel@tonic-gate 				break;
40807c478bd9Sstevel@tonic-gate 			case CHUNK_COOKIE:
4081bd670b35SErik Nordmark 				cr = ira->ira_cred;
4082bd670b35SErik Nordmark 				cpid = ira->ira_cpid;
4083de8c4a14SErik Nordmark 
40847c478bd9Sstevel@tonic-gate 				if (sctp_process_cookie(sctp, ch, mp, &iack,
4085bd670b35SErik Nordmark 				    sctph, &recv_adaptation, NULL, ira) == -1) {
40865dd46ab5SKacheong Poon 					SCTPS_BUMP_MIB(sctps,
40877c478bd9Sstevel@tonic-gate 					    sctpInInvalidCookie);
40887c478bd9Sstevel@tonic-gate 					break;
40897c478bd9Sstevel@tonic-gate 				}
40907c478bd9Sstevel@tonic-gate 				sctp_send_cookie_ack(sctp);
40917c478bd9Sstevel@tonic-gate 				sctp_stop_faddr_timers(sctp);
40927c478bd9Sstevel@tonic-gate 				if (!SCTP_IS_DETACHED(sctp)) {
40937f093707Skcpoon 					sctp->sctp_ulp_connected(
4094de8c4a14SErik Nordmark 					    sctp->sctp_ulpd, 0, cr, cpid);
40957c478bd9Sstevel@tonic-gate 					sctp_set_ulp_prop(sctp);
4096de8c4a14SErik Nordmark 
40977c478bd9Sstevel@tonic-gate 				}
40985dd46ab5SKacheong Poon 				SCTP_ASSOC_EST(sctps, sctp);
40995dd46ab5SKacheong Poon 				SCTPS_BUMP_MIB(sctps, sctpActiveEstab);
41007c478bd9Sstevel@tonic-gate 				if (sctp->sctp_cookie_mp) {
41017c478bd9Sstevel@tonic-gate 					freemsg(sctp->sctp_cookie_mp);
41027c478bd9Sstevel@tonic-gate 					sctp->sctp_cookie_mp = NULL;
41037c478bd9Sstevel@tonic-gate 				}
41047c478bd9Sstevel@tonic-gate 
41057c478bd9Sstevel@tonic-gate 				/* Validate the peer addresses. */
41067c478bd9Sstevel@tonic-gate 				sctp->sctp_active = now;
41077c478bd9Sstevel@tonic-gate 				sctp_validate_peer(sctp);
41087c478bd9Sstevel@tonic-gate 
41097c478bd9Sstevel@tonic-gate 				sctp_assoc_event(sctp, SCTP_COMM_UP, 0, NULL);
4110558fbd03Skcpoon 				if (recv_adaptation) {
4111558fbd03Skcpoon 					sctp->sctp_recv_adaptation = 1;
4112558fbd03Skcpoon 					sctp_adaptation_event(sctp);
41137c478bd9Sstevel@tonic-gate 				}
41147c478bd9Sstevel@tonic-gate 				/* Try sending queued data, or ASCONFs */
41157c478bd9Sstevel@tonic-gate 				trysend = 1;
41167c478bd9Sstevel@tonic-gate 				break;
41177c478bd9Sstevel@tonic-gate 			default:
41187c478bd9Sstevel@tonic-gate 				if (sctp_strange_chunk(sctp, ch, fp) == 0) {
41197c478bd9Sstevel@tonic-gate 					goto nomorechunks;
41207c478bd9Sstevel@tonic-gate 				} /* else skip and continue processing */
41217c478bd9Sstevel@tonic-gate 				break;
41227c478bd9Sstevel@tonic-gate 			}
41237c478bd9Sstevel@tonic-gate 			break;
41247c478bd9Sstevel@tonic-gate 
41257c478bd9Sstevel@tonic-gate 		case SCTPS_COOKIE_ECHOED:
41267c478bd9Sstevel@tonic-gate 			switch (ch->sch_id) {
41277c478bd9Sstevel@tonic-gate 			case CHUNK_COOKIE_ACK:
4128bd670b35SErik Nordmark 				cr = ira->ira_cred;
4129bd670b35SErik Nordmark 				cpid = ira->ira_cpid;
4130de8c4a14SErik Nordmark 
41317c478bd9Sstevel@tonic-gate 				if (!SCTP_IS_DETACHED(sctp)) {
41327f093707Skcpoon 					sctp->sctp_ulp_connected(
4133de8c4a14SErik Nordmark 					    sctp->sctp_ulpd, 0, cr, cpid);
41347c478bd9Sstevel@tonic-gate 					sctp_set_ulp_prop(sctp);
41357c478bd9Sstevel@tonic-gate 				}
41367c478bd9Sstevel@tonic-gate 				if (sctp->sctp_unacked == 0)
41377c478bd9Sstevel@tonic-gate 					sctp_stop_faddr_timers(sctp);
41385dd46ab5SKacheong Poon 				SCTP_ASSOC_EST(sctps, sctp);
41395dd46ab5SKacheong Poon 				SCTPS_BUMP_MIB(sctps, sctpActiveEstab);
41407c478bd9Sstevel@tonic-gate 				BUMP_LOCAL(sctp->sctp_ibchunks);
41417c478bd9Sstevel@tonic-gate 				if (sctp->sctp_cookie_mp) {
41427c478bd9Sstevel@tonic-gate 					freemsg(sctp->sctp_cookie_mp);
41437c478bd9Sstevel@tonic-gate 					sctp->sctp_cookie_mp = NULL;
41447c478bd9Sstevel@tonic-gate 				}
41457c478bd9Sstevel@tonic-gate 				sctp_faddr_alive(sctp, fp);
41467c478bd9Sstevel@tonic-gate 				/* Validate the peer addresses. */
41477c478bd9Sstevel@tonic-gate 				sctp->sctp_active = now;
41487c478bd9Sstevel@tonic-gate 				sctp_validate_peer(sctp);
41497c478bd9Sstevel@tonic-gate 
41507c478bd9Sstevel@tonic-gate 				/* Try sending queued data, or ASCONFs */
41517c478bd9Sstevel@tonic-gate 				trysend = 1;
41527c478bd9Sstevel@tonic-gate 				sctp_assoc_event(sctp, SCTP_COMM_UP, 0, NULL);
4153558fbd03Skcpoon 				sctp_adaptation_event(sctp);
41547c478bd9Sstevel@tonic-gate 				break;
41557c478bd9Sstevel@tonic-gate 			case CHUNK_ABORT:
41567c478bd9Sstevel@tonic-gate 				sctp_process_abort(sctp, ch, ECONNREFUSED);
41577c478bd9Sstevel@tonic-gate 				goto done;
41587c478bd9Sstevel@tonic-gate 			case CHUNK_COOKIE:
4159bd670b35SErik Nordmark 				cr = ira->ira_cred;
4160bd670b35SErik Nordmark 				cpid = ira->ira_cpid;
4161de8c4a14SErik Nordmark 
41627c478bd9Sstevel@tonic-gate 				if (sctp_process_cookie(sctp, ch, mp, &iack,
4163bd670b35SErik Nordmark 				    sctph, &recv_adaptation, NULL, ira) == -1) {
41645dd46ab5SKacheong Poon 					SCTPS_BUMP_MIB(sctps,
41657c478bd9Sstevel@tonic-gate 					    sctpInInvalidCookie);
41667c478bd9Sstevel@tonic-gate 					break;
41677c478bd9Sstevel@tonic-gate 				}
41687c478bd9Sstevel@tonic-gate 				sctp_send_cookie_ack(sctp);
41697c478bd9Sstevel@tonic-gate 
41707c478bd9Sstevel@tonic-gate 				if (!SCTP_IS_DETACHED(sctp)) {
41717f093707Skcpoon 					sctp->sctp_ulp_connected(
4172de8c4a14SErik Nordmark 					    sctp->sctp_ulpd, 0, cr, cpid);
41737c478bd9Sstevel@tonic-gate 					sctp_set_ulp_prop(sctp);
4174de8c4a14SErik Nordmark 
41757c478bd9Sstevel@tonic-gate 				}
41767c478bd9Sstevel@tonic-gate 				if (sctp->sctp_unacked == 0)
41777c478bd9Sstevel@tonic-gate 					sctp_stop_faddr_timers(sctp);
41785dd46ab5SKacheong Poon 				SCTP_ASSOC_EST(sctps, sctp);
41795dd46ab5SKacheong Poon 				SCTPS_BUMP_MIB(sctps, sctpActiveEstab);
41807c478bd9Sstevel@tonic-gate 				if (sctp->sctp_cookie_mp) {
41817c478bd9Sstevel@tonic-gate 					freemsg(sctp->sctp_cookie_mp);
41827c478bd9Sstevel@tonic-gate 					sctp->sctp_cookie_mp = NULL;
41837c478bd9Sstevel@tonic-gate 				}
41847c478bd9Sstevel@tonic-gate 				/* Validate the peer addresses. */
41857c478bd9Sstevel@tonic-gate 				sctp->sctp_active = now;
41867c478bd9Sstevel@tonic-gate 				sctp_validate_peer(sctp);
41877c478bd9Sstevel@tonic-gate 
41887c478bd9Sstevel@tonic-gate 				sctp_assoc_event(sctp, SCTP_COMM_UP, 0, NULL);
4189558fbd03Skcpoon 				if (recv_adaptation) {
4190558fbd03Skcpoon 					sctp->sctp_recv_adaptation = 1;
4191558fbd03Skcpoon 					sctp_adaptation_event(sctp);
41927c478bd9Sstevel@tonic-gate 				}
41937c478bd9Sstevel@tonic-gate 				/* Try sending queued data, or ASCONFs */
41947c478bd9Sstevel@tonic-gate 				trysend = 1;
41957c478bd9Sstevel@tonic-gate 				break;
41967c478bd9Sstevel@tonic-gate 			case CHUNK_INIT:
4197bd670b35SErik Nordmark 				sctp_send_initack(sctp, sctph, ch, mp, ira);
41987c478bd9Sstevel@tonic-gate 				break;
41997c478bd9Sstevel@tonic-gate 			case CHUNK_ERROR: {
42007c478bd9Sstevel@tonic-gate 				sctp_parm_hdr_t *p;
42017c478bd9Sstevel@tonic-gate 
42027c478bd9Sstevel@tonic-gate 				BUMP_LOCAL(sctp->sctp_ibchunks);
42037c478bd9Sstevel@tonic-gate 				/* check for a stale cookie */
42047c478bd9Sstevel@tonic-gate 				if (ntohs(ch->sch_len) >=
42057c478bd9Sstevel@tonic-gate 				    (sizeof (*p) + sizeof (*ch)) +
42067c478bd9Sstevel@tonic-gate 				    sizeof (uint32_t)) {
42077c478bd9Sstevel@tonic-gate 
42087c478bd9Sstevel@tonic-gate 					p = (sctp_parm_hdr_t *)(ch + 1);
42097c478bd9Sstevel@tonic-gate 					if (p->sph_type ==
42107c478bd9Sstevel@tonic-gate 					    htons(SCTP_ERR_STALE_COOKIE)) {
42115dd46ab5SKacheong Poon 						SCTPS_BUMP_MIB(sctps,
42127c478bd9Sstevel@tonic-gate 						    sctpAborted);
42138dfac042SAnil udupa 						sctp_error_event(sctp,
42148dfac042SAnil udupa 						    ch, B_FALSE);
4215c9da23f8Skcpoon 						sctp_assoc_event(sctp,
4216c9da23f8Skcpoon 						    SCTP_COMM_LOST, 0, NULL);
42177c478bd9Sstevel@tonic-gate 						sctp_clean_death(sctp,
42187c478bd9Sstevel@tonic-gate 						    ECONNREFUSED);
42197c478bd9Sstevel@tonic-gate 						goto done;
42207c478bd9Sstevel@tonic-gate 					}
42217c478bd9Sstevel@tonic-gate 				}
42227c478bd9Sstevel@tonic-gate 				break;
42237c478bd9Sstevel@tonic-gate 			}
42247c478bd9Sstevel@tonic-gate 			case CHUNK_HEARTBEAT:
42257f093707Skcpoon 				if (!hb_already) {
42267c478bd9Sstevel@tonic-gate 					sctp_return_heartbeat(sctp, ch, mp);
42277f093707Skcpoon 					hb_already = B_TRUE;
42287f093707Skcpoon 				}
42297c478bd9Sstevel@tonic-gate 				break;
42307c478bd9Sstevel@tonic-gate 			default:
42317c478bd9Sstevel@tonic-gate 				if (sctp_strange_chunk(sctp, ch, fp) == 0) {
42327c478bd9Sstevel@tonic-gate 					goto nomorechunks;
42337c478bd9Sstevel@tonic-gate 				} /* else skip and continue processing */
42347c478bd9Sstevel@tonic-gate 			} /* switch (ch->sch_id) */
42357c478bd9Sstevel@tonic-gate 			break;
42367c478bd9Sstevel@tonic-gate 
42377c478bd9Sstevel@tonic-gate 		case SCTPS_SHUTDOWN_ACK_SENT:
42387c478bd9Sstevel@tonic-gate 			switch (ch->sch_id) {
42397c478bd9Sstevel@tonic-gate 			case CHUNK_ABORT:
42407c478bd9Sstevel@tonic-gate 				/* Pass gathered wisdom to IP for keeping */
4241bd670b35SErik Nordmark 				sctp_update_dce(sctp);
42427c478bd9Sstevel@tonic-gate 				sctp_process_abort(sctp, ch, 0);
42437c478bd9Sstevel@tonic-gate 				goto done;
42447c478bd9Sstevel@tonic-gate 			case CHUNK_SHUTDOWN_COMPLETE:
42457c478bd9Sstevel@tonic-gate 				BUMP_LOCAL(sctp->sctp_ibchunks);
42465dd46ab5SKacheong Poon 				SCTPS_BUMP_MIB(sctps, sctpShutdowns);
42477c478bd9Sstevel@tonic-gate 				sctp_assoc_event(sctp, SCTP_SHUTDOWN_COMP, 0,
42487c478bd9Sstevel@tonic-gate 				    NULL);
42497c478bd9Sstevel@tonic-gate 
42507c478bd9Sstevel@tonic-gate 				/* Pass gathered wisdom to IP for keeping */
4251bd670b35SErik Nordmark 				sctp_update_dce(sctp);
42527c478bd9Sstevel@tonic-gate 				sctp_clean_death(sctp, 0);
42537c478bd9Sstevel@tonic-gate 				goto done;
42547c478bd9Sstevel@tonic-gate 			case CHUNK_SHUTDOWN_ACK:
42557c478bd9Sstevel@tonic-gate 				sctp_shutdown_complete(sctp);
42567c478bd9Sstevel@tonic-gate 				BUMP_LOCAL(sctp->sctp_ibchunks);
42575dd46ab5SKacheong Poon 				SCTPS_BUMP_MIB(sctps, sctpShutdowns);
42587c478bd9Sstevel@tonic-gate 				sctp_assoc_event(sctp, SCTP_SHUTDOWN_COMP, 0,
42597c478bd9Sstevel@tonic-gate 				    NULL);
42607c478bd9Sstevel@tonic-gate 				sctp_clean_death(sctp, 0);
42617c478bd9Sstevel@tonic-gate 				goto done;
42627c478bd9Sstevel@tonic-gate 			case CHUNK_COOKIE:
42637c478bd9Sstevel@tonic-gate 				(void) sctp_shutdown_received(sctp, NULL,
426477c67f2fSkcpoon 				    B_TRUE, B_FALSE, fp);
42657c478bd9Sstevel@tonic-gate 				BUMP_LOCAL(sctp->sctp_ibchunks);
42667c478bd9Sstevel@tonic-gate 				break;
42677c478bd9Sstevel@tonic-gate 			case CHUNK_HEARTBEAT:
42687f093707Skcpoon 				if (!hb_already) {
42697c478bd9Sstevel@tonic-gate 					sctp_return_heartbeat(sctp, ch, mp);
42707f093707Skcpoon 					hb_already = B_TRUE;
42717f093707Skcpoon 				}
42727c478bd9Sstevel@tonic-gate 				break;
42737c478bd9Sstevel@tonic-gate 			default:
42747c478bd9Sstevel@tonic-gate 				if (sctp_strange_chunk(sctp, ch, fp) == 0) {
42757c478bd9Sstevel@tonic-gate 					goto nomorechunks;
42767c478bd9Sstevel@tonic-gate 				} /* else skip and continue processing */
42777c478bd9Sstevel@tonic-gate 				break;
42787c478bd9Sstevel@tonic-gate 			}
42797c478bd9Sstevel@tonic-gate 			break;
42807c478bd9Sstevel@tonic-gate 
42817c478bd9Sstevel@tonic-gate 		case SCTPS_SHUTDOWN_RECEIVED:
42827c478bd9Sstevel@tonic-gate 			switch (ch->sch_id) {
42837c478bd9Sstevel@tonic-gate 			case CHUNK_SHUTDOWN:
42847c478bd9Sstevel@tonic-gate 				trysend = sctp_shutdown_received(sctp, ch,
428577c67f2fSkcpoon 				    B_FALSE, B_FALSE, fp);
42863e1dae9fSAnil udupa 				/*
42873e1dae9fSAnil udupa 				 * shutdown_ack_needed may have been set as
42883e1dae9fSAnil udupa 				 * mentioned in the case CHUNK_SACK below.
42893e1dae9fSAnil udupa 				 * If sctp_shutdown_received() above found
42903e1dae9fSAnil udupa 				 * the xmit queue empty the SHUTDOWN ACK chunk
42913e1dae9fSAnil udupa 				 * has already been sent (or scheduled to be
42923e1dae9fSAnil udupa 				 * sent on the timer) and the SCTP state
42933e1dae9fSAnil udupa 				 * changed, so reset shutdown_ack_needed.
42943e1dae9fSAnil udupa 				 */
42953e1dae9fSAnil udupa 				if (shutdown_ack_needed && (sctp->sctp_state ==
42963e1dae9fSAnil udupa 				    SCTPS_SHUTDOWN_ACK_SENT))
42973e1dae9fSAnil udupa 					shutdown_ack_needed = B_FALSE;
42987c478bd9Sstevel@tonic-gate 				break;
42997c478bd9Sstevel@tonic-gate 			case CHUNK_SACK:
43007c478bd9Sstevel@tonic-gate 				trysend = sctp_got_sack(sctp, ch);
43011d8c4025Svi117747 				if (trysend < 0) {
43021d8c4025Svi117747 					sctp_send_abort(sctp, sctph->sh_verf,
4303bd670b35SErik Nordmark 					    0, NULL, 0, mp, 0, B_FALSE, ira);
43041d8c4025Svi117747 					sctp_assoc_event(sctp,
43051d8c4025Svi117747 					    SCTP_COMM_LOST, 0, NULL);
43061d8c4025Svi117747 					sctp_clean_death(sctp,
43071d8c4025Svi117747 					    ECONNABORTED);
43081d8c4025Svi117747 					goto done;
43091d8c4025Svi117747 				}
43103e1dae9fSAnil udupa 
43113e1dae9fSAnil udupa 				/*
43123e1dae9fSAnil udupa 				 * All data acknowledgement after a shutdown
43133e1dae9fSAnil udupa 				 * should be done with SHUTDOWN chunk.
43143e1dae9fSAnil udupa 				 * However some peer SCTP do not conform with
43153e1dae9fSAnil udupa 				 * this and can unexpectedly send a SACK chunk.
43163e1dae9fSAnil udupa 				 * If all data are acknowledged, set
43173e1dae9fSAnil udupa 				 * shutdown_ack_needed here indicating that
43183e1dae9fSAnil udupa 				 * SHUTDOWN ACK needs to be sent later by
43193e1dae9fSAnil udupa 				 * sctp_send_shutdown_ack().
43203e1dae9fSAnil udupa 				 */
43213e1dae9fSAnil udupa 				if ((sctp->sctp_xmit_head == NULL) &&
43223e1dae9fSAnil udupa 				    (sctp->sctp_xmit_unsent == NULL))
43233e1dae9fSAnil udupa 					shutdown_ack_needed = B_TRUE;
43247c478bd9Sstevel@tonic-gate 				break;
43257c478bd9Sstevel@tonic-gate 			case CHUNK_ABORT:
43267c478bd9Sstevel@tonic-gate 				sctp_process_abort(sctp, ch, ECONNRESET);
43277c478bd9Sstevel@tonic-gate 				goto done;
43287c478bd9Sstevel@tonic-gate 			case CHUNK_HEARTBEAT:
43297f093707Skcpoon 				if (!hb_already) {
43307c478bd9Sstevel@tonic-gate 					sctp_return_heartbeat(sctp, ch, mp);
43317f093707Skcpoon 					hb_already = B_TRUE;
43327f093707Skcpoon 				}
43337c478bd9Sstevel@tonic-gate 				break;
43347c478bd9Sstevel@tonic-gate 			default:
43357c478bd9Sstevel@tonic-gate 				if (sctp_strange_chunk(sctp, ch, fp) == 0) {
43367c478bd9Sstevel@tonic-gate 					goto nomorechunks;
43377c478bd9Sstevel@tonic-gate 				} /* else skip and continue processing */
43387c478bd9Sstevel@tonic-gate 				break;
43397c478bd9Sstevel@tonic-gate 			}
43407c478bd9Sstevel@tonic-gate 			break;
43417c478bd9Sstevel@tonic-gate 
43427c478bd9Sstevel@tonic-gate 		default:
4343769b977dSvi117747 			/*
4344769b977dSvi117747 			 * The only remaining states are SCTPS_IDLE and
4345769b977dSvi117747 			 * SCTPS_BOUND, and we should not be getting here
4346769b977dSvi117747 			 * for these.
4347769b977dSvi117747 			 */
4348769b977dSvi117747 			ASSERT(0);
43497c478bd9Sstevel@tonic-gate 		} /* switch (sctp->sctp_state) */
43507c478bd9Sstevel@tonic-gate 
43517c478bd9Sstevel@tonic-gate 		ch = sctp_next_chunk(ch, &mlen);
43527c478bd9Sstevel@tonic-gate 		if (ch != NULL && !sctp_check_input(sctp, ch, mlen, 0))
43537c478bd9Sstevel@tonic-gate 			goto done;
43547c478bd9Sstevel@tonic-gate 	} while (ch != NULL);
43557c478bd9Sstevel@tonic-gate 
43567c478bd9Sstevel@tonic-gate 	/* Finished processing all chunks in packet */
43577c478bd9Sstevel@tonic-gate 
43587c478bd9Sstevel@tonic-gate nomorechunks:
43593e1dae9fSAnil udupa 
43603e1dae9fSAnil udupa 	if (shutdown_ack_needed)
43613e1dae9fSAnil udupa 		sctp_send_shutdown_ack(sctp, fp, B_FALSE);
43623e1dae9fSAnil udupa 
43637c478bd9Sstevel@tonic-gate 	/* SACK if necessary */
43647c478bd9Sstevel@tonic-gate 	if (gotdata) {
43657f093707Skcpoon 		boolean_t sack_sent;
43667f093707Skcpoon 
43677c478bd9Sstevel@tonic-gate 		(sctp->sctp_sack_toggle)++;
43687f093707Skcpoon 		sack_sent = sctp_sack(sctp, dups);
43697c478bd9Sstevel@tonic-gate 		dups = NULL;
43707c478bd9Sstevel@tonic-gate 
43717f093707Skcpoon 		/* If a SACK is sent, no need to restart the timer. */
43727f093707Skcpoon 		if (!sack_sent && !sctp->sctp_ack_timer_running) {
43737c478bd9Sstevel@tonic-gate 			sctp->sctp_ack_timer_running = B_TRUE;
43747c478bd9Sstevel@tonic-gate 			sctp_timer(sctp, sctp->sctp_ack_mp,
4375f4b3ec61Sdh155122 			    MSEC_TO_TICK(sctps->sctps_deferred_ack_interval));
43767c478bd9Sstevel@tonic-gate 		}
43777c478bd9Sstevel@tonic-gate 	}
43787c478bd9Sstevel@tonic-gate 
43797c478bd9Sstevel@tonic-gate 	if (trysend) {
438012f47623Skcpoon 		sctp_output(sctp, UINT_MAX);
43817c478bd9Sstevel@tonic-gate 		if (sctp->sctp_cxmit_list != NULL)
43827c478bd9Sstevel@tonic-gate 			sctp_wput_asconf(sctp, NULL);
43837c478bd9Sstevel@tonic-gate 	}
438492baa190SGeorge Shepherd 	/*
438592baa190SGeorge Shepherd 	 * If there is unsent data, make sure a timer is running, check
438692baa190SGeorge Shepherd 	 * timer_mp, if sctp_closei_local() ran the timers may be free.
438792baa190SGeorge Shepherd 	 */
43886be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 	if (sctp->sctp_unsent > 0 && !sctp->sctp_current->sf_timer_running &&
43896be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 	    sctp->sctp_current->sf_timer_mp != NULL) {
43907c478bd9Sstevel@tonic-gate 		SCTP_FADDR_TIMER_RESTART(sctp, sctp->sctp_current,
43916be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India 		    sctp->sctp_current->sf_rto);
43927c478bd9Sstevel@tonic-gate 	}
43937c478bd9Sstevel@tonic-gate 
43947c478bd9Sstevel@tonic-gate done:
43957c478bd9Sstevel@tonic-gate 	if (dups != NULL)
43967c478bd9Sstevel@tonic-gate 		freeb(dups);
43977c478bd9Sstevel@tonic-gate 	freemsg(mp);
43987c478bd9Sstevel@tonic-gate 
43997f093707Skcpoon 	if (sctp->sctp_err_chunks != NULL)
44007f093707Skcpoon 		sctp_process_err(sctp);
44017f093707Skcpoon 
44027c478bd9Sstevel@tonic-gate 	if (wake_eager) {
44037c478bd9Sstevel@tonic-gate 		/*
44047c478bd9Sstevel@tonic-gate 		 * sctp points to newly created control block, need to
4405bd670b35SErik Nordmark 		 * release it before exiting.
44067c478bd9Sstevel@tonic-gate 		 */
44077c478bd9Sstevel@tonic-gate 		WAKE_SCTP(sctp);
44087c478bd9Sstevel@tonic-gate 	}
44097c478bd9Sstevel@tonic-gate }
44107c478bd9Sstevel@tonic-gate 
44117c478bd9Sstevel@tonic-gate /*
4412*a215d4ebSKacheong Poon  * Some amount of data got removed from ULP's receive queue and we can
4413*a215d4ebSKacheong Poon  * push messages up if we are flow controlled before.  Reset the receive
4414*a215d4ebSKacheong Poon  * window to full capacity (conn_rcvbuf) and check if we should send a
4415*a215d4ebSKacheong Poon  * window update.
44167c478bd9Sstevel@tonic-gate  */
44177c478bd9Sstevel@tonic-gate void
44187c478bd9Sstevel@tonic-gate sctp_recvd(sctp_t *sctp, int len)
44197c478bd9Sstevel@tonic-gate {
4420f4b3ec61Sdh155122 	sctp_stack_t	*sctps = sctp->sctp_sctps;
4421*a215d4ebSKacheong Poon 	conn_t		*connp = sctp->sctp_connp;
4422*a215d4ebSKacheong Poon 	boolean_t	send_sack = B_FALSE;
44237c478bd9Sstevel@tonic-gate 
44247c478bd9Sstevel@tonic-gate 	ASSERT(sctp != NULL);
44257c478bd9Sstevel@tonic-gate 	RUN_SCTP(sctp);
44267c478bd9Sstevel@tonic-gate 
4427*a215d4ebSKacheong Poon 	sctp->sctp_flowctrld = B_FALSE;
4428*a215d4ebSKacheong Poon 	/* This is the amount of data queued in ULP. */
4429*a215d4ebSKacheong Poon 	sctp->sctp_ulp_rxqueued = connp->conn_rcvbuf - len;
4430c3c17166SGeorge Shepherd 
4431*a215d4ebSKacheong Poon 	if (connp->conn_rcvbuf - sctp->sctp_arwnd >= sctp->sctp_mss)
4432*a215d4ebSKacheong Poon 		send_sack = B_TRUE;
4433*a215d4ebSKacheong Poon 	sctp->sctp_rwnd = connp->conn_rcvbuf;
44347c478bd9Sstevel@tonic-gate 
4435*a215d4ebSKacheong Poon 	if (sctp->sctp_state >= SCTPS_ESTABLISHED && send_sack) {
44367c478bd9Sstevel@tonic-gate 		sctp->sctp_force_sack = 1;
44375dd46ab5SKacheong Poon 		SCTPS_BUMP_MIB(sctps, sctpOutWinUpdate);
44387f093707Skcpoon 		(void) sctp_sack(sctp, NULL);
44397c478bd9Sstevel@tonic-gate 	}
44407c478bd9Sstevel@tonic-gate 	WAKE_SCTP(sctp);
44417c478bd9Sstevel@tonic-gate }
4442